记录UE4小游戏
创建一个继承与AIController的AIEnemy用于控制Enemy,Enemy类负责做状态的转换。
Enemy.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "..\Public\PacManCharacter.h"
#include "Enemy.generated.h"
UCLASS()
class MYPACMAN_API AEnemy : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AEnemy();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
void SetVulnerable(); //状态转换
void SetInVulnerable();//状态转换
void SetMove(bool bMoveIt);//移动用AIEnemy类实现
void Killed();//
void ReArm();//死亡后恢复
UPROPERTY(VisibleAnywhere,Category = Body)
UStaticMeshComponent* EnemyBody;
UFUNCTION()
void OnCollision(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);//碰撞函数
bool bIsDead;
private:
class UMaterialInterface* DefaultMaterial;//材质
class UMaterialInterface* VulnerableMaterial;
FTimerHandle TimeVulnerable; //处于可被吃状态的时间
bool bIsVulnerable;
};
Enemy.cpp
// Fill out your copyright notice in the Description page of Project Settings.
//#include "Enemy.h"
#include "..\Public\Enemy.h"
#include "UObject/ConstructorHelpers.h"
#include "Components/CapsuleComponent.h"
//#include "GameFramework/Actor.h"
#include "TimerManager.h"
#include "..\Public\AIEnemy.h"
#include "GameFramework/CharacterMovementComponent.h"
// Sets default values
AEnemy::AEnemy()
{
// 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;
//创建body
EnemyBody = CreateDefaultSubobject<UStaticMeshComponent> (TEXT("Body"));
//用圆柱绑定外形
static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderObj(TEXT("'/Game/StarterContent/Shapes/Shape_Cylinder'"));
if (CylinderObj.Succeeded())
{
EnemyBody->SetStaticMesh(CylinderObj.Object);
}
//设置大小
EnemyBody->SetWorldScale3D(FVector(0.7f, 0.7f, 1.0f));
EnemyBody->SetRelativeLocation(FVector(0, 0, -50.0f));//为了和碰撞体适配下移动
EnemyBody->SetupAttachment(RootComponent);//绑定为默认根组件
//设置碰撞体大小
GetCapsuleComponent()->SetCapsuleRadius(40.0f);
GetCapsuleComponent()->SetCapsuleHalfHeight(50.0f);
//绑定材质
static ConstructorHelpers::FObjectFinder<UMaterial> VulnerableMat(TEXT("Material'/Game/Materials/M_Enemy_Vulnerable.M_Enemy_Vulnerable'"));
if (VulnerableMat.Succeeded())
{
VulnerableMaterial = Cast<UMaterialInterface>(VulnerableMat.Object);
}
//BaseCollisionComponent->SetSphereRadius(16);
//开启碰撞
SetActorEnableCollision(true);
AIControllerClass = AAIEnemy::StaticClass();
}
// Called when the game starts or when spawned
void AEnemy::BeginPlay()
{
Super::BeginPlay();
DefaultMaterial = EnemyBody->GetMaterial(0); //游戏开始获取默认材质
GetCapsuleComponent()->OnComponentBeginOverlap.AddDynamic(this, &AEnemy::OnCollision);//绑定碰撞
GetCharacterMovement()->MaxWalkSpeed = 150.0f;
//VulnerableMaterial
//VulnerableMaterial = EnemyBody->GetMaterial(0, VulnerableMat);
//SetVulnerable();
}
// Called every frame
void AEnemy::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AEnemy::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}
void AEnemy::SetVulnerable()
{
GetWorldTimerManager().SetTimer(TimeVulnerable, this, &AEnemy::SetInVulnerable, 10.0f, false);//每隔TimeVulnerable时间变为SetInVulnerable
if (bIsVulnerable)
return;
bIsVulnerable = true;
EnemyBody->SetMaterial(0, VulnerableMaterial);
GetCharacterMovement()->MaxWalkSpeed = 50.0f;
}
void AEnemy::SetInVulnerable()
{
//清楚TimeVulnerable
GetWorldTimerManager().ClearTimer(TimeVulnerable);
bIsVulnerable = false;
EnemyBody->SetMaterial(0, DefaultMaterial);
GetCharacterMovement()->MaxWalkSpeed = 150.0f;
}
void AEnemy::SetMove(bool bMoveIt)
{
//用AIEnemy来控制Enemy
AAIEnemy* AI = Cast<AAIEnemy>(GetController());
if (bMoveIt)
{
AI->SearchNewPoint();
}
else {
AI->StopMove();
}
}
void AEnemy::Killed()
{
if (bIsDead) {
return;
}
bIsDead = true;
GetCharacterMovement()->MaxWalkSpeed = 300.0f;
AAIEnemy* AI = Cast<AAIEnemy>(GetController());
AI->GoHome();
}
void AEnemy::ReArm()
{
bIsDead = false;
GetCharacterMovement()->MaxWalkSpeed = 150.0f;
if (bIsVulnerable) {
SetInVulnerable();
}
SetMove(true);
}
void AEnemy::OnCollision(UPrimitiveComponent * HitComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
if (OtherActor->IsA(APacManCharacter::StaticClass()))
{
if (bIsVulnerable) {
Killed();
}
else
{
APacManCharacter* PacMan = Cast<APacManCharacter>(OtherActor);
PacMan->Killed();
}
}
}
AIEnemy.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "AIController.h"
#include "..\Public\Enemy.h"
#include "..\MypacmanGameModeBase.h"
#include "..\Public\PacManCharacter.h"
#include "AIEnemy.generated.h"
/**
*
*/
UCLASS()
class MYPACMAN_API AAIEnemy : public AAIController
{
GENERATED_BODY()
public:
void OnPossess(class APawn* InPawn) override; //==beginplay
virtual void OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result) override;//移动停止做什么
void SearchNewPoint();
void GoHome();
void ReArm();//
void StopMove();
private:
class AEnemy* Bot;
FVector HomeLocation;
FTimerHandle DeadTime;
AMypacmanGameModeBase* GameMode;
};
AIEnemy.cpp
// Fill out your copyright notice in the Description page of Project Settings.
//#include "AIEnemy.h"
#include "..\Public\AIEnemy.h"
#include "NavigationSystem.h"
#include "Kismet/GameplayStatics.h"
#include "TimerManager.h"
void AAIEnemy::OnPossess(class APawn* InPawn) {
Super::OnPossess(InPawn);
Bot = Cast<AEnemy>(InPawn);
HomeLocation = Bot->GetActorLocation();
SearchNewPoint();
GameMode = Cast<AMypacmanGameModeBase>(UGameplayStatics::GetGameMode(this));
}
void AAIEnemy::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult & Result)
{
if (!Bot->bIsDead && GameMode->GetCurrentState()!=EGameState::Epause) {
SearchNewPoint();
}
}
//这个函数很重要,最好看下底层源码
void AAIEnemy::SearchNewPoint()
{
UNavigationSystemV1* NavMesh = FNavigationSystem::GetCurrent<UNavigationSystemV1>(this);
if (NavMesh) {
const float SearchRadius = 10000.0f;
FNavLocation RandomPt;
const bool bFound = NavMesh->GetRandomReachablePointInRadius(Bot->GetActorLocation(), SearchRadius, RandomPt);
//const bool bFound = NavMesh->GetRandomReachablePointInRadius(myPacMan->GetActorLocation(), SearchRadius, RandomPt);
if (bFound)
{
MoveToLocation(RandomPt);
}
}
}
void AAIEnemy::GoHome()
{
MoveToLocation(HomeLocation);
GetWorldTimerManager().SetTimer(DeadTime,this,&AAIEnemy::ReArm,5.0f,false);
}
void AAIEnemy::ReArm()
{
GetWorldTimerManager().ClearTimer(DeadTime);
Bot->ReArm();
}
void AAIEnemy::StopMove()
{
//StopMovement();
MoveToLocation(Bot->GetActorLocation());
}
这里还需要改个东西
MypacmanGameModeBase.h
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
//#include "PacManCharacter.h"
#include "MypacmanGameModeBase.generated.h"
/**
*
*/
enum class EGameState :short {
EMenu,
EPlaying,
Epause,
EGameOver,
EWin
};
UCLASS()
class MYPACMAN_API AMypacmanGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
EGameState GetCurrentState() const;
void SetCurrentState(EGameState value);
void SetEnemyVulnerable();
private:
EGameState currentState;
TArray<class AEnemy*> Enemys;
};
FORCEINLINE EGameState AMypacmanGameModeBase::GetCurrentState() const { return currentState; };
AMypacmanGameModeBase.cpp
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "MypacmanGameModeBase.h"
#include "Public/Enemy.h"
#include "EngineUtils.h"
void AMypacmanGameModeBase::SetCurrentState(EGameState value)
{
currentState = value;
switch (value)
{
case EGameState::EPlaying:
for (auto Iter(Enemys.CreateIterator()); Iter; ++Iter)
{
(*Iter)->SetMove(true);
}
break;
case EGameState::Epause:
for (auto Iter(Enemys.CreateIterator()); Iter; ++Iter)
{
(*Iter)->SetMove(false);
}
break;
case EGameState::EGameOver:
for (auto Iter(Enemys.CreateIterator()); Iter; ++Iter)
{
(*Iter)->Destroy();
}
break;
case EGameState::EWin:
for (auto Iter(Enemys.CreateIterator()); Iter; ++Iter)
{
(*Iter)->Destroy();
}
break;
default:
break;
}
}
void AMypacmanGameModeBase::SetEnemyVulnerable()
{
for (auto Iter(Enemys.CreateIterator()); Iter; ++Iter)
{
(*Iter)->SetVulnerable();
}
}
void AMypacmanGameModeBase::BeginPlay()
{
Super::BeginPlay();
SetCurrentState(EGameState::EMenu);
for (TActorIterator<AEnemy> enemyIter(GetWorld()); enemyIter; ++enemyIter)
{
AEnemy* enemy = Cast<AEnemy>(*enemyIter);
if (enemy)
{
Enemys.Add(enemy);
}
}
}