Ue4

UE4 C ++を使用して、パルクールゲームのキャラクターの動きを記述します



Use Ue4 C Write Parkour Game Character Movement



UE4 C ++でパルクールゲームを書く

キャラクターの動きには以下が含まれます:



  1. フォワード
  2. ジャンプ、秋
  3. 順番
    これらは対応する文字で書かれています

キーバインディング

プレイヤーはキーボードを介してキャラクターを制御します
キーバインディングは、UE4とC ++で同時にバインドする必要があります
UE4
Character.cpp

void ARunGame3Character::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) { check(PlayerInputComponent) PlayerInputComponent->BindAction('Jump', IE_Pressed, this, &ACharacter::Jump) PlayerInputComponent->BindAction('Jump', IE_Released, this, &ACharacter::StopJumping) PlayerInputComponent->BindAction('Left', IE_Pressed, this, &ARunGame3Character::Left) PlayerInputComponent->BindAction('Right', IE_Pressed, this, &ARunGame3Character::Right) PlayerInputComponent->BindAction('Down', IE_Pressed, this, &ARunGame3Character::Down) }

ジャンプ、左(右)、およびドロップはこの関数にバインドされています
Jump:押すとJump()、離すとStopJumping()、これら2つはUE4でオリジナルです
そして、左(右)と下には、自分で書いたLeft()、Right()、Down()の3つの関数があります。



フォワード

ゲームでは、キャラクターが継続的に前進する必要があります
Character.cpp

void ARunGame3Character::MoveForward() { AddMovementInput(UKismetMathLibrary::GetForwardVector( FRotator(0, GetControlRotation().Yaw, 0)) , 1.0f, false) }

この関数は、前進するキャラクターを実装します。 AddMovementInput()の3つのパラメーターは、移動する方向、移動速度、およびコントローラーのIgnoreMoveInputプロパティ値の強制移動を無視するかどうかです(最後のパラメーターはあまり理解されていません)。
UKismetMathLibrary :: GetForwardVector(FRotator(0、GetControlRotation()。Yaw、0))は、キャラクターが向いている方向を取得するためのものです。
次に、Tick()でMoveForward()を呼び出して、キャラクターが前進し続けることを実現します

ジャンプして落ちる

障害物を避けるためだけに、ジャンプと落下の機能はよく理解されています
Character.cpp



void ARunGame3Character::Down() { GetCharacterMovement()->UCharacterMovementComponent::AddImpulse( FVector(0.f,0.f,-3000.f),true) }

前述のように、元のジャンプを使用でき(ジャンプはキャラクターのZ軸データの変更です)、これは降下の関数であり、キャラクターの落下は力FVector(0.f、0.f)を適用することによって実現されます。 、-3000.f)この力の大きさと方向は

ジャンプと比較して、左右に移動することは、キャラクターのX(Y)軸の値です。なぜXまたはYなのですか?この問題は、左右に回転させるとよりよく説明されます
ここでは、コンストラクターで現在の道路位置、新しい道路位置、3つの道路のX軸値、および3つの道路のY軸値を格納するために、2つの整数と2つの配列を定義する必要があります。 1と0。キャラクターの位置が中道であるため、Aaaは1です。
同時に、キャラクターが左右に動くプロセスがあり、時間軸を使用してテレポートすることはできません。
Character.h

int Aaa//Current road position int Bbb//New road location int Yy[3] = { 0,0,0 }//Array used for left and right int Xx[3] = { 0,0,0 } UPROPERTY(EditAnywhere,BlueprintReadWrite) class UCurveFloat * FloatCurve//curve FTimeline ZoomTimeline//Timeline UFUNCTION() void DoZoom(float Rood)//The specific execution function of the timeline UFUNCTION() void Emmm()//The function executed after the timeline is completed

.hファイルで、曲線と時間軸を作成します。この曲線はUE4で指定されており、DoZoom関数とEmmm関数もここで宣言されています

Character.cpp

void ARunGame3Character::BeginPlay()//Create a timeline in BeginPlay { Super::BeginPlay() FOnTimelineFloatStatic TimelineCallBack TimelineCallBack.BindUFunction(this, TEXT('DoZoom'))//Delegated function of timeline execution FOnTimelineEventStatic onTimelineFinishedCallback onTimelineFinishedCallback.BindUFunction(this, FName{ TEXT('Emmm') })//The function executed after the commissioning timeline is completed ZoomTimeline.AddInterpFloat(FloatCurve, TimelineCallBack) ZoomTimeline.SetTimelineLength(0.1f) ZoomTimeline.SetTimelineFinishedFunc(onTimelineFinishedCallback) } void ARunGame3Character::DoZoom(float Rood)//Functions executed by the timeline { SetActorLocation(FVector( (UKismetMathLibrary::Lerp(Xx[Aaa], Xx[Bbb], Rood)), GetCapsuleComponent()->USceneComponent::GetComponentLocation().Y, GetCapsuleComponent()->USceneComponent::GetComponentLocation().Z)) //near } void ARunGame3Character::Emmm()//The function executed at the end of the timeline { Aaa = Bbb } void ARunGame3Character::Left() { Bbb = UKismetMathLibrary::Clamp(Aaa - 1, 0, 2)//Limit range ZoomTimeline.PlayFromStart()//Play timeline } void ARunGame3Character::Tick(float DeltaSeconds)//Tick { Super::Tick(DeltaSeconds) ZoomTimeline.TickTimeline(DeltaSeconds)//Pass the time into the timeline }
  • BeginPlay()でタイムラインをバインドし、
  • FOnTimelineFloatStatic TimelineCallBack
  • FOnTimelineEventStatic onTimelineFinishedCallback
  • 2つのコールバックを作成し(ここではあまり理解していません)、BindUFunctionを介して対応する関数をバインドしますTimelineCallBackはDoZoomをバインドします。タイムラインで実行される関数onTimelineFinishedCallbackはEmmmをバインドします。

以下は移動のプロセスです

Aaa Bbb 移動方向
1 0
1 1

'→'キーを押すたびに、文字は右に1回移動します。つまり、Bbb = Aaa-1 Aaa = Bbbです。逆に、 '←'キーを押すと、文字は左に1回移動します。 Bbb = Aaa + 1もちろんこれは、指定された範囲内でのみ実行できます。道路が3つあるため、範囲は[0、2]です。

順番

nブロックの道路に入った後、曲がる特別な道路があります。通常の道路と比較して、キャラクターのブール変数を変更するための衝突ボックスがあります。変更後、←→キーの機能は左から右に移動するのではなく、キャラクターのヨーの角度を回転させます。回転後、ブール変数を再度変更して←→キーの機能を復元すると同時に、文字が正常に回転した後、文字の左右の動きによる値の変化と障害物の発生を考慮する必要があります。方向
画像これは左を向いた写真です。画像を分析すると、キャラクターは元々X軸の正の方向に沿って移動していたことがわかります。文字を左右に動かすと、Y軸の値が変わります。 A1、A2、A3のY値は、回転後に配列Yyに配置されます。文字は、Y軸の負の方向に沿って移動します。キャラクターが左右に動き、X軸の値を変更します。値Xxは、B1、B2、およびB3のX値です。

HowturnRotator = FRotator(0, 0, 0)//This sentence is written in the constructor void ARunGame3Character::Turn() { //When the current direction and HowturnRotator are not equal if (UKismetMathLibrary::NotEqual_RotatorRotator(GetControlRotation(), HowturnRotator,0.000001)) { UGameplayStatics::GetPlayerController(GetWorld(),0) ->AController::SetControlRotation( UKismetMathLibrary::RInterpTo(GetControlRotation(), HowturnRotator, UGameplayStatics::GetWorldDeltaSeconds(GetWorld()), 5.0f)) } } void ARunGame3Character::Left() { if (canturn)//Can turn { HowturnRotator = UKismetMathLibrary::ComposeRotators (HowturnRotator, FRotator(0, -90, 0)) canturn = false } else //No { Bbb = UKismetMathLibrary::Clamp(Aaa - 1, 0, 2)//Limit range ZoomTimeline.PlayFromStart()//Play timeline } } Turn()//This sentence is written in Tick

ここではbool変数canturnが使用され、キャラクターが特別なフロアに入ると、デフォルトでcanturnはfalseになります。衝突イベントにより、canturnがtrueになります。これは、キャラクターが回転するための前提条件です。 canturnがtrueの場合、←(→)を押すと、FRotatorタイプの変数が変わります。ここで使用されるComposeRotators()関数は、2つのFRotatorタイプのデータを組み合わせたものとして理解できます。つまり、文字はヨー方向に-90°回転し、Turn()関数は文字の回転を設定する特定のプロセスです。

  • しかし、私が解決していない問題があります。道路がキャラクターに左折するように求めたら、→キーを押してください。どうすればよいですか?
  • 私はあまり合理的ではない(最適化が必要な)いくつかの解決策を考えました:
    1.最初に、対応する配列の値を変更しますが、この方法は非常に肥大化しています。 4つのケースを考慮する必要があり(おそらくここにパラメーターを持つ関数を書くことができます)、キャラクターの回転方向が間違っていると、0.1秒以内に壁に衝突して死にます
    2.次に、キャラクターが間違った方向に回転するのを禁止することを考えましたが、これによりゲームの難易度が下がり、ゲームの仕組みが変わります。

ここでは、レイジーメソッドを選択します。配列の値は変更せず、衝突がデスイベントをトリガーするまで0.1秒待ちます。

Character.h(ここには説明のつかない場所があります。これについては後で説明します)

#pragma once #include 'CoreMinimal.h' #include 'GameFramework/Character.h' #include 'Components/TimelineComponent.h' #include 'Kismet/GameplayStatics.h' #include 'RunGame3Character.generated.h' class URunHUD class UEndGameWidget class UPauseGameWidget UCLASS(config=Game) class ARunGame3Character : public ACharacter { GENERATED_BODY() //Camera rod UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = 'true')) class USpringArmComponent* CameraBoom //Camera UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = 'true')) class UCameraComponent* FollowCamera public: ARunGame3Character()//Constructor int cities//Number of magnets int tiaoxies//Number of jumping shoes bool IsMagnet//Whether to pick up the magnet FRotator HowturnRotator//The direction of rotation int Aaa//Current road position int Bbb//New road location int Yy[3] = { 0,0,0 }//Array used for left and right int Xx[3] = { 0,0,0 } bool canturn//Is it possible to rotate /*Timeline*/ UPROPERTY(EditAnywhere,BlueprintReadWrite) class UCurveFloat * FloatCurve//curve FTimeline ZoomTimeline//Timeline UFUNCTION() void DoZoom(float Rood)//The specific execution function of the timeline UFUNCTION() void Emmm()//The function executed after the timeline is completed /*Timeline*/ virtual void Tick(float DeltaSeconds)override//tick UFUNCTION() void MoveForward()//forward UFUNCTION() void Left()//left UFUNCTION() void Right()//To the right UFUNCTION() void Down()//decline UPROPERTY(EditDefaultsOnly, Category = UI) TSubclassOf<URunHUD>runclass URunHUD *runnerhud//game interface UPROPERTY(EditDefaultsOnly, Category = UI) TSubclassOf<UEndGameWidget>egclass UEndGameWidget *egwidget//Death interface UPROPERTY(EditDefaultsOnly, Category = UI) TSubclassOf<UPauseGameWidget>pgclass UPauseGameWidget *pgwidget//Pause interface UFUNCTION() void RestartCallback()//Replay button UFUNCTION() void menuCallback()//Replay button UFUNCTION() void pauseCallback()//Pause button UFUNCTION() void backgameCallback()//Continue to work UFUNCTION() void exitgameCallback()//Exit valid UFUNCTION() void DeathFunction()//Character death UFUNCTION() void Turn()//Rotate the character protected: virtual void BeginPlay() override//Function to start the game //Highest score record FString SlotName class UHigeSaveGame *SaveInstance protected: //Key binding virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override public: //Camera and camera rod FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom } FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera } }

Character.cpp

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include 'RunGame3Character.h' #include 'RunGame3GameMode.h'/*3.11*/ #include 'HeadMountedDisplayFunctionLibrary.h' #include 'Camera/CameraComponent.h' #include 'Components/CapsuleComponent.h' #include 'Components/InputComponent.h' #include 'GameFramework/CharacterMovementComponent.h' #include 'GameFramework/Controller.h' #include 'Kismet/KismetMathLibrary.h'/*3.17*/ #include 'Engine/Engine.h'/*3.17*/ #include 'RunHUD.h' #include 'EndGameWidget.h' #include 'PauseGameWidget.h' #include 'Components/TextBlock.h' #include 'Components/Button.h' #include 'Components/Widget.h' #include 'Components/TimelineComponent.h' #include 'Blueprint/UserWidget.h' #include 'Components/PrimitiveComponent.h' #include 'Components/SkeletalMeshComponent.h' #include 'GameFramework/SpringArmComponent.h' #include 'HigeSaveGame.h' ////////////////////////////////////////////////////////////////////////// // ARunGame3Character //Constructor ARunGame3Character::ARunGame3Character() { PrimaryActorTick.bCanEverTick = true//Set the timeline to be executable // Capsule body GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f) // Do not rotate when the controller is rotating. Let it affect the camera. bUseControllerRotationPitch = false bUseControllerRotationYaw = false bUseControllerRotationRoll = false // Configure character movement, GetCharacterMovement()->bOrientRotationToMovement = true // Character moves in the direction of input... GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f) // ...at this rotation rate GetCharacterMovement()->JumpZVelocity = 600.f GetCharacterMovement()->AirControl = 0.2f //GetCharacterMovement()->MaxWalkSpeed ​​= 1000.f//Set the speed of character movement // Create a camera rod (pull towards the player in the event of a collision) CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT('CameraBoom')) CameraBoom->SetupAttachment(RootComponent) CameraBoom->TargetArmLength = 300.0f // The camera follows at this distance behind the character CameraBoom->bUsePawnControlRotation = true // Rotate the arm based on the controller // Create a camera FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT('FollowCamera')) FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName) // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation FollowCamera->bUsePawnControlRotation = true // Camera does not rotate relative to arm cities = 0 tiaoxies = 0 IsMagnet = false Aaa = 1 Bbb = 0 canturn = false HowturnRotator = FRotator(0, 0, 0) SlotName = 'HighscoreData' } //play again void ARunGame3Character::RestartCallback() { UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(),'restartlevel',nullptr)//Play the switch card again } //Return to the main menu void ARunGame3Character::menuCallback() { UGameplayStatics::OpenLevel(GetWorld(), 'MainMenuMap')//Play the switch card } //death void ARunGame3Character::DeathFunction() { GetMesh()->SetSimulatePhysics(true)//Set characters to simulate physics GetCharacterMovement()->DisableMovement()//Stop exercise ARunGame3GameMode * RunGameGa = Cast<ARunGame3GameMode>(UGameplayStatics::GetGameMode(GetWorld())) egwidget = CreateWidget<UEndGameWidget>(GetWorld(), egclass)//Create death interface egwidget->AddToViewport()//Display death interface egwidget->endfenfen->SetText(FText::FromString(FString::FromInt(RunGameGa->fenshu)))//Display score and number of coins egwidget->endqianqian->SetText(FText::FromString(FString::FromInt(RunGameGa->yingbi))) if(egwidget->restart)//Click to replay { FScriptDelegate ScriptDelegate ScriptDelegate.BindUFunction(this, FName(TEXT('RestartCallback')))//Delegate RestartCallback egwidget->restart->OnClicked.Add(ScriptDelegate) } if (egwidget->menu)//Click to return to the main menu { FScriptDelegate ScriptDelegate ScriptDelegate.BindUFunction(this, FName(TEXT('menuCallback')))//Delegate menuCallback egwidget->menu->OnClicked.Add(ScriptDelegate) } //Archive if (UGameplayStatics::DoesSaveGameExist(SlotName, 0))//If archive exists { SaveInstance = Cast<UHigeSaveGame>(UGameplayStatics::LoadGameFromSlot(SlotName, 0))//Create a UHigeSaveGame if (SaveInstance->HighscoreValue < RunGameGa->fenshu)//When this game score is greater than the old highest score { SaveInstance->HighscoreValue = RunGameGa->fenshu//The score in this round is the new highest score UGameplayStatics::SaveGameToSlot(SaveInstance, SlotName, 0)//Archive } egwidget->endgaogao->SetText(FText::FromString(FString::FromInt(SaveInstance->HighscoreValue)))//Display the highest score } else//If there is no archive { //Create archive SaveInstance = Cast<UHigeSaveGame>(UGameplayStatics::CreateSaveGameObject(UHigeSaveGame::StaticClass())) SaveInstance->HighscoreValue = RunGameGa->fenshu//The highest score for this round UGameplayStatics::SaveGameToSlot(SaveInstance, SlotName, 0)//Archive egwidget->endgaogao->SetText(FText::FromString(FString::FromInt(SaveInstance->HighscoreValue)))//Display the highest score } } //Character rotation void ARunGame3Character::Turn() { //When the current direction and HowturnRotator are not equal if (UKismetMathLibrary::NotEqual_RotatorRotator(GetControlRotation(), HowturnRotator,0.000001)) { UGameplayStatics::GetPlayerController(GetWorld(),0)->AController::SetControlRotation( UKismetMathLibrary::RInterpTo(GetControlRotation(), HowturnRotator, UGameplayStatics::GetWorldDeltaSeconds(GetWorld()), 5.0f)) //Set position (rotate from (current direction to HowturnRotator), per second, rotation speed) } } //Continue the game void ARunGame3Character::backgameCallback() { UGameplayStatics::SetGamePaused(GetWorld(), false)//Cancel the game pause pgwidget->RemoveFromParent()//Remove the pause interface } //exit the game void ARunGame3Character::exitgameCallback() { UKismetSystemLibrary::QuitGame(this, nullptr, EQuitPreference::Quit, true) } //Pause the game void ARunGame3Character::pauseCallback() { UGameplayStatics::SetGamePaused(GetWorld(), true)//Pause the game pgwidget = CreateWidget<UPauseGameWidget>(GetWorld(), pgclass)//Create a pause interface pgwidget->AddToViewport()//display if (pgwidget->backgame)//Continue the game { FScriptDelegate ScriptDelegate ScriptDelegate.BindUFunction(this, FName(TEXT('backgameCallback')))//Binding delegate pgwidget->backgame->OnClicked.Add(ScriptDelegate) } if (pgwidget->exitgame)//exit the game { FScriptDelegate ScriptDelegate ScriptDelegate.BindUFunction(this, FName(TEXT('exitgameCallback')))//Binding delegate pgwidget->exitgame->OnClicked.Add(ScriptDelegate) } } //Games start void ARunGame3Character::BeginPlay() { Super::BeginPlay() runnerhud = CreateWidget<URunHUD>(GetWorld(), runclass)//Create game interface runnerhud->AddToViewport()//display //Display the highest score runnerhud->gaogao->SetText(FText::FromString(FString::FromInt(Cast<UHigeSaveGame>(UGameplayStatics::LoadGameFromSlot(SlotName, 0))->HighscoreValue))) if (runnerhud->pauseButton)//Pause the game { FScriptDelegate ScriptDelegate ScriptDelegate.BindUFunction(this, FName(TEXT('pauseCallback')))//Binding delegate runnerhud->pauseButton->OnClicked.Add(ScriptDelegate) } //FloatCurve = NewObject()//Initialize curve //FloatCurve->FloatCurve.AddKey(0.f, 0.f) //FloatCurve->FloatCurve.AddKey(0.1f, 1.f) //Initialize the time axis, time axis binding curve and TimelineCallBack, TCB binding specific execution function //Binding timeline FOnTimelineFloatStatic TimelineCallBack TimelineCallBack.BindUFunction(this, TEXT('DoZoom'))//Delegated functions executed by the timeline FOnTimelineEventStatic onTimelineFinishedCallback onTimelineFinishedCallback.BindUFunction(this, FName{ TEXT('Emmm') })//The function executed after the commissioning timeline is completed ZoomTimeline.AddInterpFloat(FloatCurve, TimelineCallBack) ZoomTimeline.SetTimelineLength(0.1f) ZoomTimeline.SetTimelineFinishedFunc(onTimelineFinishedCallback) } //Key binding void ARunGame3Character::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) { check(PlayerInputComponent) PlayerInputComponent->BindAction('Jump', IE_Pressed, this, &ACharacter::Jump) PlayerInputComponent->BindAction('Jump', IE_Released, this, &ACharacter::StopJumping) PlayerInputComponent->BindAction('Left', IE_Pressed, this, &ARunGame3Character::Left) PlayerInputComponent->BindAction('Right', IE_Pressed, this, &ARunGame3Character::Right) PlayerInputComponent->BindAction('Down', IE_Pressed, this, &ARunGame3Character::Down) } //Move forward void ARunGame3Character::MoveForward() { AddMovementInput(UKismetMathLibrary::GetForwardVector( FRotator(0, GetControlRotation().Yaw, 0)) , 1.0f, false) } //Functions executed by the timeline void ARunGame3Character::DoZoom(float Rood) { if (UKismetMathLibrary::NearlyEqual_FloatFloat(Yy[0],Yy[1],0.1))//When characters X are different and Y are the same { //UE_LOG(LogTemp, Warning, TEXT('111')) SetActorLocation(FVector( (UKismetMathLibrary::Lerp(Xx[Aaa], Xx[Bbb], Rood)), GetCapsuleComponent()->USceneComponent::GetComponentLocation().Y, GetCapsuleComponent()->USceneComponent::GetComponentLocation().Z)) //near } else//When character Y is different and X is the same { //UE_LOG(LogTemp, Warning, TEXT('222')) SetActorLocation(FVector(GetCapsuleComponent()->USceneComponent::GetComponentLocation().X, (UKismetMathLibrary::Lerp(Yy[Aaa], Yy[Bbb], Rood)), GetCapsuleComponent()->USceneComponent::GetComponentLocation().Z)) } } //After the timeline is completed void ARunGame3Character::Emmm() { Aaa = Bbb } //Left arrow key void ARunGame3Character::Left() { if (canturn)//Can turn { //Set HowturnRotator, no turning HowturnRotator = UKismetMathLibrary::ComposeRotators(HowturnRotator, FRotator(0, -90, 0)) canturn = false } else //No { Bbb = UKismetMathLibrary::Clamp(Aaa - 1, 0, 2)//Limit range ZoomTimeline.PlayFromStart()//Play timeline } } //Right arrow key void ARunGame3Character::Right() { if (canturn) { HowturnRotator = UKismetMathLibrary::ComposeRotators(HowturnRotator, FRotator(0, 90, 0)) canturn = false } else { Bbb = UKismetMathLibrary::Clamp(Aaa + 1, 0, 2) ZoomTimeline.PlayFromStart() } } //decline void ARunGame3Character::Down() { //Apply force GetCharacterMovement()->UCharacterMovementComponent::AddImpulse(FVector(0.f,0.f,-3000.f),true) } //tick void ARunGame3Character::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds) Turn()//Rotation function MoveForward()//Move forward ZoomTimeline.TickTimeline(DeltaSeconds)//Pass the time into the timeline }