上一次修改时间:2019-03-16 23:03:51

跑酷游戏代码

引擎:UE4 4.18.3

代码结构

图片.png

运行图

图片.png

PikaqiuThirdTestGameMode.h

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "PikaqiuThirdTestGameMode.generated.h"

UCLASS(minimalapi)
class APikaqiuThirdTestGameMode : public AGameModeBase
{
    GENERATED_BODY()

    //存储直线的地板
    UPROPERTY()
    TArray<TSubclassOf<class AActor>> pikaMidArr;
    //存储左右的地板
    UPROPERTY()
    TArray<TSubclassOf<class AActor>> pikaLRArr;

public:
    APikaqiuThirdTestGameMode();

    virtual void BeginPlay() override;

    //添加地板
    void PikaAddFloor();
    //打印测试
    void PikaPirnt(FString piksStr);

    TSubclassOf<AActor> pikaNewFloor;

    FTransform pikaNextTransforms;

    //获取随机的地板
    TSubclassOf<AActor> PikaRandomInputFloor();

    //将UI界面加载到屏幕上
    void PikaUIAddToScreen();
    //UI实例变量
    UUserWidget *PikaMainUI;
    TSubclassOf<UUserWidget> PikaUIMainInstance;

    //确定金币的生成方向X轴或Y轴
    //1:X轴负方向 2:Y轴负方向 3:X轴正方向 4:Y轴正方向
    //LEFT -1 RIGHT +1
    int32 IsX;

};

PikaqiuThirdTestGameMode.cpp

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#include "PikaqiuThirdTestGameMode.h"
#include "PikaqiuThirdTestCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include "PikaFloor.h"
#include "PikaMainUI.h"
#include "Engine.h"
#include "PikaGameStateBase.h"

APikaqiuThirdTestGameMode::APikaqiuThirdTestGameMode()
{
    // set default pawn class to our Blueprinted character
    static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));
    if (PlayerPawnBPClass.Class != NULL)
    {
        DefaultPawnClass = PlayerPawnBPClass.Class;
    }
    //初始化容器
    pikaMidArr.Empty();
    pikaLRArr.Empty();

    //获取蓝图中的直线地板
    TSubclassOf<AActor> pikaFloorBP01 = LoadClass<AActor>(NULL , TEXT("Blueprint'/Game/Floor/BP/PikaFloorBP01.PikaFloorBP01_C'"));
    pikaMidArr.Add(pikaFloorBP01);
    TSubclassOf<AActor> pikaFloorBP02 = LoadClass<AActor>(NULL, TEXT("Blueprint'/Game/Floor/BP/PikaFloorBP02.PikaFloorBP02_C'"));
    pikaMidArr.Add(pikaFloorBP02);
    TSubclassOf<AActor> pikaFloorBP03 = LoadClass<AActor>(NULL, TEXT("Blueprint'/Game/Floor/BP/PikaFloorBP03.PikaFloorBP03_C'"));
    pikaMidArr.Add(pikaFloorBP03);

    //获取蓝图中的上下地板
    TSubclassOf<AActor> pikaFloorBP04 = LoadClass<AActor>(NULL, TEXT("Blueprint'/Game/Floor/BP/PikaFloorBP04.PikaFloorBP04_C'"));
    pikaMidArr.Add(pikaFloorBP04);
    TSubclassOf<AActor> pikaFloorBP05 = LoadClass<AActor>(NULL, TEXT("Blueprint'/Game/Floor/BP/PikaFloorBP05.PikaFloorBP05_C'"));
    pikaMidArr.Add(pikaFloorBP05);

    //获取蓝图中的左右地板
    TSubclassOf<AActor> pikaFloorBP06 = LoadClass<AActor>(NULL, TEXT("Blueprint'/Game/Floor/BP/PikaFloorBP06.PikaFloorBP06_C'"));
    pikaLRArr.Add(pikaFloorBP06);
    TSubclassOf<AActor> pikaFloorBP07 = LoadClass<AActor>(NULL, TEXT("Blueprint'/Game/Floor/BP/PikaFloorBP07.PikaFloorBP07_C'"));
    pikaLRArr.Add(pikaFloorBP07);

    //获取自定义的UI类实例
    static ConstructorHelpers::FClassFinder<UUserWidget> PikaWidget(TEXT("/Game/UMG/PikaMainUI"));
    PikaUIMainInstance = PikaWidget.Class;

    //加载我们的GameState
    GameStateClass = APikaGameStateBase::StaticClass();

    IsX = 1;
}

void APikaqiuThirdTestGameMode::BeginPlay()
{
    Super::BeginPlay();
    if (GetWorld()) 
    {
        for (int32 i=0 ; i<10 ; i++) 
        {
            PikaAddFloor();
        }
    }
    //将UI界面加载到屏幕上
    PikaUIAddToScreen();
}

void APikaqiuThirdTestGameMode::PikaAddFloor() 
{
    pikaNewFloor = PikaRandomInputFloor();
    
    if (pikaNewFloor != NULL) 
    {
        FVector const pikaLoction = pikaNextTransforms.GetLocation();
        FRotator pikaRotation(pikaNextTransforms.Rotator());
        //在场景中放入地板
        APikaFloor *pikaFloor = GetWorld()->SpawnActor<APikaFloor>(pikaNewFloor, pikaLoction, pikaRotation);
        //记录当前地板的位置,做为下一个生成的地板的初始位置
        pikaNextTransforms = pikaFloor->PikaGetAttachToTransform();
        //PikaPirnt(pikaNextTransforms.ToString());
    }
}

void APikaqiuThirdTestGameMode::PikaPirnt(FString piksStr)
{
    if (GEngine) 
    {
        GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, piksStr);
    }
}

//获取随机的地板
TSubclassOf<AActor> APikaqiuThirdTestGameMode::PikaRandomInputFloor() 
{
    //return pikaMidArr[2];
    int32 pikaRandom = FMath::RandRange(1, 100);
    //生成直线地板(包括上下地板)的概率
    int32 pikaProbMid = 60;

    if (pikaRandom <= pikaProbMid)
    {    
        //生成直线地板
        int32 pikaSumMidArr = pikaMidArr.Num();
        if (pikaSumMidArr)
        {
            //组数开始的下标为0
            int32 pikaIndexMid = FMath::RandRange(0, pikaSumMidArr - 1);

            return pikaMidArr[pikaIndexMid];
        }
    }
    else 
    {
        //生成左右地板
        int32 pikaSumLRArr = pikaLRArr.Num();
        if (pikaSumLRArr)
        {
            int32 pikaIndexLR = FMath::RandRange(0, pikaSumLRArr - 1);

            return pikaLRArr[pikaIndexLR];
        }
    }

    return NULL;
}

//将UI界面加载到屏幕上PikaMainUI
void APikaqiuThirdTestGameMode::PikaUIAddToScreen() 
{
    if (PikaUIMainInstance)
    {
        if (GetWorld())
        {
            PikaMainUI = CreateWidget<UUserWidget>(GetWorld(), PikaUIMainInstance);
            if (PikaMainUI)
            {
                PikaMainUI->AddToViewport();

                UPikaMainUI *PikaMain = Cast<UPikaMainUI>(PikaMainUI);
                if (PikaMain)
                {
                    //初始化金币数和步数
                    PikaMain->pikaCoinNum = FText::FromString(TEXT("0"));
                    PikaMain->pikaCharacterDistance = FText::FromString(TEXT("0"));
                }
            }
        }

    }
}

PikaqiuThirdTestCharacter.h

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "PikaqiuThirdTestCharacter.generated.h"

UCLASS(config = Game)
class APikaqiuThirdTestCharacter : public ACharacter
{
    GENERATED_BODY()

        /** Camera boom positioning the camera behind the character */
        UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
        class USpringArmComponent* CameraBoom;

    /** Follow camera */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
        class UCameraComponent* FollowCamera;
public:
    APikaqiuThirdTestCharacter();

    /** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
        float BaseTurnRate;

    /** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
        float BaseLookUpRate;

protected:

    /** Resets HMD orientation in VR. */
    void OnResetVR();

    /** Called for forwards/backward input */
    void MoveForward(float Value);

    /** Called for side to side input */
    void MoveRight(float Value);

    /**
     * Called via input to turn at a given rate.
     * @param Rate    This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
     */
    void TurnAtRate(float Rate);

    /**
     * Called via input to turn look up/down at a given rate.
     * @param Rate    This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
     */
    void LookUpAtRate(float Rate);

    /** Handler for when a touch input begins. */
    void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);

    /** Handler for when a touch input stops. */
    void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);

protected:
    // APawn interface
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    // End of APawn interface

public:
    /** Returns CameraBoom subobject **/
    FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
    /** Returns FollowCamera subobject **/
    FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }

    /********************************系统生成的代码分割线**********************************/

private:
    //右手状态
    bool rightState;
    //左手状态
    bool leftState;
    //能否旋转
    bool canTrun;
    //死亡状态
    bool deathState;
    //导向旋转
    FRotator desireRotation;

protected:
    //右转身函数
    void RightTurn();
    //左转身函数
    void LeftTurn();
    //按键响应函数
    void PikaSetupPlayerInputComponent(class UInputComponent *InputComponent);

public:
    //模拟的构造函数
    void PikaStructure();
    //重载BeginPlay和Tick函数
    virtual void BeginPlay() override;
    virtual void Tick(float DeltaSeconds) override;

    //自动向前跑
    void KeepMoving();
    FVector JudgeRAndL(FRotator pikaRotation);
    FVector AddInputMoveForworld();

    void PikaPirnt(FString  pikaStr);

    FRotator PikaCombinRotation(FRotator rotation1, FRotator rotation2);

    //设置角色的转向
    void PikaCharacterTurn();
};

PikaqiuThirdTestCharacter.cpp

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

#include "PikaqiuThirdTestCharacter.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "Engine.h"

//////////////////////////////////////////////////////////////////////////
// APikaqiuThirdTestCharacter

APikaqiuThirdTestCharacter::APikaqiuThirdTestCharacter()
{
    // Set size for collision capsule
    GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

    // set our turn rates for input
    BaseTurnRate = 45.f;
    BaseLookUpRate = 45.f;

    // Don't rotate when the controller rotates. Let that just 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;

    // Create a camera boom (pulls in towards the player if there is 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 follow 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 = false; // Camera does not rotate relative to arm

    // Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character) 
    // are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)

    PikaStructure();
}

//////////////////////////////////////////////////////////////////////////
// Input

void APikaqiuThirdTestCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
    // Set up gameplay key bindings
    check(PlayerInputComponent);
    PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
    PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

    PlayerInputComponent->BindAxis("MoveForward", this, &APikaqiuThirdTestCharacter::MoveForward);
    PlayerInputComponent->BindAxis("MoveRight", this, &APikaqiuThirdTestCharacter::MoveRight);

    // We have 2 versions of the rotation bindings to handle different kinds of devices differently
    // "turn" handles devices that provide an absolute delta, such as a mouse.
    // "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
    PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
    PlayerInputComponent->BindAxis("TurnRate", this, &APikaqiuThirdTestCharacter::TurnAtRate);
    PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
    PlayerInputComponent->BindAxis("LookUpRate", this, &APikaqiuThirdTestCharacter::LookUpAtRate);

    // handle touch devices
    PlayerInputComponent->BindTouch(IE_Pressed, this, &APikaqiuThirdTestCharacter::TouchStarted);
    PlayerInputComponent->BindTouch(IE_Released, this, &APikaqiuThirdTestCharacter::TouchStopped);

    // VR headset functionality
    PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &APikaqiuThirdTestCharacter::OnResetVR);

    //皮卡丘按键响应
    //PikaSetupPlayerInputComponent(PlayerInputComponent);
}


void APikaqiuThirdTestCharacter::OnResetVR()
{
    UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}

void APikaqiuThirdTestCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
    Jump();
}

void APikaqiuThirdTestCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
    StopJumping();
}

void APikaqiuThirdTestCharacter::TurnAtRate(float Rate)
{
    // calculate delta for this frame from the rate information
    AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void APikaqiuThirdTestCharacter::LookUpAtRate(float Rate)
{
    // calculate delta for this frame from the rate information
    AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

void APikaqiuThirdTestCharacter::MoveForward(float Value)
{
    if ((Controller != NULL) && (Value != 0.0f))
    {
        // find out which way is forward
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);

        // get forward vector
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
        AddMovementInput(Direction, Value);
    }
}

void APikaqiuThirdTestCharacter::MoveRight(float Value)
{
    if ((Controller != NULL) && (Value != 0.0f))
    {
        // find out which way is right
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);

        // get right vector 
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
        // add movement in that direction
        AddMovementInput(Direction, Value);
    }
}

/********************************系统生成的代码分割线**********************************/

//模拟的构造函数
void APikaqiuThirdTestCharacter::PikaStructure()
{
    //初始化基础变量
    //右手状态
    rightState = false;
    //左手状态
    leftState = false;
    //能否旋转
    canTrun = true;
    //死亡状态
    deathState = false;
    //导向旋转
    desireRotation = FRotator(0);
}

//重载BeginPlay和Tick函数
void APikaqiuThirdTestCharacter::BeginPlay()
{
    Super::BeginPlay();
}
void APikaqiuThirdTestCharacter::Tick(float DeltaSeconds)
{
    Super::Tick(DeltaSeconds);

    if (deathState)
    {

    }
    else
    {
        //PikaCharacterTurn();
        KeepMoving();
    }
}

//自动向前跑
void APikaqiuThirdTestCharacter::KeepMoving()
{
    AddMovementInput(JudgeRAndL(desireRotation), 1);
}

FVector APikaqiuThirdTestCharacter::JudgeRAndL(FRotator pikaRotation)
{
    if (rightState)
    {
        rightState = false;

        return AddInputMoveForworld();
    }
    else if (leftState)
    {
        leftState = false;

        return AddInputMoveForworld();
    }
    else
    {
        return AddInputMoveForworld();
    }
}

FVector APikaqiuThirdTestCharacter::AddInputMoveForworld()
{
    //获取角色的旋转值
    const FRotator pikaRotation = Controller->GetControlRotation();
    //保证只有Yaw变化
    const FRotator yawRotation(0, pikaRotation.Yaw, 0);
    const FVector desiron = FRotationMatrix(yawRotation).GetUnitAxis(EAxis::X);

    return desiron;

}

void APikaqiuThirdTestCharacter::PikaPirnt(FString  pikaStr)
{
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 0.5f, FColor::Red, pikaStr);
    }
}

//按键响应
void APikaqiuThirdTestCharacter::PikaSetupPlayerInputComponent(class UInputComponent *InputComponent)
{
    //键盘按键
    InputComponent->BindAction("pikaRight", IE_Pressed, this, &APikaqiuThirdTestCharacter::RightTurn);
    InputComponent->BindAction("pikaLeft", IE_Pressed, this, &APikaqiuThirdTestCharacter::LeftTurn);
}

FRotator APikaqiuThirdTestCharacter::PikaCombinRotation(FRotator rotation1, FRotator rotation2)
{
    FQuat aQuat = FQuat(rotation1);
    FQuat bQuat = FQuat(rotation2);

    return FRotator(aQuat * bQuat);
}

//右转身函数
void APikaqiuThirdTestCharacter::RightTurn()
{
    if (canTrun)
    {
        //顺时针旋转90度
        FRotator pikaRotation = FRotator(0, 90, 0);
        desireRotation = PikaCombinRotation(desireRotation, pikaRotation);
        rightState = true;
    }
    
}
//左转身函数
void APikaqiuThirdTestCharacter::LeftTurn()
{
    if (canTrun)
    {
        //逆时针旋转90度
        FRotator pikaRotation = FRotator(0, -90, 0);
        desireRotation = PikaCombinRotation(desireRotation, pikaRotation);
        leftState = true;
    }
}

// 设置角色的转向
void APikaqiuThirdTestCharacter::PikaCharacterTurn()
{
    GetController()->SetControlRotation(FMath::RInterpTo(GetControlRotation(), desireRotation, GetWorld()->GetRealTimeSeconds(), 10));
}

PikaqiuThirdTest.Build.cs

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class PikaqiuThirdTest : ModuleRules
{
    public PikaqiuThirdTest(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
        //模块加入
        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" , "UMG"});
    }
}

PikaMainUI.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "PikaMainUI.generated.h"

/**
 * 
 */
UCLASS()
class PIKAQIUTHIRDTEST_API UPikaMainUI : public UUserWidget
{
    GENERATED_BODY()
    
public:
    //主界面UI上记录金币数和步数的变量
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = UI)
    FText pikaCoinNum;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = UI)
    FText pikaCharacterDistance;
    
};

PikaGameStateBase.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "PikaGameStateBase.generated.h"

/**
 * 
 */
UCLASS()
class PIKAQIUTHIRDTEST_API APikaGameStateBase : public AGameStateBase
{
    GENERATED_BODY()
    
    
public:
    APikaGameStateBase();
    //记录用户的金币数和步数
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = UI)
    int32 PikaGlod;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = UI)
    int32 PikaDistance;

    bool PikaStopLoopForCharacterDistance;

    virtual void BeginPlay() override;

    //计算角色移动的距离
    void PikaAddDistance();
    //计算三维空间的距离,即角色移动向量的模
    float PikaGetVectorLeng(FVector Vctor);

    FTimerHandle PikaHandle;
    
};

PikaGameStateBase.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "PikaGameStateBase.h"
#include "PikaqiuThirdTestCharacter.h"
#include "PikaqiuThirdTestGameMode.h"
#include "PikaMainUI.h"
#include "GameFramework/CharacterMovementComponent.h"



APikaGameStateBase::APikaGameStateBase() 
{
    //金币
    PikaGlod = 0;
    //角色距离
    PikaDistance = 0;

    PikaStopLoopForCharacterDistance = true;
}

void APikaGameStateBase::BeginPlay()
{
    Super::BeginPlay();

    GetWorldTimerManager().SetTimer(PikaHandle, this, &APikaGameStateBase::PikaAddDistance, 0.03f, true);
}

//计算角色移动的距离
void APikaGameStateBase::PikaAddDistance() 
{
    if (PikaHandle.IsValid())
    {
        //GetWorldTimerManager().ClearTimer(PikaHandle);
    }
    if (PikaStopLoopForCharacterDistance)
    {
        if (GetWorld()) 
        {
            //获取GameMode的实例
            APikaqiuThirdTestGameMode *pikaGameMode = Cast<APikaqiuThirdTestGameMode>(GetWorld()->GetAuthGameMode());
            //获取PikaqiuThirdTestCharacter的实例
            APikaqiuThirdTestCharacter *PikaCharacter = Cast<APikaqiuThirdTestCharacter>(GetWorld()->GetFirstPlayerController()->GetPawn());
            
            if (PikaCharacter && pikaGameMode)
            {
                //计算角色移动距离
                //GetMovementComponent()需要GameFramework/CharacterMovementComponent.h头文件
                int32 TempDistance = (PikaGetVectorLeng(PikaCharacter->GetMovementComponent()->Velocity))*0.03f;
                PikaDistance += TempDistance;
                //更新到UI界面上
                UPikaMainUI *PikaMain = Cast<UPikaMainUI>(pikaGameMode->PikaMainUI);
                if (PikaMain)
                {
                    PikaMain->pikaCharacterDistance = FText::FromString(FString::FromInt(PikaDistance));
                }

            }
            //设置计算一次距离的时间
            GetWorldTimerManager().SetTimer(PikaHandle, this, &APikaGameStateBase::PikaAddDistance, 0.03f, true);
        }
    }
}

//计算三维空间的距离,即角色移动向量的模
float APikaGameStateBase::PikaGetVectorLeng(FVector Vctor)
{
    return sqrt(Vctor.X*Vctor.X + Vctor.Y*Vctor.Y + Vctor.Z*Vctor.Z);
}

PikaFloor.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "Components/ArrowComponent.h"
#include "PikaFloor.generated.h"

//Floor类型
//暴露给蓝图后,在蓝图中设置好本蓝图的类型,再在代码中根据当前类型来决定金币的生成规则
UENUM()
namespace PikaFloorType
{
    enum PikaType
    {
        StraightFloor,//直线地板
        UpFloor,//往上的地板
        DownFloor,//往下的地板
        LeftFloor,//左转的地板
        RightFloor,//右转的地板
    };
}

UCLASS()
class PIKAQIUTHIRDTEST_API APikaFloor : public AActor
{
    GENERATED_BODY()
    //场景组件
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaqiu", meta = (AllowPrivateAccess = "true"))
    USceneComponent *pikaSeceneComponent;
    //根组件
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaqiu", meta = (AllowPrivateAccess = "true"))
    USceneComponent *pikaRootFCompoent;
    //碰撞
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaqiu", meta = (AllowPrivateAccess = "true"))
    UBoxComponent *pikaBoxCompoent; 
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaqiu", meta = (AllowPrivateAccess = "true"))
    UBoxComponent *pikaTurnComponent; 
    //模型
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaqiu", meta = (AllowPrivateAccess = "true"))
    UStaticMeshComponent *pikaBoxMesh;
    //标记地板生成的位置
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaqiu", meta = (AllowPrivateAccess = "true"))
    UArrowComponent *pikaSpawnPoint;
    //标记左右金币生成的位置
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaqiu", meta = (AllowPrivateAccess = "true"))
    UArrowComponent *pikaSpawnPointR;
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaqiu", meta = (AllowPrivateAccess = "true"))
    UArrowComponent *pikaSpawnPointL;

    //金币Actor,生成金币时用
    TSubclassOf<AActor> PikaCoinActor;
    //金币数组,存储已经生成的金币,用于在销毁地板时,一同销毁金币
    TArray<AActor*> PikaCoinArr;

    //类型枚举
    UPROPERTY(EditDefaultsOnly, Category = "PikaFloorType")
    TEnumAsByte<PikaFloorType::PikaType> PikFloorTypeBP;
public:    
    // Sets default values for this actor's properties
    APikaFloor();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;
    //获取当前Actor(地板)的位置信息
    FTransform PikaGetAttachToTransform();

    //地板里pikaBoxCompoent碰撞体的碰撞函数,使用前需要先进行绑定
    UFUNCTION()
    void PikaOverlap(UPrimitiveComponent *pikaOverlapppedComponent , AActor *pikaOtherActor , UPrimitiveComponent *pikaOtherComp , int32 pikaOtherBodyIndex , bool pikaFromSweepBool , const FHitResult & pikaSweepResult);

    //打印测试
    void PikaPrint(FString piksStr);

    //计时器
    FTimerHandle pikaTimer;

    //销毁对象实例
    void PikaDestroy();

    //在地板随机放置金币
    void PikaGetCoinForFloor();
    //金币生成函数
    void PikaSpawnCoinArr(UArrowComponent *PikaSpawnPos , bool IsMid);
    
};

PikaFloor.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "PikaFloor.h"
#include "Components/StaticMeshComponent.h"
#include "Engine.h"
#include "PikaqiuThirdTestCharacter.h"
#include "PikaqiuThirdTestGameMode.h"
#include "UnrealMathUtility.h"


// Sets default values
APikaFloor::APikaFloor()
{
     // 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;
    //组件加载
    //场景组件
    pikaSeceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("pikaqiuSecene"));
    pikaRootFCompoent = CreateDefaultSubobject<USceneComponent>(TEXT("pikaqiuRoot"));
    pikaTurnComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("pikaTurnBox"));
    pikaBoxMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("pikaBoxMesh"));

    //碰撞
    pikaBoxCompoent = CreateDefaultSubobject<UBoxComponent>(TEXT("pikaCollBox"));

    //标记地板生成的位置
    pikaSpawnPoint = CreateDefaultSubobject<UArrowComponent>(TEXT("pikaSpanwPoint"));
    //标记左右金币生成的位置
    pikaSpawnPointR = CreateDefaultSubobject<UArrowComponent>(TEXT("pikaSpanwPointR"));
    pikaSpawnPointL = CreateDefaultSubobject<UArrowComponent>(TEXT("pikaSpanwPointL"));

    //将本组件的根组件设置为场景组件pikaSeceneComponent
    RootComponent = pikaSeceneComponent;

    //绑定
    pikaRootFCompoent->AttachToComponent(pikaSeceneComponent, FAttachmentTransformRules::KeepRelativeTransform);
    pikaSpawnPoint->AttachToComponent(pikaRootFCompoent, FAttachmentTransformRules::KeepRelativeTransform);
    pikaSpawnPointR->AttachToComponent(pikaRootFCompoent, FAttachmentTransformRules::KeepRelativeTransform);
    pikaSpawnPointL->AttachToComponent(pikaRootFCompoent, FAttachmentTransformRules::KeepRelativeTransform);
    pikaBoxCompoent->AttachToComponent(pikaRootFCompoent, FAttachmentTransformRules::KeepRelativeTransform);
    pikaTurnComponent->AttachToComponent(pikaRootFCompoent, FAttachmentTransformRules::KeepRelativeTransform);
    pikaBoxMesh->AttachToComponent(pikaRootFCompoent, FAttachmentTransformRules::KeepRelativeTransform);

    //获取金币蓝图的Actor
    static ConstructorHelpers::FClassFinder<AActor> PikaCoinBP(TEXT("/Game/Coin/BP/PikaCoinBP01"));
    PikaCoinActor = PikaCoinBP.Class;
}

// Called when the game starts or when spawned
void APikaFloor::BeginPlay()
{
    Super::BeginPlay();

    //绑定pikaBoxCompoent的碰撞函数
    //该函数放在构造函数中时,有可能出现UBoxComponent类没有创建完成,就执行绑定,从而导致绑定失败的情况
    //OnComponentBeginOverlap.AddUniqueDynamic和OnComponentEndOverlap.AddUniqueDynamic分别为碰到碰撞体和离开碰撞体时调用的事件
    pikaBoxCompoent->OnComponentBeginOverlap.AddUniqueDynamic(this, &APikaFloor::PikaOverlap);
    //三渲二
    //pikaBoxMesh->SetRenderCustomDepth(true);
    
    //获取gameMode类的实例
    APikaqiuThirdTestGameMode *PikaGameMode = Cast<APikaqiuThirdTestGameMode>(GetWorld()->GetAuthGameMode());

    //如果不是左右转弯的地板,则生成金币
    if (PikFloorTypeBP == PikaFloorType::LeftFloor)
    {
        PikaGameMode->IsX -= 1;
    }
    else if (PikFloorTypeBP == PikaFloorType::RightFloor)
    {
        PikaGameMode->IsX += 1;
    }
    else if (PikFloorTypeBP == PikaFloorType::UpFloor || PikFloorTypeBP == PikaFloorType::DownFloor)
    {
    
    }
    else
    {
        PikaGetCoinForFloor();
    }
    if (PikaGameMode->IsX > 4)
        PikaGameMode->IsX = 1;
    if (PikaGameMode->IsX < 1)
        PikaGameMode->IsX = 4;
}

// Called every frame
void APikaFloor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

FTransform APikaFloor::PikaGetAttachToTransform()
{
    FTransform pikaTransform;
    //获取当前组件的Location和Rotation
    pikaTransform.SetLocation(pikaSpawnPoint->GetComponentToWorld().GetLocation());
    pikaTransform.SetRotation(pikaSpawnPoint->GetComponentQuat());
    
    return pikaTransform;
}

void APikaFloor::PikaPrint(FString piksStr)
{
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 6.0f, FColor::Red, piksStr);
    }
}

//地板里pikaBoxCompoent碰撞体的碰撞函数,使用前需要先进行绑定
void APikaFloor::PikaOverlap(UPrimitiveComponent *pikaOverlapppedComponent, AActor *pikaOtherActor, UPrimitiveComponent *pikaOtherComp, int32 pikaOtherBodyIndex, bool pikaFromSweepBool, const FHitResult & pikaSweepResult)
{
    //检测碰撞体碰撞到的对象是否是玩家角色
    if (pikaOtherActor->IsA(APikaqiuThirdTestCharacter::StaticClass())) 
    {
        if (GetWorld()) 
        {
            //获取GameMode的实例
            APikaqiuThirdTestGameMode *pikaGameMode = Cast<APikaqiuThirdTestGameMode>(GetWorld()->GetAuthGameMode());
            if (pikaGameMode) 
            {
                pikaGameMode->PikaAddFloor();
                //延时5秒执行销毁地板
                if (pikaTimer.IsValid()) 
                {
                    GetWorldTimerManager().ClearTimer(pikaTimer);
                }
                GetWorldTimerManager().SetTimer(pikaTimer , this , &APikaFloor::PikaDestroy, 5.0f , true);
            }
        }
    }
}

//销毁对象实例
void APikaFloor::PikaDestroy()
{
    //销毁金币
    if (PikaCoinArr.Num() > 0)
    {    
        for (AActor *PikaCoinDestory:PikaCoinArr)
        {
            if (PikaCoinDestory)
            {
                PikaCoinDestory->Destroy(true);
            }
        }
    }
    //销毁地板
    Destroy(true);
}

//在地板随机放置金币
void APikaFloor::PikaGetCoinForFloor()
{
    int32 RandomNum = FMath::FRandRange(0 , 2);

    switch (RandomNum)
    {
    case 0://在右边生成金币
        if (pikaSpawnPointR)
        {
            PikaSpawnCoinArr(pikaSpawnPointR, false);
        }
        break;
    case 1://在左边生成金币
        if (pikaSpawnPointL)
        {
            PikaSpawnCoinArr(pikaSpawnPointL, false);
        }
        break;
    default:
        break;
    }
    
    /*switch (RandomNum)
    {
    case 0://不生成金币
        break;
    case 1://在左边生成金币
        if (pikaSpawnPointL)
        {
            PikaSpawnCoinArr(pikaSpawnPointL , false);
        }
        break;
    case 2://在中间生成金币
        //PikaSpawnCoinArr(pikaSpawnPointL, true);
        break;
    case 3://在右边生成金币
        if (pikaSpawnPointR)
        {
            PikaSpawnCoinArr(pikaSpawnPointR , false);
        }
        break;
    default:
        break;
    }*/
}

//金币生成函数
void APikaFloor::PikaSpawnCoinArr(UArrowComponent *PikaSpawnPos , bool IsMid)
{
    PikaCoinArr.Empty();
    if (PikaCoinActor && GetWorld())
    {
        FVector PikaLocation = PikaSpawnPos->GetComponentLocation();
        FRotator PikaRotator = PikaSpawnPos->GetComponentRotation();
        if (IsMid) 
        {
            //PikaLocation.Y += 340;
        }

        FVector PikaTempLocation;
        for (int32 i=0 ; i<=15 ; i++) 
        {
            //每隔一段距离生成一个金币
            PikaTempLocation = PikaLocation;
            
            //获取gameMode类的实例
            APikaqiuThirdTestGameMode *PikaGameMode = Cast<APikaqiuThirdTestGameMode>(GetWorld()->GetAuthGameMode());
            if (1 == PikaGameMode->IsX) 
            {
                PikaTempLocation.X -= (i * 120);
            }
            else if (2 == PikaGameMode->IsX)
            {
                PikaTempLocation.Y += (i * 120);
            }
            else if (3 == PikaGameMode->IsX)
            {
                PikaTempLocation.X += (i * 120);
            }
            else if (4 == PikaGameMode->IsX)
            {
                PikaTempLocation.Y -= (i * 120);
            }
            else 
            {
                PikaTempLocation.X -= (i * 120);
            }
            
            /*if (PikFloorTypeBP == PikaFloorType::UpFloor)
            {
                PikaTempLocation.Z -= (i * 45);
            }
            else if (PikFloorTypeBP == PikaFloorType::DownFloor)
            {
                PikaTempLocation.Z += (i * 60);
            }*/
            
            AActor *PikaCoinBPObj = GetWorld()->SpawnActor<AActor>(PikaCoinActor, PikaTempLocation, PikaRotator);
            if (PikaCoinBPObj)
            {
                //存储已经生成的金币,当地板被销毁时,金币也一同销毁
                PikaCoinArr.Add(PikaCoinBPObj);
            }
        }
        
    }
}

PikaCoin.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "GameFramework/RotatingMovementComponent.h"
#include "PikaCoin.generated.h"

UCLASS()
class PIKAQIUTHIRDTEST_API APikaCoin : public AActor
{
    GENERATED_BODY()

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaCoin", meta = (AllowPrivateAccess = "true"))
    UStaticMeshComponent *pikaCoinMesh;
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaCoin", meta = (AllowPrivateAccess = "true"))
    UBoxComponent *pikaMeshComponent;
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "pikaCoin", meta = (AllowPrivateAccess = "true"))
    URotatingMovementComponent *pikaRotatingMovement;

    //声音播放
    UPROPERTY(EditDefaultsOnly, Category = "PikaSound")
    USoundBase *PikaEatCoinSound;

public:    
    // Sets default values for this actor's properties
    APikaCoin();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    //金币的碰撞函数
    UFUNCTION()
    void PikaCharacterOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);
    
    //打印测试
    void PikaPirnt(FString piksStr);
};

PikaCoin.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "PikaCoin.h"
#include "PikaqiuThirdTestCharacter.h"
#include "PikaqiuThirdTestGameMode.h"
#include "Engine.h"
#include "PikaGameStateBase.h"
#include "PikaMainUI.h"


// Sets default values
APikaCoin::APikaCoin()
{
     // 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;

    pikaCoinMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("pikaCoinMesh"));
    pikaMeshComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("pikaBoxComponents"));
    pikaRotatingMovement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("pikaRotatingMovement"));

    RootComponent = pikaMeshComponent;
    pikaCoinMesh->SetupAttachment(pikaMeshComponent);
}

// Called when the game starts or when spawned
void APikaCoin::BeginPlay()
{
    Super::BeginPlay();
    //碰撞方式要选择OnComponentBeginOverlap,OnComponentHit方式的碰撞会产生阻力,使碰撞对象停顿
    pikaMeshComponent->OnComponentBeginOverlap.AddDynamic(this, &APikaCoin::PikaCharacterOverlap);
}

// Called every frame
void APikaCoin::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

//金币的碰撞函数
void APikaCoin::PikaCharacterOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
    if (OtherActor->IsA(APikaqiuThirdTestCharacter::StaticClass()))
    {
        if (GetWorld())
        {
            //获取GameMode的实例
            APikaqiuThirdTestGameMode *pikaGameMode = Cast<APikaqiuThirdTestGameMode>(GetWorld()->GetAuthGameMode());
            //获取GameState的实例
            APikaGameStateBase *PikaGameState = Cast<APikaGameStateBase>(GetWorld()->GetGameState());
            if (pikaGameMode && PikaGameState)
            {
                //检测到金币的碰撞后,金币数加1并更新到屏幕上
                PikaGameState->PikaGlod++;
                UPikaMainUI *PikaMain = Cast<UPikaMainUI>(pikaGameMode->PikaMainUI);
                if (PikaMain)
                {
                    PikaMain->pikaCoinNum = FText::FromString(FString::FromInt(PikaGameState->PikaGlod));
                }
            }
            //当吃掉金币后发出金币的声音
            if (PikaEatCoinSound)
            {
                UGameplayStatics::PlaySoundAtLocation(GetWorld(), PikaEatCoinSound, GetActorLocation());
            }
        }
        Destroy(true);
    }
}

void APikaCoin::PikaPirnt(FString piksStr)
{
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(-1, 6.0f, FColor::Red, piksStr);
    }
}

1