Rope Swing

Overview This page provides a way to implement a simple rope swing Tutorials Simple demo with my tutorial: https://youtu.be/AIQX3CvOXo8 In your character code, you only need to add a variable ifOnR...

Updated over 4 years ago Edit Page Revisions

Overview

This page provides a way to implement a simple rope swing

Tutorials

Simple demo with my tutorial:

In your character code, you only need to add a variable ifOnRope to see if the character is attached to rope; also, you need to modify your jump functin so that if you are on the rope, you jump and will set ifOnRope to false and leave the Rope:

MyCharacter.cpp

    
void AMyCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    Super::SetupPlayerInputComponent(InputComponent);
    InputComponent->BindAxis("Forward", this,
        &AMyCharacter::MoveForward);
    InputComponent->BindAxis("Strafe", this, &AMyCharacter::MoveRight);
    InputComponent->BindAction("Jump", IE_Pressed, this, &AMyCharacter::OnStartJump);
    InputComponent->BindAction("Jump", IE_Released, this, &AMyCharacter::OnStopJump);
    ifOnRope = false;
}
void AMyCharacter::OnStartJump()
{
    
    bPressedJump = true;
    if (ifOnRope)
        ifOnRope = false;
}
void AMyCharacter::OnStopJump()
{
    bPressedJump = false;
}

Then we need to add some variable to the rope actor: float rotateDegree; // The "Max" swing degree float TotalTime; //Time span since character attach to rope, this may be improved by using TimeSpan structure FVector actorFirstLocation; // The location when character proxs rope FVector Pivot; //rope's pivot We use the following pendulum equation to calculate swing:

File:Swing_1.gif

For simplicity, when character touches the rope, we set the maximum theta from 0 to 40 degree and let "G/L" be a value making your pendulum's speed appropriate. In my computer, I make it to be 1/30 and I get a pendulum with a moderate speed. When character is attached to rope, we set the totalTime to zero, in every tick, we add deltaTime to TotalTime, more efficient way may involve the usage of TimeSpan structure. Now after adding this function, your pendulum can swing after your character touches the rope, full code will be introduced later, so don't be intimidated if you are kind of confused now. SetActorRotation(FRotator(rotateDegree*sin(rotateDegree*sin(TotalTime)/30), 0.0f, 0.0f)); Now, your pendulum swings. What we need to do is to let the character swing at the same tempo, too; What we know is the rotation degree of the rope at each frame:FRotator(rotateDegree*sin(rotateDegree*sin(TotalTime)/30) We also know the pivot location(a,b,c), as illustrated by the following picture:

File:Swing_3.png

In two dimensional, if pivot is (a,b). Then character's position on rope is: (a+x*sin(╬©), b-x*cos(╬©)), where x is the distance from character to pivot In my application, only z-axis, the height, and x-axis, the range of the swing change. So it is Pivot + (x*sin(╬©),0,-x*cos(╬©)) Now we can see the whole picture:

Rope.h

    
UCLASS()
class MYPROJECT5_API AMyActor : public AActor
{
    GENERATED_BODY()
    
public: 
    // Sets default values for this actor's properties
    AMyActor(const FObjectInitializer& ObjectInitializer);
    float rotateDegree; // The Max swing degree
    float TotalTime;    //Time span since character attach to rope, this may be improved by using TimeSpan structure
    FVector actorFirstLocation; // The location when character proxs rope
    FVector Pivot;              //rope's pivot
    AMyCharacter* actorAttached;//Our character
    // Called when the game starts or when spawned

    virtual void BeginPlay() override;
    UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, Category = MeleeWeapon)
        UBoxComponent* ProxBox;
    UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, Category = MeleeWeapon)
        UStaticMeshComponent* Mesh;
    UFUNCTION(BlueprintNativeEvent, Category = Collision)
        void Prox(AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult);
    void Prox_Implementation(AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep,
        const FHitResult & SweepResult);
    // Called every frame
    virtual void Tick(float DeltaSeconds) override;
};

Prox function detect when it overlaps with other actors.

Rope.cpp

   
AMyActor::AMyActor(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    rotateDegree = 0.0f;
    TotalTime = 0.0f;
    Mesh = ObjectInitializer.CreateDefaultSubobject(this,
        TEXT("Mesh"));
    RootComponent = Mesh;
    ProxBox = ObjectInitializer.CreateDefaultSubobject(this,
        TEXT("ProxBox"));
    ProxBox->OnComponentBeginOverlap.AddDynamic(this,
        &AMyActor::Prox);
    ProxBox->AttachTo(RootComponent);
    actorAttached = NULL;
    actorFirstLocation = FVector(0.0f, 0.0f, 0.0f);
    Pivot = FVector(-110.0f, 0.0f, 260.0f); // my pivot, I know the location, yours should be different

} 
void AMyActor::Prox_Implementation(AActor* OtherActor,
    UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool
    bFromSweep, const FHitResult & SweepResult)
{

    actorAttached = Cast(OtherActor); // set actorAttached to our character if it detects rope overlaps with our character
    if (actorAttached != NULL && (actorAttached->ifOnRope == false))
    {
        TotalTime = 0.0f;         //initilize total time
        actorAttached->ifOnRope = true; 
        rotateDegree = 40;         //set the max swing degree to 40
        actorFirstLocation = actorAttached->GetActorLocation(); //record the location of overlap
    }
    
}

void AMyActor::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );
    TotalTime += DeltaTime;
    float DistanceToPivot = (actorFirstLocation - Pivot).Size(); // Calculate the distance from character to pivot, it is a constant when character swings the rope
    if (actorAttached != NULL)
    {

        if (actorAttached->ifOnRope){
                //let the rope swing
            SetActorRotation(FRotator(rotateDegree*sin(rotateDegree*sin(TotalTime)/30), 0.0f, 0.0f));

                        //character swing
            actorAttached->SetActorLocation(Pivot + FVector(DistanceToPivot*sin(rotateDegree*sin(TotalTime)/30), 0.0f, -cos(rotateDegree*sin(TotalTime)/30)*DistanceToPivot)
                );

        }
        else
        {
                         // If character leaves the rope, we must let it stop
            if (rotateDegree>=0)
            rotateDegree -= 2;
            SetActorRotation(FRotator(rotateDegree*sin(rotateDegree*sin(TotalTime) / 30), 0.0f, 0.0f));
        }
    }

}
== Conclusion ==
This is a simple implement of rope swing. Hope it is helpful.