# 僵尸爬墙场景AI

通过一个生成器来生成一堆僵尸,向着城墙跑,并成三角状来攀爬城墙。

# 僵尸生成器

下面是僵尸生成器的实现,可作为一般生成器使用:

//=========================================SpawnActor.h===========================================
#pragma once

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

class AZombie;

UCLASS()
class MYPROJECT01_API ASpawnActor : public AActor
{
    GENERATED_BODY()
    
public:	
    // Sets default values for this actor's properties
    ASpawnActor();

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int memberTotalNum = 10;

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        float intervalTime = 0.5; //0.2

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        float minDistance = 100;

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        AActor* deathHeightActor;

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        AActor*  facePointActor;


    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        TArray<TSubclassOf<AZombie>> zombieAActors;

    //My version begin
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        float needToChangeHorizontalDistance = 1000;

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        float changeHorizontalWithinDistance = 1500;
    //My version end

    //UPROPERTY(EditAnywhere, Category = "AAA MoveAdjustParameter")
    //	TArray<AActor*>  climbWall;
    //UPROPERTY(EditAnywhere, Category = "AAA MoveAdjustParameter")
    //	TArray<AActor*>  floorActor;

    UPROPERTY(EditAnywhere, Category = "BBB SpawnComponent")
        UBoxComponent* generateBoxZone;

    FVector generatorCenter;


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

    //随机位置生成:
    AZombie* genAActorAtPosition(const FVector& targetLocation);

    void generateRandomPos();

    UFUNCTION(BlueprintCallable, Category = "CPlusPlusSpawner")
        void generatorMain();


    float generateTime = 0;

    FVector generatorBoundsExtent;
    float xMin;
    float xMax;
    float yMin;
    float yMax;

        //每个MemberCharacter的位置
    TArray<FVector> randomPos;



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

};


//==============================================SpawnActor.cpp==========================================
#include "SpawnActor.h"
#include "Zombie.h"

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

    generateBoxZone = CreateDefaultSubobject<UBoxComponent>(TEXT("GenerateBoxZone"));
    generateBoxZone->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    generateBoxZone->SetVisibility(true);
    generateBoxZone->SetWorldScale3D(FVector(20, 99, 1));
    RootComponent = generateBoxZone;



}

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

    GetActorBounds(false, generatorCenter, generatorBoundsExtent);
    xMin = generatorCenter.X - generatorBoundsExtent.X;
    xMax = generatorCenter.X + generatorBoundsExtent.X;
    yMin = generatorCenter.Y - generatorBoundsExtent.Y;
    yMax = generatorCenter.Y + generatorBoundsExtent.Y;

    UE_LOG(LogTemp, Warning, TEXT("Character Position is %s"), *generatorCenter.ToCompactString());
    UE_LOG(LogTemp, Warning, TEXT("Character width is %s"), *generatorBoundsExtent.ToCompactString());

    
}

//随机位置生成:
AZombie * ASpawnActor::genAActorAtPosition(const FVector& targetLocation)
{
    //if (zombieAActors.Num() == 0)
    //{
    //	UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit,true);
    //}
    AZombie* returnValue = nullptr;

    FRotator targetRot(0, 0, 0);
    FActorSpawnParameters SpawnInfo;

    int randomMember = 0;
    if (zombieAActors.Num() != 0) {
        randomMember = FMath::RandRange(0, zombieAActors.Num() - 1);
        returnValue = GetWorld()->SpawnActor<AZombie>(zombieAActors[randomMember], targetLocation, targetRot, SpawnInfo);
        returnValue->spawnActorObj = this;
        //		allzombieAActors.Add(returnValue);
    }
    return returnValue;
}

void ASpawnActor::generateRandomPos()
{

    //	UE_LOG(LogTemp, Warning, TEXT("Character Position is %s"), *generatorCenter.ToCompactString());
    //	UE_LOG(LogTemp, Warning, TEXT("Character width is %s"), *BoundsExtent.ToCompactString());

    FVector targetLocation(0, 0, 0);

    bool bIsInMinDistance = false;

    for (int i = 0; i < memberTotalNum; ) {
        bIsInMinDistance = false;

        targetLocation.X = FMath::RandRange(xMin, xMax);
        targetLocation.Y = FMath::RandRange(yMin, yMax);
        targetLocation.Z = generatorCenter.Z;

        if (randomPos.Num() != 0) {
            for (int j = 0; j < randomPos.Num(); j++) {
                if (FVector::Distance(targetLocation, randomPos[j]) < minDistance) {
                    bIsInMinDistance = true;
                    break;
                }
            }
        }
        if (!bIsInMinDistance) {
            randomPos.Add(targetLocation);
            i++;
        }
    }
}

void ASpawnActor::generatorMain()
{
    randomPos.Empty();
    generateRandomPos();
    for (int i = 0; i < memberTotalNum; i++) {
        genAActorAtPosition(randomPos[i]);
    }
}

// Called every frame
void ASpawnActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    if (generateTime > intervalTime) {
        generatorMain();
        generateTime = 0;
    }
    else {
        generateTime += DeltaTime;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

# 僵尸逻辑

主要包含各个状态的逻辑处理:

# 版本1

//================================MemberCharacter.h======================================
#pragma once

#include "CoreMinimal.h"
#include "Runtime/Engine/Public/EngineGlobals.h"
#include "components/ArrowComponent.h"
#include "components/CapsuleComponent.h"
#include "components/SphereComponent.h"
#include "Animation/AnimSequence.h"
#include "UObject/ConstructorHelpers.h"
#include "DrawDebugHelpers.h"
#include "Kismet/KismetMathLibrary.h"
#include "MemberCharacter.generated.h"

class ASpawnActor;

UENUM()
enum class EMemberState{
    Init,
    Run,
    Attack,
    Climb,
    Die,
    Jump,
    Falling
};

UCLASS()
class JETPACKANIMATIONSET_API AMemberCharacter : public AActor
{
    GENERATED_BODY()

public:

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int maxRunSpeed = 400;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int runForce = 100000;

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int maxLeftRightSpeed = 300; //200
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bUsingMultiForce"))
        int centerLeftRightForce = 30000; //10000
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int clampSpeedToAttack = 50;


    //UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter" ,meta = (EditCondition = "!bUsingMultiForce"))
    //	int climbForce = 25;
    UPROPERTY(VisibleAnywhere, Category = "AAA SpawnParameter")
        bool bUsingMultiForce = true;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int maxClimbSpeed = 200;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bUsingMultiForce"))
        int climbUpForce = 100000;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        bool bCanBeJump = true;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bCanBeJump"))
        int jumpImpulse = 17000;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bCanBeJump"))
        float jumpInterval = 1;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bCanBeJump"))
        float jumpProbability = 0.1;

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        bool bCanBeDropDeath = true;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bCanBeDropDeath"))
        int droppingBackForce = 100000;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bCanBeDropDeath"))
        int droppingLeftRightForce = 300000;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bCanBeDropDeath"))
        int droppingSpeed = 200;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bCanBeDropDeath"))
        float dropDeathProbability = 0.01;



    UPROPERTY(EditAnywhere, Category = "AAA MoveParameter")
        int distanceToWall = 100;

    UPROPERTY(EditAnywhere, Category = "AAA MoveParameter")
        int distanceToClimb = 100;
    UPROPERTY(EditAnywhere, Category = "AAA MoveParameter")
        int distanceToHead = 50;
    UPROPERTY(EditAnywhere, Category = "AAA MoveParameter")
        int distanceToFloor = 50;


    UPROPERTY(EditAnywhere, Category = "AAA AnimationParameter")
        TMap<EMemberState, UAnimSequence*> animDic;


    //public variable
    //UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
    //	bool bCollideWithWall = false;
    //UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
    //	bool bIsClimbing = false;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        bool bIsPersonOnHead = true;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        EMemberState currentState;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        EMemberState nextState = EMemberState::Run;

    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        USceneComponent* characterRootComp;

    //UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
    //	USceneComponent* sceneComp;

    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        USkeletalMeshComponent* appearanceComp;

    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        UCapsuleComponent * baseCollision;

    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        UCapsuleComponent * collisionComp;

    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        ASpawnActor* spawnActorObj;

    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        int capsuleRadius = 30; //default 25 45
    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        int capsuleHeight = 80; //default 92 100 60


        // Sets default values for this character's properties
    AMemberCharacter();


    //	void EnterFallingState();

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

    //	FVector forceDirection;

    void SwitchState();
    void StateMachine(float deltaTime);

    bool IsEnteringClimbState();
    bool IsEnteringAttackState();
    bool IsEnteringDieState();

    bool CheckingPersonOnHead();

    void AddRunAndFromSideToCenterForce();

    float climbUpTimeCal = 0;

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

};

//================================MemberCharacter.cpp======================================
#include "MemberCharacter.h"
#include "SpawnActor.h"

// Sets default values
AMemberCharacter::AMemberCharacter()
{
    // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    //	sceneComp = CreateDefaultSubobject<USceneComponent>(TEXT("sceneComp"));

        //rootComp = CreateDefaultSubobject<USphereComponent>(TEXT("rootComp"));
        //rootComp->SetCollisionObjectType(ECC_GameTraceChannel2);
        //rootComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
        //rootComp->SetCollisionResponseToAllChannels(ECR_Ignore);
        //rootComp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
        //rootComp->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
        //rootComp->SetCollisionResponseToChannel(ECC_GameTraceChannel2, ECR_Block);
        //rootComp->SetSimulatePhysics(true);
        //rootComp->GetBodyInstance()->bLockXRotation = true;
        //rootComp->GetBodyInstance()->bLockYRotation = true;
        //rootComp->SetEnableGravity(true);
        //rootComp->SetMassOverrideInKg(NAME_None, 50);
        //RootComponent = rootComp;

    baseCollision = CreateDefaultSubobject<UCapsuleComponent>(TEXT("BaseCollision"));
    baseCollision->SetCollisionObjectType(ECC_GameTraceChannel1);
    baseCollision->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    baseCollision->SetCollisionResponseToAllChannels(ECR_Ignore);
    baseCollision->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
    baseCollision->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
    baseCollision->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Block);

    baseCollision->SetCapsuleHalfHeight(capsuleHeight);
    baseCollision->SetCapsuleRadius(capsuleRadius);

    baseCollision->SetSimulatePhysics(true);
    baseCollision->GetBodyInstance()->bLockXRotation = true;
    baseCollision->GetBodyInstance()->bLockYRotation = true;
    baseCollision->GetBodyInstance()->bLockZRotation = true;
    baseCollision->SetEnableGravity(true);
    baseCollision->SetMassOverrideInKg(NAME_None, 50);
    //baseCollision->SetWorldScale3D(FVector(1, 1.2, 2));
    //baseCollision->SetRelativeLocation(FVector(1, 1,120));
//	baseCollision->SetupAttachment(RootComponent);
    RootComponent = baseCollision;
    collisionComp = baseCollision;

    characterRootComp = CreateDefaultSubobject<USceneComponent>(TEXT("characterRootComp"));
    characterRootComp->SetupAttachment(RootComponent);

    appearanceComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("ApearanceMesh"));
    appearanceComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    appearanceComp->SetCollisionObjectType(ECC_WorldDynamic);
    appearanceComp->SetCollisionResponseToAllChannels(ECR_Ignore);
    appearanceComp->SetVisibility(true);
    appearanceComp->SetupAttachment(characterRootComp);

    appearanceComp->SetAnimationMode(EAnimationMode::AnimationSingleNode);


    //debug mode begin
    appearanceComp->SetRelativeLocation(FVector(0, 0, -70)); //-90 -50
    appearanceComp->SetRelativeRotation(FRotator(0, -90, 0));
    ConstructorHelpers::FObjectFinder<USkeletalMesh> skeletonRef(TEXT("/Game/JetpackAnimSet/Characters/UE4_Mannequin/Mesh/SK_Mannequin.SK_Mannequin"));
    appearanceComp->SetSkeletalMesh(skeletonRef.Object);
    ConstructorHelpers::FObjectFinder<UAnimSequence> runAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Heavy/RocketLauncher/H_RocketLauncher_Run_F.H_RocketLauncher_Run_F"));
    ConstructorHelpers::FObjectFinder<UAnimSequence> jumpAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Heavy/1HandHold/H_1HandHold_Fly_D.H_1HandHold_Fly_D"));
    ConstructorHelpers::FObjectFinder<UAnimSequence> attackAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Melee/Claw/U_Claw_Fire.U_Claw_Fire"));
    ConstructorHelpers::FObjectFinder<UAnimSequence> climbAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Death/U_Generic_Death_1.U_Generic_Death_1"));
    ConstructorHelpers::FObjectFinder<UAnimSequence> dieAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Death/U_Generic_Death_2.U_Generic_Death_2"));
    ConstructorHelpers::FObjectFinder<UAnimSequence> fallingAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Death/U_Generic_Death_3.U_Generic_Death_3"));
    //debug mode end


    animDic.Add(EMemberState::Run, runAnimRef.Object);
    animDic.Add(EMemberState::Jump, jumpAnimRef.Object);
    animDic.Add(EMemberState::Attack, attackAnimRef.Object);
    animDic.Add(EMemberState::Climb, climbAnimRef.Object);
    animDic.Add(EMemberState::Die, dieAnimRef.Object);
    animDic.Add(EMemberState::Falling, fallingAnimRef.Object);


}

// Called when the game starts or when spawned
void AMemberCharacter::BeginPlay()
{
    Super::BeginPlay();
    //if (targetMesh.Num() != 0) {
    //	appearanceComp->SetStaticMesh(targetMesh[FMath::RandRange(0,targetMesh.Num()-1)]);
    //}
    //forceDirection.X = 1;
    //forceDirection.Y = 0;
    //forceDirection.Z = 0;

}

void AMemberCharacter::SwitchState()
{
    if (nextState != currentState) {
        currentState = nextState;
        if (currentState != EMemberState::Die) {
            appearanceComp->PlayAnimation(animDic[currentState], true);
        }
        else {
            appearanceComp->PlayAnimation(animDic[EMemberState::Die], false);
        }
        //	appearanceComp->SetAnimation(animDic[currentState]);
    }
}

void AMemberCharacter::StateMachine(float deltaTime)
{

    if (currentState == EMemberState::Run) {

        /*	if (this->GetVelocity().Size() < maxRunSpeed) {
                FVector climbForceVector = spawnActorObj->targetPointActor->GetActorLocation() - this->GetActorLocation();
                climbForceVector.Z = 0;
                collisionComp->AddForce(runForce*climbForceVector.GetSignVector());
            }*/

        if (IsEnteringAttackState()) {
            collisionComp->ComponentVelocity = FVector(0, 0, 0);
            nextState = EMemberState::Attack;
        }
        else {


        }
        if (IsEnteringClimbState()) {
            if (bCanBeJump && FMath::RandRange(0.0f, 1.0f) <= jumpProbability) {
                nextState = EMemberState::Jump;
            }
            else {
                nextState = EMemberState::Climb;
            }
        }

    }
    if (currentState == EMemberState::Climb)
    {

        if (FMath::Abs(FVector::DotProduct(this->GetActorUpVector(), this->GetVelocity())) < maxClimbSpeed) {
            collisionComp->AddForce(climbUpForce*this->GetActorUpVector());
        }
        if (IsEnteringAttackState()) {
            nextState = EMemberState::Attack;
        }
    }
    if (currentState == EMemberState::Jump)
    {
        if (climbUpTimeCal > jumpInterval) {
            collisionComp->AddImpulse(jumpImpulse*this->GetActorUpVector());
            climbUpTimeCal = 0;
        }
        else {
            climbUpTimeCal += deltaTime;
        }
        if (IsEnteringAttackState()) {
            nextState = EMemberState::Attack;
        }
    }

    if (currentState == EMemberState::Climb || currentState == EMemberState::Jump) {
        if (this->GetVelocity().Size() < clampSpeedToAttack) {
            nextState = EMemberState::Attack;
        }
    }

    if (currentState == EMemberState::Run || currentState == EMemberState::Climb || currentState == EMemberState::Jump || currentState == EMemberState::Attack) {
        AddRunAndFromSideToCenterForce();
    }
    if (bCanBeDropDeath && (currentState == EMemberState::Climb || currentState == EMemberState::Jump) &&
        this->GetActorLocation().Z > this->spawnActorObj->deathHeightActor->GetActorLocation().Z)
    {
        //UE_LOG(LogTemp, Warning, TEXT("Check to death!"));
        bool checkOnHeadThing = CheckingPersonOnHead();
        if (bIsPersonOnHead != checkOnHeadThing) {
            bIsPersonOnHead = checkOnHeadThing;
        }

        if (bIsPersonOnHead == false) {
            if (FMath::RandRange(0.0f, 1.0f) <= dropDeathProbability) {
                nextState = EMemberState::Falling;
            }
        }
    }

    //if (spawnActorObj->bCanBeDeath && (currentState == EMemberState::Climb || currentState == EMemberState::Jump) &&
    //	this->GetActorLocation().Z > spawnActorObj->deathHeight->GetActorLocation().Z)
    //{
    //	//UE_LOG(LogTemp, Warning, TEXT("Check to death!"));
    //	bool checkOnHeadThing = CheckingPersonOnHead();
    //	if (bIsPersonOnHead != checkOnHeadThing) {
    //		bIsPersonOnHead = checkOnHeadThing;
    //	}
    //}

    if (currentState == EMemberState::Falling) {
        
        if (this->GetVelocity().Size() < droppingSpeed) {
            FVector tempCenterForceVector = spawnActorObj->targetPointActor->GetActorLocation() - this->GetActorLocation();
            FVector multiplyVector = FVector::CrossProduct(tempCenterForceVector, this->GetActorForwardVector()).GetSignVector();
//			collisionComp->AddForce(spawnActorObj->deathImpulse*this->GetActorRightVector()*FVector::DotProduct(FVector(0, 0, 1), multiplyVector));
//			collisionComp->AddForce(spawnActorObj->deathBackImpulse* this->GetActorForwardVector()*-1);
            collisionComp->AddForce(droppingLeftRightForce*this->GetActorRightVector()*FVector::DotProduct(FVector(0, 0, 1), multiplyVector));
            collisionComp->AddForce(droppingBackForce* this->GetActorForwardVector()*-1);


        }
        if (IsEnteringDieState()) {
            nextState = EMemberState::Die;
        }
    }
    if (currentState == EMemberState::Die) {
        PrimaryActorTick.SetTickFunctionEnable(false);
    }
}

void AMemberCharacter::AddRunAndFromSideToCenterForce()
{
    if (FMath::Abs(FVector::DotProduct(this->GetActorForwardVector(), this->GetVelocity())) < maxRunSpeed) {
        collisionComp->AddForce(runForce*this->GetActorForwardVector());
    }
    FVector faceVector = spawnActorObj->targetPointActor->GetActorLocation() - this->GetActorLocation();
    if(FMath::Abs(this->GetActorLocation().Y-spawnActorObj->GetActorLocation().Y)>spawnActorObj->needToChangeHorizontalDistance || faceVector.X<this->spawnActorObj->changeHorizontalWithinDistance)
    if (FMath::Abs(FVector::DotProduct(this->GetActorRightVector(), this->GetVelocity())) < maxLeftRightSpeed) {
        FVector multiplyVector = FVector::CrossProduct(faceVector, this->GetActorForwardVector()).GetSignVector();
        collisionComp->AddForce(centerLeftRightForce*this->GetActorRightVector()*FVector::DotProduct(FVector(0, 0, 1), multiplyVector)*-1);
    }

    if (currentState == EMemberState::Run) {
        faceVector.Z = this->GetActorLocation().Z;
        faceVector = this->GetActorLocation() + faceVector;
        characterRootComp->SetWorldRotation(UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), faceVector));
    }
    else {
        characterRootComp->SetWorldRotation(UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), spawnActorObj->faceTargetActor->GetActorLocation()));
    }


    /*if (FVector::DotProduct(this->GetActorRightVector(), climbForceVector)>0) {
        collisionComp->AddForce(centerLeftRightForce*this->GetActorRightVector());
    }
    else {
        collisionComp->AddForce(centerLeftRightForce*this->GetActorRightVector()*-1);
    }*/
}

bool AMemberCharacter::IsEnteringClimbState()
{
    FHitResult outSingleHit;
    FVector StartPoint = this->GetActorLocation();
    StartPoint.X += capsuleRadius + 1;
    FVector EndPoint = StartPoint + this->GetActorForwardVector()*distanceToClimb;
    FCollisionObjectQueryParams queryObjectList;
    queryObjectList.AddObjectTypesToQuery(ECC_GameTraceChannel1);
    //		DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.01, 0, 1);
    if (GetWorld()->LineTraceSingleByObjectType(outSingleHit, StartPoint, EndPoint, queryObjectList)) {
        AMemberCharacter* temp = Cast<AMemberCharacter>(outSingleHit.GetActor());
        if (temp->currentState == EMemberState::Attack) {
            return true;
        }
    }
    return false;

}

bool AMemberCharacter::IsEnteringAttackState()
{
    TArray<FHitResult> outHit;
    FVector StartPoint = this->GetActorLocation();
    StartPoint.X += capsuleRadius + 1;
    FVector EndPoint = StartPoint + this->GetActorForwardVector()*distanceToWall;
    FCollisionObjectQueryParams queryObjectList;
    queryObjectList.AddObjectTypesToQuery(ECC_WorldStatic);
    //	DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.01, 0, 1);
    if (GetWorld()->LineTraceMultiByObjectType(outHit, StartPoint, EndPoint, queryObjectList)) {
        for (int i = 0; i < outHit.Num(); i++) {
            /*if (spawnActorObj->IsCollidingWithWall(outHit[i].GetActor()))
            {
                return true;
            }*/
            if (spawnActorObj->climbWall.Find(outHit[i].GetActor()) != -1) {
                return true;
            }
        }
    }
    return false;
}

bool AMemberCharacter::IsEnteringDieState()
{
    TArray<FHitResult> outHit;
    FVector StartPoint = this->GetActorLocation();
    StartPoint.Z -= capsuleHeight - 1;
    FVector EndPoint = StartPoint + this->GetActorUpVector()*-1 * distanceToFloor;
    FCollisionObjectQueryParams queryObjectList;
    queryObjectList.AddObjectTypesToQuery(ECC_WorldStatic);
    //	DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.01, 0, 1);
    if (GetWorld()->LineTraceMultiByObjectType(outHit, StartPoint, EndPoint, queryObjectList)) {
        for (int i = 0; i < outHit.Num(); i++) {
            if (spawnActorObj->floorActor.Find(outHit[i].GetActor()) != -1)
            {
                return true;
            }
        }
    }
    return false;
}

bool AMemberCharacter::CheckingPersonOnHead()
{
    FHitResult outSingleHit;
    FVector StartPoint = this->GetActorLocation();
    StartPoint.Z += capsuleHeight + 1;
    FVector EndPoint = StartPoint + this->GetActorUpVector()*distanceToHead;
    FCollisionObjectQueryParams queryObjectList;
    queryObjectList.AddObjectTypesToQuery(ECC_GameTraceChannel1);
    //	DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.01, 0, 1);
    if (GetWorld()->LineTraceSingleByObjectType(outSingleHit, StartPoint, EndPoint, queryObjectList)) {
        //	UE_LOG(LogTemp, Warning, TEXT("Person on Head!"));
        return true;
    }
    return false;
}



//void AMemberCharacter::EnterFallingState()
//{
//	nextState = EMemberState::Die;
//	//PrimaryActorTick.SetTickFunctionEnable(false);
//	FVector climbForceVector = spawnActorObj->targetPointActor->GetActorLocation() - this->GetActorLocation();
//	FVector multiplyVector = FVector::CrossProduct(climbForceVector, this->GetActorForwardVector()).GetSignVector();
//	collisionComp->AddImpulse(spawnActorObj->deathImpulse*this->GetActorRightVector()*FVector::DotProduct(FVector(0, 0, 1), multiplyVector));
//	collisionComp->AddImpulse(spawnActorObj->deathBackImpulse* this->GetActorForwardVector()*-1);
//	//baseCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
//	//baseCollision->SetEnableGravity(false);
//
//}

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

    SwitchState();
    StateMachine(DeltaTime);
    //	UE_LOG(LogTemp, Warning, TEXT("Character Position is %s"), *(spawnActorObj->generatorCenter).ToCompactString());

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514

# 版本2

//================================Zombie.h======================================
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ZombieState.h"
#include "Animation/AnimSequence.h"
#include "UObject/ConstructorHelpers.h"
#include "Components/SphereComponent.h"
#include "Kismet/KismetMathLibrary.h"
#include "Zombie.generated.h"

class ASpawnActor;

UCLASS()
class JETPACKANIMATIONSET_API AZombie : public AActor
{
    GENERATED_BODY()
    
public:	
    // Sets default values for this actor's properties
    AZombie();
    UPROPERTY(EditAnywhere, Category = "AAA AnimationParameter")
        TMap<EZombieState, UAnimSequence*> animDic;

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int maxRunSpeed = 600;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int forwardForce = 1000;  //1000 fail:2000
    UPROPERTY(VisibleAnywhere, Category = "AAA SpawnParameter")
        int sphereRadius = 50;
    UPROPERTY(VisibleAnywhere, Category = "AAA SpawnParameter")
        int forwardTraceDistance = 60;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int maxJumpSpeed = 400;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int jumpForce = 1500;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int centerForce = 3000; //3000 fail:2000
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int orientationSpeedMax = 30;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int rotateSpeed = 2.0;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        bool bCanBeDeath = true;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bCanBeDeath"))
        float dropDeathProbability = 0.01;

    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        EZombieState currentState;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        EZombieState nextState = EZombieState::Run;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        bool bBehindPersonCanClimb = false;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        bool bCurrentPersonClimbing = false;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        FString beforeZombie;


    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        ASpawnActor* spawnActorObj;
    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        USkeletalMeshComponent* appearanceComp;
    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        USphereComponent * baseCollision;
    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        USceneComponent* characterRootComp;
    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        USphereComponent* collisionComp;



    void goToDie();



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

    void SwitchState();
    void StateMachine(float deltaTime);

    void checkIsWall();
    //void addCustomForce(FVector direction, int forceNum,int maxSpeed);
    void clampForce(int maxSpeed);



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

};
//================================Zombie.cpp======================================

#include "Zombie.h"
#include "SpawnActor.h"

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

    baseCollision = CreateDefaultSubobject<USphereComponent>(TEXT("BaseCollision"));
    baseCollision->SetCollisionObjectType(ECC_GameTraceChannel1);
    baseCollision->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    baseCollision->SetCollisionResponseToAllChannels(ECR_Ignore);
    baseCollision->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
    baseCollision->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
    baseCollision->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Block);

    baseCollision->SetSimulatePhysics(true);
    baseCollision->GetBodyInstance()->bLockXRotation = true;
    baseCollision->GetBodyInstance()->bLockYRotation = true;
    baseCollision->GetBodyInstance()->bLockZRotation = true;
    baseCollision->SetEnableGravity(true);
    baseCollision->SetMassOverrideInKg(NAME_None, 1);
    baseCollision->SetSphereRadius(sphereRadius);

    RootComponent = baseCollision;
    collisionComp = baseCollision;

    characterRootComp = CreateDefaultSubobject<USceneComponent>(TEXT("characterRootComp"));
    characterRootComp->SetupAttachment(RootComponent);

    appearanceComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("ApearanceMesh"));
    appearanceComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    appearanceComp->SetCollisionObjectType(ECC_PhysicsBody);
    //appearanceComp->SetEnableGravity(true);
    //appearanceComp->SetMassOverrideInKg(NAME_None, 1);
    appearanceComp->SetCollisionResponseToAllChannels(ECR_Ignore);
    appearanceComp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
    appearanceComp->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
    appearanceComp->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Ignore);

    appearanceComp->SetVisibility(true);
    appearanceComp->SetupAttachment(characterRootComp);

    appearanceComp->SetAnimationMode(EAnimationMode::AnimationSingleNode);


    //debug mode begin
    appearanceComp->SetRelativeLocation(FVector(0, 0, -sphereRadius)); //-90 -50
    appearanceComp->SetRelativeRotation(FRotator(0, -90, 0));
    ConstructorHelpers::FObjectFinder<USkeletalMesh> skeletonRef(TEXT("/Game/JetpackAnimSet/Characters/UE4_Mannequin/Mesh/SK_Mannequin.SK_Mannequin"));
    appearanceComp->SetSkeletalMesh(skeletonRef.Object);

    ConstructorHelpers::FObjectFinder<UAnimSequence> runAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Heavy/RocketLauncher/H_RocketLauncher_Run_F.H_RocketLauncher_Run_F"));
    ConstructorHelpers::FObjectFinder<UAnimSequence> jumpAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Heavy/1HandHold/H_1HandHold_Fly_D.H_1HandHold_Fly_D"));
    ConstructorHelpers::FObjectFinder<UAnimSequence> attackAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Melee/Claw/U_Claw_Fire.U_Claw_Fire"));
    ConstructorHelpers::FObjectFinder<UAnimSequence> climbAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Death/U_Generic_Death_1.U_Generic_Death_1"));
    ConstructorHelpers::FObjectFinder<UAnimSequence> dieAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Death/U_Generic_Death_2.U_Generic_Death_2"));
    ConstructorHelpers::FObjectFinder<UAnimSequence> fallingAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Death/U_Generic_Death_3.U_Generic_Death_3"));

    animDic.Add(EZombieState::Run, runAnimRef.Object);
    animDic.Add(EZombieState::Jump, jumpAnimRef.Object);
    animDic.Add(EZombieState::Attack, attackAnimRef.Object);
    animDic.Add(EZombieState::Climb, climbAnimRef.Object);
    animDic.Add(EZombieState::Die, dieAnimRef.Object);
    animDic.Add(EZombieState::Falling, fallingAnimRef.Object);

}

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

void AZombie::SwitchState()
{
    if (nextState != currentState) {
        currentState = nextState;
        if (currentState != EZombieState::Die) {
            appearanceComp->PlayAnimation(animDic[currentState], true);
        }
        else {
            appearanceComp->PlayAnimation(animDic[EZombieState::Die], false);
        }
        //	appearanceComp->SetAnimation(animDic[currentState]);
    }
}

void AZombie::StateMachine(float deltaTime)
{
    //collisionComp->AddForce(forwardForce*this->GetActorForwardVector());

    if (currentState != EZombieState::Die) {
        checkIsWall();
    }

    if (currentState == EZombieState::Run) {
        collisionComp->AddForce(forwardForce*GetActorForwardVector());
        clampForce(maxRunSpeed);
    }
    if (currentState == EZombieState::Climb) {
        if (collisionComp->GetPhysicsLinearVelocity().Size() <= maxJumpSpeed) {
            collisionComp->AddForce(forwardForce*GetActorForwardVector());
            collisionComp->AddForce(jumpForce*GetActorUpVector());
            FVector temp = GetActorLocation() - spawnActorObj->GetActorLocation();
            FVector projectVector = temp.ProjectOnTo(GetActorRightVector());
            if (projectVector.Normalize()) {
                collisionComp->AddForce(FVector::DotProduct(projectVector, GetActorRightVector())*-1 * centerForce*GetActorRightVector());
            }
        }
        //clampForce(maxJumpSpeed);
    }
//	if (collisionComp->GetPhysicsLinearVelocity().Size() < orientationSpeedMax)
    //if(currentState == EZombieState::Run)
    //{
    //	//FVector faceVector = spawnActorObj->facePointActor->GetActorLocation() - this->GetActorLocation();
    //	//faceVector.Z = this->GetActorLocation().Z;
    //	//faceVector = this->GetActorLocation() + faceVector;
    //	//characterRootComp->SetWorldRotation(UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), faceVector));
    //	FRotator fr1 = UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), this->GetActorLocation() + collisionComp->GetPhysicsLinearVelocity());
    //	characterRootComp->SetWorldRotation(FMath::RInterpTo(characterRootComp->GetComponentRotation(),fr1, deltaTime, rotateSpeed));

    //} else
    if(currentState != EZombieState::Die)
    {
        FRotator fr1 = UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), spawnActorObj->facePointActor->GetActorLocation());
        characterRootComp->SetWorldRotation(FMath::RInterpTo(characterRootComp->GetComponentRotation(), fr1, deltaTime, rotateSpeed));
    }
    /*if (collisionComp->GetPhysicsLinearVelocity().Size() < orientationSpeedMax) {
            FVector temp1 = this->GetActorLocation() - spawnActorObj->GetActorLocation();
        if (temp1.Normalize()) {
        //	characterRootComp->SetWorldRotation(FMath::RInterpTo(characterRootComp->GetComponentRotation(), UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), this->GetActorLocation() + temp1), deltaTime, rotateSpeed));
        }
    
    }
    else {
        characterRootComp->SetWorldRotation(UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(),this->GetActorLocation()+collisionComp->GetPhysicsLinearVelocity()));
    }*/

}

void AZombie::checkIsWall()
{
    FHitResult outSingleHit;
    FVector StartPoint = GetActorLocation();
    StartPoint.X += (sphereRadius + 1);
    FVector EndPoint = StartPoint + GetActorForwardVector()*forwardTraceDistance;
    FCollisionObjectQueryParams queryObjectList;
    queryObjectList.AddObjectTypesToQuery(ECC_GameTraceChannel1);
//	queryObjectList.AddObjectTypesToQuery(ECC_WorldStatic);
    //	DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.01, 0, 1);
    FCollisionQueryParams collisionParams;
    collisionParams.bReturnPhysicalMaterial = false;
    collisionParams.AddIgnoredActor(this);
    if (GetWorld()->SweepSingleByObjectType(outSingleHit, StartPoint, EndPoint, FQuat(),queryObjectList,FCollisionShape::MakeSphere(sphereRadius),collisionParams)) {
        //	UE_LOG(LogTemp, Warning, TEXT("Person on Head!"));
        AZombie* temp = Cast<AZombie>(outSingleHit.GetActor());
        if (temp != nullptr) {
            beforeZombie = temp->GetActorLabel();
            if (temp->bBehindPersonCanClimb == true) {
                bBehindPersonCanClimb = true;
                if (temp->bCurrentPersonClimbing == true) {
                    bCurrentPersonClimbing = false;
                    if (nextState != EZombieState::Run) {
                        nextState = EZombieState::Run;
                    }
                }
                else {
                    bCurrentPersonClimbing = true;
                    nextState = EZombieState::Climb;
                }
            }
        }
        else {
            bBehindPersonCanClimb = true;
            bCurrentPersonClimbing = false;
            if (nextState != EZombieState::Run) {
                nextState = EZombieState::Run;
            }
        }
    }
}

void AZombie::clampForce(int maxSpeed)
{
    int tempSpeed=collisionComp->GetPhysicsLinearVelocity().Size();
    if (tempSpeed >= maxSpeed) {
        tempSpeed = maxSpeed;
    }
    FVector tempDirection= collisionComp->GetPhysicsLinearVelocity();
    if (tempDirection.Normalize()) {
        collisionComp->SetPhysicsLinearVelocity(tempDirection*tempSpeed);
    }
    //if (collisionComp->GetPhysicsLinearVelocity().Size() < maxSpeed) {
    //	collisionComp->AddForce(direction*forceNum);
    //}
    //else {
    //	collisionComp->SetPhysicsLinearVelocity(direction*maxSpeed);
    //}
}

void AZombie::goToDie()
{
    if (bCanBeDeath) {
        if (FMath::RandRange(0.0f, 1.0f) <= dropDeathProbability) {
            baseCollision->SetCollisionObjectType(ECC_PhysicsBody);
            //baseCollision->SetSimulatePhysics(false);
            //baseCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
            //appearanceComp->SetSimulatePhysics(true);
            nextState = EZombieState::Die;
        }
    }
}

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

    SwitchState();
    StateMachine(DeltaTime);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321

# 版本3

该版本整合了版本1和版本2:

//=======================================Zombie.h===============================================
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ZombieState.h"
#include "Animation/AnimSequence.h"
#include "UObject/ConstructorHelpers.h"
#include "Components/SphereComponent.h"
#include "DrawDebugHelpers.h"
#include "Kismet/KismetMathLibrary.h"
#include "Zombie.generated.h"

class ASpawnActor;

UCLASS()
class MYPROJECT01_API AZombie : public AActor
{
    GENERATED_BODY()
    
public:	
    // Sets default values for this actor's properties
    AZombie();
    UPROPERTY(EditAnywhere, Category = "AAA AnimationParameter")
        TMap<EZombieState, UAnimSequence*> animDic;

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int maxRunSpeed = 400;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int forwardForce = 2000;  //1000 fail:2000
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int sphereRadius = 30;
    UPROPERTY(VisibleAnywhere, Category = "AAA SpawnParameter")
        int forwardTraceDistance = 200;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int maxJumpSpeed = 200;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int jumpForce = 1000;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int climbForce = 1000;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int centerForce = 2000; //3000 fail:2000
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int orientationSpeedMax = 30;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int rotateSpeed = 2.0;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        bool bCanBeDeath = true;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter", meta = (EditCondition = "bCanBeDeath"))
        float dropDeathProbability = 0.1;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        float changeIntervalTime = 1;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        UPhysicsAsset* physicsAsset;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int dieforwardForce = 2000;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int dieUpForce = 10000;
    //My version begin version 2
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        bool bUseVersion2 = true;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        int maxRunSpeedV2 = 400;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        int maxLeftRightSpeedV2 = 300;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        int centerForceV2 = 1500;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        bool bCanBeJump = true;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        float jumpProbability = 0.1;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        float jumpIntervalV2 = 1;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        int jumpImpulseV2 = 300;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        int maxJumpSpeedV2 = 400;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        int maxClimbSpeedV2 = 400;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        int jumpClimbToAttackSpeed = 50;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        float jumpClimbToAttackIntervalTime = 1;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        float jumpClimbToAttackCalTime = 0;
    float jumpTimeCal = 0;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        int climbUpForceV2 = 1500;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter2")
        int forwardTraceDistanceV2 = 500;


    //My version end

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "BBB SelfState")
        EZombieState currentState;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        EZombieState nextState = EZombieState::Run;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        bool bBehindPersonCanClimb = false;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        bool bCurrentPersonClimbing = false;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        FString beforeZombie;
    UPROPERTY(VisibleAnywhere, Category = "BBB SelfState")
        float changeTime=0;


    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        ASpawnActor* spawnActorObj;
    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        USkeletalMeshComponent* appearanceComp;
    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        USphereComponent * baseCollision;
    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        USceneComponent* characterRootComp;
    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        USphereComponent* collisionComp;



    void goToDie();



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

    void SwitchState(float deltaTime);
    void StateMachine(float deltaTime);


    void checkIsWall();
    //void addCustomForce(FVector direction, int forceNum,int maxSpeed);
    void clampForce(int maxSpeed);

    FVector forwardVector;
    FVector rightVector;
    FVector upVector;
    bool bInit = false;




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

    void dieUpDown();

    void AddForwardRunForce();
    void AddSideToCenterForce(AActor* target);

    void rotateCharacterAppearance(AActor* target,float deltaTime);

    //My version begin


    bool IsEnteringClimbState();
    bool IsEnteringAttackState();
//	bool IsEnteringDieState();
//	bool CheckingPersonOnHead();
//	void AddRunAndFromSideToCenterForce();
    bool IsEnteringAttackState2();

    void StateMachineV2(float deltaTime);

    void StateMachineV3(float deltaTime);
    bool IsCollideWithWall();
    //My version end
};
//=======================================Zombie.cpp===============================================
#include "Zombie.h"
#include "SpawnActor.h"

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

    baseCollision = CreateDefaultSubobject<USphereComponent>(TEXT("BaseCollision"));
    baseCollision->SetCollisionObjectType(ECC_GameTraceChannel1);
    baseCollision->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    baseCollision->SetCollisionResponseToAllChannels(ECR_Ignore);
    baseCollision->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
    baseCollision->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
    baseCollision->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Block);

    baseCollision->SetSimulatePhysics(true);
    baseCollision->GetBodyInstance()->bLockXRotation = true;
    baseCollision->GetBodyInstance()->bLockYRotation = true;
    baseCollision->GetBodyInstance()->bLockZRotation = true;
    baseCollision->SetEnableGravity(true);
    baseCollision->SetMassOverrideInKg(NAME_None, 1);
    baseCollision->SetSphereRadius(sphereRadius);

    RootComponent = baseCollision;
    collisionComp = baseCollision;

    characterRootComp = CreateDefaultSubobject<USceneComponent>(TEXT("characterRootComp"));
    characterRootComp->SetupAttachment(RootComponent);

    appearanceComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("ApearanceMesh"));
    //appearanceComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    appearanceComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    appearanceComp->SetCollisionObjectType(ECC_PhysicsBody);
    //appearanceComp->SetEnableGravity(true);
    //appearanceComp->SetMassOverrideInKg(NAME_None, 1);
    appearanceComp->SetCollisionResponseToAllChannels(ECR_Ignore);
    appearanceComp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
    appearanceComp->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
    appearanceComp->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Ignore);
    appearanceComp->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Block);


    appearanceComp->SetVisibility(true);
    appearanceComp->SetupAttachment(characterRootComp);

    //appearanceComp->SetAnimationMode(EAnimationMode::AnimationSingleNode);
    appearanceComp->SetAnimationMode(EAnimationMode::AnimationBlueprint);
    appearanceComp->SetGenerateOverlapEvents(true);


    //debug mode begin
    appearanceComp->SetRelativeLocation(FVector(0, 0, -sphereRadius)); //-90 -50
//	appearanceComp->SetRelativeRotation(FRotator(0, -90, 0));
//	ConstructorHelpers::FObjectFinder<USkeletalMesh> skeletonRef(TEXT("/Game/JetpackAnimSet/Characters/UE4_Mannequin/Mesh/SK_Mannequin.SK_Mannequin"));
//	appearanceComp->SetSkeletalMesh(skeletonRef.Object);

    //ConstructorHelpers::FObjectFinder<UAnimSequence> runAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Heavy/RocketLauncher/H_RocketLauncher_Run_F.H_RocketLauncher_Run_F"));
    //ConstructorHelpers::FObjectFinder<UAnimSequence> jumpAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Heavy/1HandHold/H_1HandHold_Fly_D.H_1HandHold_Fly_D"));
    //ConstructorHelpers::FObjectFinder<UAnimSequence> attackAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Melee/Claw/U_Claw_Fire.U_Claw_Fire"));
    //ConstructorHelpers::FObjectFinder<UAnimSequence> climbAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Death/U_Generic_Death_1.U_Generic_Death_1"));
    //ConstructorHelpers::FObjectFinder<UAnimSequence> dieAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Death/U_Generic_Death_2.U_Generic_Death_2"));
    //ConstructorHelpers::FObjectFinder<UAnimSequence> fallingAnimRef(TEXT("/Game/JetpackAnimSet/Animations/Universal/Death/U_Generic_Death_3.U_Generic_Death_3"));

    //animDic.Add(EZombieState::Run, runAnimRef.Object);
    //animDic.Add(EZombieState::Jump, jumpAnimRef.Object);
    //animDic.Add(EZombieState::Attack, attackAnimRef.Object);
    //animDic.Add(EZombieState::Climb, climbAnimRef.Object);
    //animDic.Add(EZombieState::Die, dieAnimRef.Object);
    //animDic.Add(EZombieState::Falling, fallingAnimRef.Object);

    animDic.Add(EZombieState::Run,nullptr);
    animDic.Add(EZombieState::Jump, nullptr);
    animDic.Add(EZombieState::Attack, nullptr);
    animDic.Add(EZombieState::Climb, nullptr);
    animDic.Add(EZombieState::Die, nullptr);
    animDic.Add(EZombieState::Falling, nullptr);

}

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

void AZombie::SwitchState(float deltaTime)
{
    if (nextState != currentState) {
            currentState = nextState;
    /*		if (currentState != EZombieState::Die) {
                appearanceComp->PlayAnimation(animDic[currentState], true);
            }
            else {
                appearanceComp->PlayAnimation(animDic[EZombieState::Die], false);
            }
*/
        //	appearanceComp->SetAnimation(animDic[currentState]);
    }
}

void AZombie::StateMachine(float deltaTime)
{
    //collisionComp->AddForce(forwardForce*this->GetActorForwardVector());

    if (currentState != EZombieState::Die) {
        checkIsWall();
    }

    if (currentState == EZombieState::Run) {
    /*	collisionComp->AddForce(forwardForce*forwardVector);
        clampForce(maxRunSpeed);*/
        AddForwardRunForce();
    }

    if(spawnActorObj->facePointActor->GetActorLocation().Z > GetActorLocation().Z)
    if (currentState == EZombieState::Climb) {
        AddForwardRunForce();
        AddSideToCenterForce(spawnActorObj);
        if (collisionComp->GetPhysicsLinearVelocity().ProjectOnTo(upVector).Size() <= maxJumpSpeed) {
            collisionComp->AddForce(climbForce*upVector);
        }
        //clampForce(maxJumpSpeed);
    }
//	if (collisionComp->GetPhysicsLinearVelocity().Size() < orientationSpeedMax)
    //if(currentState == EZombieState::Run)
    //{
    //	//FVector faceVector = spawnActorObj->facePointActor->GetActorLocation() - this->GetActorLocation();
    //	//faceVector.Z = this->GetActorLocation().Z;
    //	//faceVector = this->GetActorLocation() + faceVector;
    //	//characterRootComp->SetWorldRotation(UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), faceVector));
    //	FRotator fr1 = UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), this->GetActorLocation() + collisionComp->GetPhysicsLinearVelocity());
    //	characterRootComp->SetWorldRotation(FMath::RInterpTo(characterRootComp->GetComponentRotation(),fr1, deltaTime, rotateSpeed));

    //} else
    if(currentState != EZombieState::Die)
    {
        rotateCharacterAppearance(spawnActorObj->facePointActor,deltaTime);
    
    }
    /*if (collisionComp->GetPhysicsLinearVelocity().Size() < orientationSpeedMax) {
            FVector temp1 = this->GetActorLocation() - spawnActorObj->GetActorLocation();
        if (temp1.Normalize()) {
        //	characterRootComp->SetWorldRotation(FMath::RInterpTo(characterRootComp->GetComponentRotation(), UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), this->GetActorLocation() + temp1), deltaTime, rotateSpeed));
        }
    
    }
    else {
        characterRootComp->SetWorldRotation(UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(),this->GetActorLocation()+collisionComp->GetPhysicsLinearVelocity()));
    }*/

}



void AZombie::checkIsWall()
{
    FHitResult outSingleHit;
    FVector StartPoint = GetActorLocation();
    //StartPoint.X += (sphereRadius + 1);
    FVector EndPoint = StartPoint + forwardVector*forwardTraceDistance;
    FCollisionObjectQueryParams queryObjectList;
    queryObjectList.AddObjectTypesToQuery(ECC_GameTraceChannel1);
//	queryObjectList.AddObjectTypesToQuery(ECC_WorldStatic);
    //	DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.01, 0, 1);
    FCollisionQueryParams collisionParams;
    collisionParams.bReturnPhysicalMaterial = false;
    collisionParams.AddIgnoredActor(this);
    if (GetWorld()->SweepSingleByObjectType(outSingleHit, StartPoint, EndPoint, FQuat(),queryObjectList,FCollisionShape::MakeSphere(sphereRadius),collisionParams)) {
        AZombie* temp = Cast<AZombie>(outSingleHit.GetActor());
        if (temp != nullptr) {
            beforeZombie = temp->GetActorLabel();
            if (temp->bBehindPersonCanClimb == true) {
                bBehindPersonCanClimb = true;
                if (temp->bCurrentPersonClimbing == true) {
                    bCurrentPersonClimbing = false;
                    if (nextState != EZombieState::Run) {
                        nextState = EZombieState::Run;
                    }
                }
                else {
                    bCurrentPersonClimbing = true;
                    nextState = EZombieState::Climb;
                }
            }
        }
        else {
            bBehindPersonCanClimb = true;
            bCurrentPersonClimbing = false;
            //if (nextState != EZombieState::Run) {
            //	nextState = EZombieState::Run;
            //}
        }
    }
}

void AZombie::clampForce(int maxSpeed)
{
    int tempSpeed=collisionComp->GetPhysicsLinearVelocity().Size();
    if (tempSpeed >= maxSpeed) {
        tempSpeed = maxSpeed;
    }
    FVector tempDirection= collisionComp->GetPhysicsLinearVelocity();
    if (tempDirection.Normalize()) {
        collisionComp->SetPhysicsLinearVelocity(tempDirection*tempSpeed);
    }
    //if (collisionComp->GetPhysicsLinearVelocity().Size() < maxSpeed) {
    //	collisionComp->AddForce(direction*forceNum);
    //}
    //else {
    //	collisionComp->SetPhysicsLinearVelocity(direction*maxSpeed);
    //}
}

void AZombie::goToDie()
{
    if (bCanBeDeath) {
        if (FMath::RandRange(0.0f, 1.0f) <= dropDeathProbability) {
            //baseCollision->SetCollisionObjectType(ECC_PhysicsBody);
            baseCollision->SetSimulatePhysics(false);
            baseCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
            appearanceComp->SetSimulatePhysics(true);
            appearanceComp->SetPhysicsAsset(physicsAsset);
            appearanceComp->AddImpulse(forwardVector*dieforwardForce);
            nextState = EZombieState::Die;
        }
    }
}

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

    if (!bInit && spawnActorObj != nullptr) {
        bInit = true;
        forwardVector = spawnActorObj->GetActorForwardVector();
        rightVector = spawnActorObj->GetActorRightVector();
        upVector = GetActorUpVector();
        //UE_LOG(LogTemp, Warning, TEXT("spawn actor rotation is %s"), *(spawnActorObj->GetActorRotation()).ToCompactString());
        //this->SetActorRotation(spawnActorObj->GetActorRotation());
        //UE_LOG(LogTemp, Warning, TEXT("self rotation is %s"), *(this->GetActorRotation()).ToCompactString());

    }

    SwitchState(DeltaTime);
    if (bUseVersion2) {
        StateMachineV2(DeltaTime);
    }
    else {
        StateMachine(DeltaTime);
    }

    //StateMachineV3(DeltaTime);
}

void AZombie::dieUpDown()
{
    if (currentState == EZombieState::Die) {
        appearanceComp->AddImpulse(upVector * dieUpForce);
        //AddSideToCenterForce(spawnActorObj->facePointActor);
        //FVector temp = GetActorLocation() - spawnActorObj->facePointActor->GetActorLocation();
        //FVector projectVector = temp.ProjectOnTo(rightVector);
        //if (projectVector.Normalize()) {
        //	appearanceComp->AddForce(FVector::DotProduct(projectVector, rightVector) * -1 * centerForce * rightVector);
        //}
    }
}

void AZombie::AddForwardRunForce()
{
    if (collisionComp->GetPhysicsLinearVelocity().ProjectOnTo(forwardVector).Size() < maxRunSpeedV2) {
        collisionComp->AddForce(forwardForce * forwardVector);
    }
}

//target 需要在该物体的斜后方
void AZombie::AddSideToCenterForce(AActor* target)
{
    if (collisionComp->GetPhysicsLinearVelocity().ProjectOnTo(rightVector).Size() < maxLeftRightSpeedV2) {
        FVector temp = GetActorLocation() - target->GetActorLocation();
        FVector projectVector = temp.ProjectOnTo(rightVector);
        if (projectVector.Normalize()) {
            collisionComp->AddForce(FVector::DotProduct(projectVector, rightVector) * -1 * centerForceV2 * rightVector);
        }
    }
}

void AZombie::rotateCharacterAppearance(AActor* target,float deltaTime)
{
    FRotator fr1;
    if (currentState == EZombieState::Run || currentState == EZombieState::Attack) {
        FVector faceVector = target->GetActorLocation()-this->GetActorLocation();
        fr1 = UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), this->GetActorLocation()+FVector::VectorPlaneProject(faceVector,upVector));
    }
    else {
        fr1 = UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(),target->GetActorLocation());
    }
    characterRootComp->SetWorldRotation(FMath::RInterpTo(characterRootComp->GetComponentRotation(), fr1, deltaTime, rotateSpeed));
}

//My Version begin

bool AZombie::IsEnteringClimbState()
{
    FHitResult outSingleHit;
    //FVector StartPoint = collisionComp->GetComponentLocation() + forwardVector * (sphereRadius + 1);
    FVector StartPoint = this->GetActorLocation();
    //	StartPoint.X += (sphereRadius + 1);
    FVector EndPoint = StartPoint + forwardVector * forwardTraceDistanceV2;
    FCollisionObjectQueryParams queryObjectList;
    FCollisionQueryParams collisionParams;
    collisionParams.bReturnPhysicalMaterial = false;
    collisionParams.AddIgnoredActor(this);
    queryObjectList.AddObjectTypesToQuery(ECC_GameTraceChannel1);
    //		DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.01, 0, 1);
    if (GetWorld()->LineTraceSingleByObjectType(outSingleHit, StartPoint, EndPoint, queryObjectList,collisionParams)) {
        AZombie* temp = Cast<AZombie>(outSingleHit.GetActor());
        if (temp != nullptr && temp->currentState == EZombieState::Attack) {
            return true;
        }
    }
    return false;
}

bool AZombie::IsEnteringAttackState2()
{
    TArray<FHitResult> outHit;
//	FVector StartPoint = collisionComp->GetComponentLocation()+forwardVector*(sphereRadius+1);
    FVector StartPoint = this->GetActorLocation();
//	StartPoint.X += (sphereRadius + 1);
    FVector EndPoint = StartPoint + forwardVector * forwardTraceDistanceV2;
    FCollisionObjectQueryParams queryObjectList;
    //queryObjectList.bReturnPhysicalMaterial = false;
    //queryObjectList.AddIgnoredActor(this);
    queryObjectList.AddObjectTypesToQuery(ECC_GameTraceChannel1);
    FCollisionQueryParams collisionParams;
    collisionParams.bReturnPhysicalMaterial = false;
    collisionParams.AddIgnoredActor(this);
    //DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.05, 0, 3);
    if (GetWorld()->LineTraceMultiByObjectType(outHit, StartPoint, EndPoint, queryObjectList,collisionParams)) {
        if(outHit.Num()>0)
        for (int i = 0; i < outHit.Num(); i++) {
        //UE_LOG(LogTemp, Warning, TEXT("collide with %s!"),*outHit[i].GetActor()->GetName());
            AZombie* temp = Cast<AZombie>(outHit[i].GetActor());
            if (temp == nullptr) {
                return true;
            }
        }
    }
    else {
        nextState = EZombieState::Run;
    }
    return false;
    //queryObjectList.AddObjectTypesToQuery(ECC_WorldStatic);
    ////	DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.01, 0, 1);
    //if (GetWorld()->LineTraceMultiByObjectType(outHit, StartPoint, EndPoint, queryObjectList)) {
    //	for (int i = 0; i < outHit.Num(); i++) {
    //		/*if (spawnActorObj->IsCollidingWithWall(outHit[i].GetActor()))
    //		{
    //			return true;
    //		}*/
    //		if (spawnActorObj->climbWall.Find(outHit[i].GetActor()) != -1) {
    //			return true;
    //		}
    //	}
    //}
}

bool AZombie::IsEnteringAttackState()
{
    FHitResult outHit;
    //	FVector StartPoint = collisionComp->GetComponentLocation()+forwardVector*(sphereRadius+1);
    FVector StartPoint = this->GetActorLocation();
    //	StartPoint.X += (sphereRadius + 1);
    FVector EndPoint = StartPoint + forwardVector * forwardTraceDistanceV2;
    FCollisionObjectQueryParams queryObjectList;
    //queryObjectList.bReturnPhysicalMaterial = false;
    //queryObjectList.AddIgnoredActor(this);
    queryObjectList.AddObjectTypesToQuery(ECC_GameTraceChannel1);
    FCollisionQueryParams collisionParams;
    collisionParams.bReturnPhysicalMaterial = false;
    collisionParams.AddIgnoredActor(this);
    //DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.05, 0, 3);
    if (GetWorld()->LineTraceSingleByObjectType(outHit, StartPoint, EndPoint, queryObjectList, collisionParams)) {
        AZombie* temp = Cast<AZombie>(outHit.GetActor());
        if (temp == nullptr) {
            return true;
        }
    }
    else {
        nextState = EZombieState::Run;
    }
    return false;
    //queryObjectList.AddObjectTypesToQuery(ECC_WorldStatic);
    ////	DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.01, 0, 1);
    //if (GetWorld()->LineTraceMultiByObjectType(outHit, StartPoint, EndPoint, queryObjectList)) {
    //	for (int i = 0; i < outHit.Num(); i++) {
    //		/*if (spawnActorObj->IsCollidingWithWall(outHit[i].GetActor()))
    //		{
    //			return true;
    //		}*/
    //		if (spawnActorObj->climbWall.Find(outHit[i].GetActor()) != -1) {
    //			return true;
    //		}
    //	}
    //}
}


//void AZombie::AddRunAndFromSideToCenterForce()
//{
//	if (FMath::Abs(FVector::DotProduct(forwardVector, this->GetVelocity())) < maxRunSpeed) {
//		collisionComp->AddForce(forwardForce * forwardVector);
//	}
//	FVector faceVector = spawnActorObj->facePointActor->GetActorLocation() - this->GetActorLocation();
//	if (FMath::Abs(this->GetActorLocation().Y - spawnActorObj->GetActorLocation().Y) > spawnActorObj->needToChangeHorizontalDistance || faceVector.X < this->spawnActorObj->changeHorizontalWithinDistance)
//		if (FMath::Abs(FVector::DotProduct(rightVector, this->GetVelocity())) < maxLeftRightSpeed) {
//			FVector multiplyVector = FVector::CrossProduct(faceVector, forwardVector).GetSignVector();
//			collisionComp->AddForce(centerForce * rightVector * FVector::DotProduct(FVector(0, 0, 1), multiplyVector) * -1);
//		}
//
//	if (currentState == EZombieState::Run) {
//		faceVector.Z = this->GetActorLocation().Z;
//		faceVector = this->GetActorLocation() + faceVector;
//		characterRootComp->SetWorldRotation(UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), faceVector));
//	}
//	else {
//		characterRootComp->SetWorldRotation(UKismetMathLibrary::FindLookAtRotation(this->GetActorLocation(), spawnActorObj->facePointActor->GetActorLocation()));
//	}
//}

void AZombie::StateMachineV2(float deltaTime)
{
    if (currentState == EZombieState::Run) {
    
        if (IsEnteringAttackState()) {
            nextState = EZombieState::Attack;
        }
        if (IsEnteringClimbState()) {
            if (bCanBeJump && FMath::RandRange(0.0f, 1.0f) <= jumpProbability) {
                nextState = EZombieState::Jump;
            }
            else {
                nextState = EZombieState::Climb;
            }
        }

    }
    if (currentState == EZombieState::Climb)
    {
        if (collisionComp->GetPhysicsLinearVelocity().ProjectOnTo(upVector).Size() < maxClimbSpeedV2) {
            collisionComp->AddForce(climbUpForceV2 * upVector);
        }
    }

    if (currentState == EZombieState::Jump)
    {
        if (jumpTimeCal > jumpIntervalV2) {
            if (collisionComp->GetPhysicsLinearVelocity().ProjectOnTo(upVector).Size()<maxJumpSpeedV2) {
                collisionComp->AddImpulse(jumpImpulseV2 * upVector);
                jumpTimeCal = 0;
            }
        }
        else {
            jumpTimeCal += deltaTime;
        }
    }

    if (currentState == EZombieState::Climb || currentState == EZombieState::Jump) {
        //将某些速度小于阈值一段时间的僵尸变成attack状态
        if (collisionComp->GetPhysicsLinearVelocity().ProjectOnTo(upVector).Size() < jumpClimbToAttackSpeed) {
            if (jumpClimbToAttackCalTime > jumpClimbToAttackIntervalTime) {
                nextState = EZombieState::Attack;
                jumpClimbToAttackCalTime = 0;
            }
            else {
                jumpClimbToAttackCalTime += deltaTime;
            }
        }
        else {
            jumpClimbToAttackCalTime = 0;
        }

        //将靠近墙的僵尸转变成attack状态
        if (IsEnteringAttackState()) {
            nextState = EZombieState::Attack;
        }

    }

    if (currentState == EZombieState::Climb || currentState == EZombieState::Jump || currentState == EZombieState::Attack) {
        AddSideToCenterForce(spawnActorObj);
    }

    if (currentState != EZombieState::Die) {
        AddForwardRunForce();
        rotateCharacterAppearance(spawnActorObj->facePointActor, deltaTime);
    }



}



//My Version end


//version 3 begin

void AZombie::StateMachineV3(float deltaTime)
{
    if (currentState != EZombieState::Die && IsCollideWithWall()) {
        nextState = EZombieState::Attack;
    }
    if (currentState == EZombieState::Run) {

        if (IsEnteringClimbState()) {
            if (bCanBeJump && FMath::RandRange(0.0f, 1.0f) <= jumpProbability) {
                nextState = EZombieState::Jump;
            }
            else {
                nextState = EZombieState::Climb;
            }
        }

    }
    if (currentState == EZombieState::Climb)
    {
        if (collisionComp->GetPhysicsLinearVelocity().ProjectOnTo(upVector).Size() < maxClimbSpeedV2) {
            collisionComp->AddForce(climbUpForceV2 * upVector);
        }
    }

    if (currentState == EZombieState::Jump)
    {
        if (jumpTimeCal > jumpIntervalV2) {
            if (collisionComp->GetPhysicsLinearVelocity().ProjectOnTo(upVector).Size() < maxJumpSpeedV2) {
                collisionComp->AddImpulse(jumpImpulseV2 * upVector);
                jumpTimeCal = 0;
            }
        }
        else {
            jumpTimeCal += deltaTime;
        }
    }

    if (currentState == EZombieState::Climb || currentState == EZombieState::Jump) {
        //将某些速度小于阈值一段时间的僵尸变成attack状态
        if (collisionComp->GetPhysicsLinearVelocity().ProjectOnTo(upVector).Size() < jumpClimbToAttackSpeed) {
            if (jumpClimbToAttackCalTime > jumpClimbToAttackIntervalTime) {
                nextState = EZombieState::Attack;
                jumpClimbToAttackCalTime = 0;
            }
            else {
                jumpClimbToAttackCalTime += deltaTime;
            }
        }
        else {
            jumpClimbToAttackCalTime = 0;
        }
    
    }

    if (currentState == EZombieState::Climb || currentState == EZombieState::Jump || currentState == EZombieState::Attack) {
        AddSideToCenterForce(spawnActorObj);
    }

    if (currentState != EZombieState::Die) {
        AddForwardRunForce();
        rotateCharacterAppearance(spawnActorObj->facePointActor, deltaTime);
    }



}

bool AZombie::IsCollideWithWall()
{
    FHitResult outHit;
    FVector StartPoint = this->GetActorLocation();
    FVector EndPoint = StartPoint + forwardVector * forwardTraceDistanceV2;
    FCollisionObjectQueryParams queryObjectList;
    queryObjectList.AddObjectTypesToQuery(ECC_GameTraceChannel1);
    FCollisionQueryParams collisionParams;
    collisionParams.bReturnPhysicalMaterial = false;
    collisionParams.AddIgnoredActor(this);
    //DrawDebugLine(GetWorld(), StartPoint, EndPoint, FColor::Green, false, 0.05, 0, 3);
    if (GetWorld()->LineTraceSingleByObjectType(outHit, StartPoint, EndPoint, queryObjectList, collisionParams)) {
        AZombie* temp = Cast<AZombie>(outHit.GetActor());
        if (temp == nullptr) {
            return true;
        }
    }
    return false;
}

//version 3 end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773

# 死亡区域

僵尸进入这个区域会触发死亡:

//=====================================DieSphere.h===========================================
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/StaticMeshComponent.h"
#include "Components/BoxComponent.h"
#include "Zombie.h"
#include "DieSphere.generated.h"

UCLASS()
class MYPROJECT01_API ADieSphere : public AActor
{
    GENERATED_BODY()
    
public:	
    // Sets default values for this actor's properties
    ADieSphere();

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        float deathImpulseIntervalTime = 3;

    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        float deathTime = 0;

    UPROPERTY(VisibleAnywhere, Category = "BBB Components")
        UStaticMeshComponent* MeshComp;
    UPROPERTY(VisibleAnywhere, Category = "BBB Components")
        UBoxComponent* dieTriggerComp;
    UPROPERTY(VisibleAnywhere, Category = "BBB Components")
        UBoxComponent* climbComp;
    UPROPERTY(VisibleAnywhere, Category = "BBB Components")
        UBoxComponent* dieUpComp;



protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
    const int width = 100;
    const int thickness = 10;

public:	
    // Called every frame
    virtual void Tick(float DeltaTime) override;
private:
    TArray<AActor*> dieZombies;
    TArray<UPrimitiveComponent*> dieSkeletal;

};
//=====================================DieSphere.cpp=========================================
#include "DieSphere.h"

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

    MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
    MeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    RootComponent = MeshComp;

    dieTriggerComp = CreateDefaultSubobject<UBoxComponent>(TEXT("dieTriggerComponent"));
    dieTriggerComp->SetCollisionObjectType(ECC_WorldStatic);
    dieTriggerComp->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
    dieTriggerComp->SetCollisionResponseToAllChannels(ECR_Ignore);
    dieTriggerComp->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Overlap);
    dieTriggerComp->SetupAttachment(MeshComp);
    dieTriggerComp->SetRelativeLocation(FVector(0, 0, 980.0));
    dieTriggerComp->SetRelativeScale3D(FVector(width, thickness, 12));
    dieTriggerComp->SetRelativeRotation(FRotator(0,30,0));

    dieUpComp = CreateDefaultSubobject<UBoxComponent>(TEXT("dieUpComponent"));
    dieUpComp->SetCollisionObjectType(ECC_WorldStatic);
    dieUpComp->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
    dieUpComp->SetCollisionResponseToAllChannels(ECR_Ignore);
    dieUpComp->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Overlap);
    dieUpComp->SetupAttachment(MeshComp);
    dieUpComp->SetRelativeLocation(FVector(0, 0, 570.0));
    dieUpComp->SetRelativeScale3D(FVector(width, thickness, 3));
    dieUpComp->SetRelativeRotation(FRotator(0,30,0));

    climbComp = CreateDefaultSubobject<UBoxComponent>(TEXT("climbComponent"));
    climbComp->SetCollisionObjectType(ECC_GameTraceChannel1);
    climbComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    climbComp->SetCollisionResponseToAllChannels(ECR_Ignore);
    climbComp->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Block);
    climbComp->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Block);
    climbComp->SetupAttachment(MeshComp);
    climbComp->SetRelativeLocation(FVector(0,0,-100));
    climbComp->SetRelativeScale3D(FVector(width, thickness, 20));
    climbComp->SetRelativeRotation(FRotator(0,30,0));
}

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

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

    dieTriggerComp->GetOverlappingActors(dieZombies);
    TArray<AActor*>::TIterator it = dieZombies.CreateIterator();
    while (it) {
        AActor* tempActor = *it;
        //UE_LOG(LogActor, Warning, TEXT("actor %s died!"), *(tempActor->GetActorLabel()));
        AZombie* temp = Cast<AZombie>(*it);
        if (temp != nullptr) {
            temp->goToDie();
        }
        ++it;
    }
    dieZombies.Empty();

    if (deathTime < deathImpulseIntervalTime) {
        deathTime += DeltaTime;
    }
    else {
        deathTime = 0;
        dieUpComp->GetOverlappingActors(dieZombies);
        //TArray<UPrimitiveComponent*>::TIterator it = dieSkeletal.CreateIterator();
        TArray<AActor*>::TIterator it = dieZombies.CreateIterator();
        while (it) {
            AActor* tempActor = *it;
        UE_LOG(LogActor, Warning, TEXT("actor %s died!"), *(tempActor->GetActorLabel()));
            AZombie* temp = Cast<AZombie>(*it);
            if (temp != nullptr) {
                temp->dieUpDown();
            }
            ++it;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

# 生成地形

下面是一个生成地形:

generateTerrain

//================================================generateTerrain.h===================================================

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "UObject/ConstructorHelpers.h"
#include "BaseSceneObj.h"
#include "generateTerrain.generated.h"

UCLASS()
class JETPACKANIMATIONSET_API AgenerateTerrain : public AActor
{
    GENERATED_BODY()
    
public:	
    // Sets default values for this actor's properties
    AgenerateTerrain();
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int baseNum = 21;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        TSubclassOf<ABaseSceneObj> baseObj;
    UPROPERTY(EditAnywhere, Category = "AAA SpawnParameter")
        int objDistance=100;

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

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

    FVector objBoundsExtent;
    FVector objCenter;

    void GenerateBaseShape();
    void getObjBasePosition();
    void generateLineBaseObjAtPosition(FVector pos,int num);
};
//================================================generateTerrain.cpp===================================================

#include "generateTerrain.h"

// Sets default values
AgenerateTerrain::AgenerateTerrain()
{
     // 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;
    ConstructorHelpers::FObjectFinder<UStaticMesh> MeshRef(TEXT("/Engine/BasicShapes/Cube.Cube"));
    targetMesh = MeshRef.Object;
}

void AgenerateTerrain::GenerateBaseShape()
{

    if (baseNum % 2 == 1) {
        int lineNum = baseNum;
        FVector baseLocation = GetActorLocation();
        while (lineNum >= 1) {
            baseLocation.Y = GetActorLocation().Y-(lineNum - 1) / 2 * objDistance;
            generateLineBaseObjAtPosition(baseLocation, lineNum);
            lineNum -= 2;
            int heightNum = lineNum;
            FVector heightLocation = baseLocation;
            while (heightNum >=1) {
                heightLocation.Y += objDistance;
                heightLocation.Z += objDistance;
                generateLineBaseObjAtPosition(heightLocation,heightNum);
                heightNum -= 2;
            }
            baseLocation.X -= objDistance;
        }
    }
    UE_LOG(LogTemp, Warning, TEXT("generation is ok!"));
}

void AgenerateTerrain::generateLineBaseObjAtPosition(FVector pos, int num)
{
    FActorSpawnParameters SpawnInfo;
    FRotator targetRot(0, 0, 0);
    for (int i = 0; i < num; i++) {
        FVector targetLocation = pos;
        targetLocation.Y = pos.Y + i * objDistance;
        this->GetWorld()->SpawnActor<ABaseSceneObj>(baseObj, targetLocation, targetRot, SpawnInfo);
    }
}
void AgenerateTerrain::getObjBasePosition()
{
    ABaseSceneObj* tempBaseObj = Cast<ABaseSceneObj>(baseObj);
    if (tempBaseObj != nullptr) {
        if (tempBaseObj->getCenterAndBound()) {
            objDistance = tempBaseObj->objBoundsExtent.Y*2;
            if (GEngine) {
                GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, FString::Printf(TEXT("使用物体自身半径:!")));
            }
        }
    }
    else {
        if (GEngine) {
            GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, FString::Printf(TEXT("转换类型失败!")));
        }
    }
}

// Called when the game starts or when spawned
void AgenerateTerrain::BeginPlay()
{
    Super::BeginPlay();
//	getObjBasePosition();
    GenerateBaseShape();
}

// Called every frame
void AgenerateTerrain::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

# 生成单元

下面是被生成的生成单元:

//================================BaseSceneObj.h=================================

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Runtime/Engine/Public/EngineGlobals.h"
#include "BaseSceneObj.generated.h"

UCLASS()
class JETPACKANIMATIONSET_API ABaseSceneObj : public AActor
{
    GENERATED_BODY()
    
public:	
    // Sets default values for this actor's properties
    ABaseSceneObj();
    bool getCenterAndBound();
    UPROPERTY(VisibleAnywhere, Category = "CCC Appearance")
        UBoxComponent * baseCollision;

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

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

    UStaticMeshComponent* staticMesh;
    UStaticMesh* targetMesh;


};
//================================BaseSceneObj.cpp=================================

#include "BaseSceneObj.h"

// Sets default values
ABaseSceneObj::ABaseSceneObj()
{
     // 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;
    baseCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("BaseCollision"));
    baseCollision->SetCollisionObjectType(ECC_GameTraceChannel1);
    baseCollision->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    baseCollision->SetCollisionResponseToAllChannels(ECR_Ignore);
    baseCollision->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
    baseCollision->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
    baseCollision->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Block);
    RootComponent = baseCollision;

    ConstructorHelpers::FObjectFinder<UStaticMesh> MeshRef(TEXT("/Engine/BasicShapes/Cube.Cube"));
    targetMesh = MeshRef.Object;
    staticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("staticMesh"));
    staticMesh->SetStaticMesh(targetMesh);
    staticMesh->SetVisibility(true);
    staticMesh->SetSimulatePhysics(false);
    staticMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    staticMesh->SetupAttachment(baseCollision);

}

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

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

}

bool ABaseSceneObj::getCenterAndBound() {
    if (RootComponent != nullptr) {
        GetActorBounds(false, objCenter, objBoundsExtent);
        return true;
    }
    else {
        if (GEngine) {
            GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, FString::Printf(TEXT("BaseSceneObj 根节点是空的!")));
        }
        return false;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93