新建一个AIController的类
现在将导航网格拖入场景中
将位置归为0,0,0
调整画刷,让其包裹整个场景
然后把导航勾上
在世界大纲中,我们就发现了这个组件
我们设置单元格大小和高度为5
当出现绿色就说明参数是合格的,绿色区域就是可以导航的区域
然后我们取消掉这个勾
========================
编写AI控制器的头文件
UCLASS()
class PACMAN_API AEnermyController : public AAIController
{
GENERATED_BODY()
public:
//覆盖父类的函数
//控制函数
void OnPossess(class APawn* PawnToPossess) override;
//移动完成函数
virtual void OnMoveCompleted(FAIRequestID RequestID,const FPathFollowingResult& Result) override;
//找到新的目的地
void GoToNewDestination();
private:
//被控制的角色
class APacdotEnermy* ControlleredEnermy;
};
编写源文件
#include "EnermyController.h"
#include "PacdotEnermy.h"
#include "NavigationSystem.h"
void AEnermyController::OnPossess( APawn* PawnToPossess)
{
//建议覆盖的函数,要调用一下父类的函数
Super::OnPossess(PawnToPossess);
//转化被控制的对象为APacdotEnermy类型
ControlleredEnermy = Cast<APacdotEnermy>(PawnToPossess);
}
void AEnermyController::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result)
{
Super::OnMoveCompleted(RequestID, Result);
//移动完成时,直接开启下一次的移动
GoToNewDestination();
}
void AEnermyController::GoToNewDestination()
{
//得到场景中导航网格的指针
UNavigationSystemV1* NavMesh = UNavigationSystemV1::GetCurrent(this);
if (NavMesh)
{
//保存目的地的结构体
FNavLocation RondamLocation;
//在可到达半径范围内,得到一个随机点
//参数一:被控制角色的位置
//参数二:搜索半径
//参数三:保存新目的地
//返回值:是否找到新目的地
const bool bNewDestinationFound = NavMesh->GetRandomReachablePointInRadius(ControlleredEnermy->GetActorLocation(),10000, RondamLocation);
if (bNewDestinationFound)
{
//参数二:在目的地附近多远就可以视为移动完成
MoveToLocation(RondamLocation.Location, 50.0f);
}
}
}
加一个这个,在这个文件
进入敌人蓝图,选择ai控制器
现在还没有逻辑来驱动东西开始
打开游戏模式c++类
创建枚举,存储游戏状态
UENUM(BlueprintType)
enum class EGameState : uint8
{
EMenu,
EPlay,
EPause,
EWin,
EGameOver
};
编写游戏模式类的头文件
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "PacmanGameModeBase.generated.h"
/**
*
*/
//设置一个枚举
UENUM(BlueprintType)
enum class EGameState : uint8
{
EMenu,
EPlay,
EPause,
EWin,
EGameOver
};
UCLASS()
class PACMAN_API APacmanGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
//构造函数
APacmanGameModeBase();
//当前的游戏状态
UPROPERTY(EditAnywhere, BlueprintReadWrite)
EGameState CurrentState;
//场景中食物的总数
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int PacdotNum;
//设置和查询食物的总数
int GetPacdotNum() const;
void SetPacdotNum(int value);
//设置和查询游戏状态
EGameState GetCurrentState() const;
void SetCurrentState(EGameState value);
//游戏开始函数
void StartGame();
//游戏暂停函数
void PauseGame();
//重新开始函数
void ReStartGame();
protected:
//开始函数
virtual void BeginPlay() override;
private:
//场景中的所有敌人
TArray<class APacdotEnermy *> Enermis;
};
//定义成内敛函数
FORCEINLINE int APacmanGameModeBase::GetPacdotNum() const
{
return PacdotNum;
}
FORCEINLINE EGameState APacmanGameModeBase::GetCurrentState() const
{
return CurrentState;
}
编写源代码
#include "PacmanGameModeBase.h"
#include "Pacdot.h"
#include "PacdotEnermy.h"
#include "EngineUtils.h"
#include "EnermyController.h"
APacmanGameModeBase::APacmanGameModeBase()
{
//初始化游戏状态为菜单模式
CurrentState = EGameState::EMenu;
}
void APacmanGameModeBase::BeginPlay()
{
//利用迭代,查找场景中有多少食物
for (TActorIterator<APacdot> PacItr(GetWorld()); PacItr; ++PacItr)
{
PacdotNum++;
}
//利用迭代,找到所有敌人
for (TActorIterator<APacdotEnermy> EneItr(GetWorld()); EneItr; ++EneItr)
{
Enermis.Add(Cast<APacdotEnermy>(*EneItr));
}
}
void APacmanGameModeBase::SetCurrentState(EGameState value)
{
CurrentState = value;
switch (CurrentState)
{
case EGameState::EMenu:
break;
case EGameState::EPlay:
break;
case EGameState::EPause:
break;
case EGameState::EWin:
break;
case EGameState::EGameOver:
break;
default:
break;
}
}
void APacmanGameModeBase::SetPacdotNum(int value)
{
PacdotNum = value;
//如果没有食物了,就获胜了
if (PacdotNum == 0)
{
SetCurrentState(EGameState::EWin);
}
}
void APacmanGameModeBase::StartGame()
{
//如果游戏在菜单状态,该函数才有效
if (CurrentState == EGameState::EMenu)
{
SetCurrentState(EGameState::EPlay);
//启动游戏之后,让每个敌人开始移动
for (auto Iter(Enermis.CreateIterator()); Iter; ++Iter)
{
Cast<AEnermyController>((*Iter)->GetController())->GoToNewDestination();
}
}
}
void APacmanGameModeBase::PauseGame()
{
}
void APacmanGameModeBase::ReStartGame()
{
}
然后打开主角类,首先定义一个游戏模式类的指针成员变量
class APacmanGameModeBase * ModeBaseRef;
在源文件中导入头文件
#include "Kismet/GameplayStatics.h"
然后我们在游戏开始函数里,得到当前游戏模式
//得到当前游戏模式
ModeBaseRef = Cast< APacmanGameModeBase>(UGameplayStatics::GetGameMode(this));
添加三个函数
//游戏开始函数
void StartGame();
//游戏暂停函数
void PauseGame();
//重新开始函数
void ReStartGame();
在源文件中进行定义
void APacdotPlayer::StartGame()
{
ModeBaseRef->StartGame();
}
void APacdotPlayer::PauseGame()
{
ModeBaseRef->PauseGame();
}
void APacdotPlayer::ReStartGame()
{
ModeBaseRef->ReStartGame();
}
然后进行按键绑定
PlayerInputComponent->BindAction("StartGame", IE_Pressed, this, &APacdotPlayer::StartGame);
PlayerInputComponent->BindAction("PauseGame", IE_Pressed, this, &APacdotPlayer::PauseGame);
PlayerInputComponent->BindAction("ReStartGame", IE_Pressed, this,&APacdotPlayer::ReStartGame);
编译,运行游戏,敌人可以动了
现在实现避免敌人互相碰撞
我们添加一个碰撞通道
然后添加碰撞预设
给敌人蓝图的胶囊体设置碰撞预设
这两个碰撞预设都设为nocollision