5592人加入学习
(18人评价)
【旧版】Unreal初级课程 - 3D吃豆人

旧版课程,制作完成于2018-03-29,基于Unreal 4.18

价格 免费

1. 网格组件 UStaticMeshComponent

指定什么样的网格,就显示什么样的形状。

头文件:#include "Components/StaticMeshComponent.h"

声明:UStaticMeshComponent *CollectableMesh;

 

2. 碰撞体组件

检测两个物体的碰撞

 

3. 

EditDefaultsOnly:该属性可通过属性窗口来编辑,但仅能对原型编辑

 

EditAnywhere:该属性可从编辑器内的属性窗口编辑。

[展开全文]

在【模式】搜索Sound, 选择【环境音效】拖放到关卡中,在细节面板指定 WAV 的音效文件,即可播放BGM;

其他音乐格式转换的WAV的网站:

https://www.media.io/

// 获取GameMode // UGameplayStatics 需要引入 #include "Kismet/GameplayStatics.h"
	GameMode = Cast<APacManGameModeBase>(UGameplayStatics::GetGameMode(this));


	// 使用 ConstructorHelpers 需要引入的头文件:#include "Public/UObject/ConstructorHelpers.h"        找到了想要的变成的球体
	static ConstructorHelpers::FObjectFinder<UStaticMesh> Sphere(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'"));


// 使用GetCapsuleComponent,需要引入头文件:#include "Components/CapsuleComponent.h"
	GetCapsuleComponent()->SetCapsuleRadius(40.0f);
	GetCapsuleComponent()->SetCapsuleHalfHeight(50.0f);


	// 使用GetCharacterMovement(),需要引入的头文件:#include "GameFramework/CharacterMovementComponent.h"
	GetCharacterMovement()->MaxWalkSpeed = 50.0f;



	// 使用 GetCapsuleComponent() ,需要引入的头文件:#include "Components/CapsuleComponent.h"
	// 第一个参数是当前类,第二个参数是 要绑定的函数
	// 注册碰撞函数
	GetCapsuleComponent()->OnComponentBeginOverlap.AddDynamic(this, &APacManCharacter::OnCollision);



	// EditDefaultsOnly 可以在编辑器里面编辑,EditDefaultsOnly 只能对其原型进行修改;Category 分类
	// VisibleAnywhere  在任何地方都可以看到它,创建实例后,仍然可以修改它;不改成VisibleAneywhere,无法修改其材质
	UPROPERTY(VisibleAnywhere, Category = Collectable)
	UStaticMeshComponent* CollectableMesh;


	// EditAnyWhere 不仅可以对其原型进行修改,还可以对其实例修改
	UPROPERTY(EditAnywhere, Category = Collectable)
	bool bIsSuperCollectable;


	// UFont 字体,显示的文字,字体,为了寻找字体方便,修改字体方便,一个蓝图类来继承它,在蓝图中修改字体
	// EditAnyWhere 在哪块都能修改
	// BlueprintReadWrite 表示可以在蓝图中修改这个属性/定义
	// Category 分类,分到 HUDFont 类中去
	UPROPERTY(EditAnyWhere,BlueprintReadWrite,Category=HUDFont)



	#include "Public/TimerManager.h"
	GetWorldTimerManager().ClearTimer(TimeVulnerable);

AIEnemy.h

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

#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "Enemy.h"
#include "PacManGameModeBase.h"
#include "AIEnemy.generated.h"

/**
 * 
 */
UCLASS()
class PACMAN_API AAIEnemy : public AAIController
{
	GENERATED_BODY()
	
public:
	// 要重写的函数
	// 相当于beginplay,每当开启一个AI的时候调用,首先执行OnPossess
	void OnPossess(class APawn* InPawn) override;
	// 移动停止后调用
	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;
	APacManGameModeBase* GameMode;
};

AIEnemy.cpp

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


#include "Public/AIEnemy.h"
#include "NavigationSystem.h"
#include "Public/TimerManager.h"
#include "Kismet/GameplayStatics.h"

void AAIEnemy::OnPossess(class APawn* InPawn)
{
	Super::OnPossess(InPawn);
	// 获得Enemy,肉体
	Bot = Cast<AEnemy>(InPawn);
	HomeLocation = Bot->GetActorLocation();
	// 获取GameMode // UGameplayStatics 需要引入 #include "Kismet/GameplayStatics.h"
	GameMode = Cast<APacManGameModeBase>(UGameplayStatics::GetGameMode(this));
	SearchNewPoint();
}

void AAIEnemy::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult & Result)
{
	// 当敌人不是死亡的时候 并且 游戏不是暂停的时候
	if (!Bot->bIsDead &&GameMode->GetCurrentState() != EGameState::EPause)
	{
		// 找新的随机点
		SearchNewPoint();
	}
}

void AAIEnemy::SearchNewPoint()
{
	// 判断导航网格在不在
	UNavigationSystemV1* NavMesh = UNavigationSystemV1::GetCurrent(this);
	// 如果在
	if (NavMesh)
	{
		// 设置搜索半径
		// 1000.0f 太小了,改成 10000.0f
		const float SearchRadius = 10000.0f;
		// 设置一个点
		FNavLocation RandomPt;
		
		// 以敌人的位置为中心,这个半径为范围,找一个点,
		const bool bFound = NavMesh->GetRandomReachablePointInRadius(Bot->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());
}

Collectables.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SphereComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Collectables.generated.h"

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

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

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
	// EditDefaultsOnly 可以在编辑器里面编辑,EditDefaultsOnly 只能对其原型进行修改;Category 分类
	// VisibleAnywhere  在任何地方都可以看到它,创建实例后,仍然可以修改它;不改成VisibleAneywhere,无法修改其材质
	UPROPERTY(VisibleAnywhere, Category = Collectable)
	UStaticMeshComponent* CollectableMesh;

	UPROPERTY(EditDefaultsOnly, Category = Collectable)
	USphereComponent* BaseCollisionComponent;
	
	// EditAnyWhere 不仅可以对其原型进行修改,还可以对其实例修改
	UPROPERTY(EditAnywhere, Category = Collectable)
	bool bIsSuperCollectable;

};

Collectables.cpp

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


#include "Public/Collectables.h"
#include "Public/UObject/ConstructorHelpers.h"

// Sets default values
ACollectables::ACollectables()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	// 等于true,会使得下面这个函数(Tick)每帧运行,这个是豆的,不需要这个,设置成false
	PrimaryActorTick.bCanEverTick = false;
	// 开启碰撞功能(默认不开启碰撞功能)
	SetActorEnableCollision(true);
	// 创建网格组件
	CollectableMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CollectableMesh"));
	// 创建碰撞体组件
	BaseCollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("BaseCollisionComponent"));
	// 将 碰撞体 和 网格 绑定到一起
	CollectableMesh->AttachTo(BaseCollisionComponent);
	// 使用 ConstructorHelpers 需要引入的头文件:#include "Public/UObject/ConstructorHelpers.h"        找到了想要的变成的球体
	static ConstructorHelpers::FObjectFinder<UStaticMesh> Sphere(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'"));
	if (Sphere.Succeeded())
	{
		CollectableMesh->SetStaticMesh(Sphere.Object);
	}
	// 指定球的大小和尺寸
	CollectableMesh->SetWorldScale3D(FVector(0.3, 0.3, 0.3));
	BaseCollisionComponent->SetSphereRadius(16);
}

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

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

}

Enemy.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Components/StaticMeshComponent.h"
#include "Enemy.generated.h"

UCLASS()
class PACMAN_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;

	UPROPERTY(VisibleAnywhere,Category=Body)
	UStaticMeshComponent* EnemyBody;
	/*
		Vulnerable adj. 易受攻击的;易受伤害的;脆弱的;有局方的;有身价的
		InVulnerable adj. 不能伤害的;刀枪不入的;无懈可击的;不可攻破的
	*/
	// 易受攻击
	void SetVulnerable();
	// 不易受攻击
	void SetInVulnerable();
	
	
	// 是否移动
	void SetMove(bool bMoveIt);
	// 被杀死,回出生点
	void Killed();
	// 速度回到150
	void ReArm();

	// 敌人是否死亡
	bool bIsDead;

	UFUNCTION()
		void OnCollision(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);

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 "Public/Enemy.h"
#include "UObject/ConstructorHelpers.h"
#include "Components/CapsuleComponent.h"
#include "Public/TimerManager.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Public/PacManCharacter.h"
#include "Public/AIEnemy.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;
	// 显示组件
	EnemyBody = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Body"));
	// 创建样子
	// 找到对应的材质
	static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderObj(TEXT("'/Game/StarterContent/Shapes/Shape_Cylinder'"));
	// 成功找到就
	// Succeeded()需要加括号
	if (CylinderObj.Succeeded())
	{
		// 指定它的样子
		EnemyBody->SetStaticMesh(CylinderObj.Object);
	}
	// 设置它的大小
	EnemyBody->SetRelativeScale3D(FVector(0.7f, 0.7f, 1.0f));
	// 使得碰撞体和圆柱体重合在一起
	// CapsuleComponent 和 UStaticMeshComponent 重合
	EnemyBody->SetRelativeLocation(FVector(0, 0, -50));
	// 绑定到根组件上
	// AttachTo 过时了,换成:SetupAttachment
	EnemyBody->SetupAttachment(RootComponent);
	// 设置碰撞体的大小
	// 使用GetCapsuleComponent,需要引入头文件:#include "Components/CapsuleComponent.h"
	GetCapsuleComponent()->SetCapsuleRadius(40.0f);
	GetCapsuleComponent()->SetCapsuleHalfHeight(50.0f);


	static ConstructorHelpers::FObjectFinder<UMaterial> VulnerableMat(TEXT("'/Game/Materials/M_Enemy_Vulnerable'"));
	// 获得AI,设置好AIController
	AIControllerClass = AAIEnemy::StaticClass();
	// 启用碰撞函数
	SetActorEnableCollision(true);
}

// 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;
}

// 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()
{
	// 吃到豆子可以增加10
	// 过一段时间可以不受攻击
	// 使用GetWorldTimerManager(),需要引入头文件:#include "Public/TimerManager.h"
	// 第一个参数是TimeVulnerable;第二个参数:作用在哪个类;第三个参数:作用于哪个函数;第四个参数:时间间隔;第五个参数:是否循环
	GetWorldTimerManager().SetTimer(TimeVulnerable, this, &AEnemy::SetInVulnerable, 10.0f, false);

	if (bIsVulnerable)
	{
		return;
	}
	bIsVulnerable = true;
	// 连续吃到豆子,敌人当前的状态已经设置成了易受攻击的状态
	// 没必要执行 EnemyBody->SetMaterial(0, VulnerableMaterial);
	// 定义一个变量来判断当前的状态,已经是易受到攻击的状态,没必要设置材质了
	// 时间段可以从10秒变成20秒外,剩下的都不需要再改变了
	EnemyBody->SetMaterial(0, VulnerableMaterial);

	// 使用GetCharacterMovement(),需要引入的头文件:#include "GameFramework/CharacterMovementComponent.h"
	GetCharacterMovement()->MaxWalkSpeed = 50.0f;
}

void AEnemy::SetInVulnerable()
{
	GetWorldTimerManager().ClearTimer(TimeVulnerable);
	bIsVulnerable = false;
	EnemyBody->SetMaterial(0, DefaultMaterial);
	GetCharacterMovement()->MaxWalkSpeed = 150.0f;
}
// 是否移动
void AEnemy::SetMove(bool bMoveIt)
{
	// 获得AI
	// 最后括号里面的AIControllerClass弃用 换成 GetController()
	AAIEnemy* AI = Cast<AAIEnemy>(GetController());
	// 移动
	if (bMoveIt)
	{
		// 找下一个点
		AI->SearchNewPoint();
	}
	else
	{
		// 停止移动
		AI->StopMove();
	}
}

void AEnemy::Killed()
{
	if (bIsDead)
	{
		return;
	}

	bIsDead = true;

	GetCharacterMovement()->MaxWalkSpeed = 300.0f;
	// 获得AI
	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)
{
	// #include "Public/PacManCharacter.h"
	if (OtherActor->IsA(APacManCharacter::StaticClass()))
	{
		if (bIsVulnerable)
		{
			Killed();
		}
		else
		{
			APacManCharacter* PacMan = Cast<APacManCharacter>(OtherActor);
			PacMan->Killed();
		}
	}
}

PacManCharacter.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "PacManGameModeBase.h"
#include "PacManCharacter.generated.h"

UCLASS()
class PACMAN_API APacManCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	APacManCharacter();

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 MoveXAxis(float AxisValue);
	void MoveYAxis(float AxisValue);
	// 重启游戏
	void ReStart();
	// 重玩一局游戏
	void NewGame();
	// 暂停游戏
	void Pause();
	// 主角死亡
	void Killed();
	// 最后2个参数可写可不写 
	// 需要注意的是 class AActor* otherActor ,碰撞的物体是不是敌人or食物
	// 使用了反射机制,需要添加关键字 UFUNCTION()
	UFUNCTION()
		void OnCollision(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);

	// 下面这个2个,改成了Public可供PacManHUD显示调用
	// 需要吃的 豆 的数量
	int CollectablesToEat;
	// 当前的生命值
	int Lives;
private:
	// 定义一个向量
	FVector CurrentVelocity;
	// 初始的位置
	FVector StartPoint;
	// 
	APacManGameModeBase* GameMode;
};

PacManCharacter.cpp

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


#include "Public/PacManHUD.h"
#include "PacManGameModeBase.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/Canvas.h"
#include "Public/PacManCharacter.h"

void APacManHUD::DrawHUD()
{
	// 获得当前的游戏模式
	class APacManGameModeBase* GameMode = Cast<APacManGameModeBase>(UGameplayStatics::GetGameMode(this));
	// 根据不同的状态,显示不同的文字
	switch (GameMode->GetCurrentState())
	{
	case EGameState::EMenu:
		// 显示文字的函数,
		// 第一个参数:需要显示的文字;
		// 第二个参数:文字的颜色;
		// 第三个参数:获得横的屏幕的宽度;
		// 第四个参数:获得纵的屏幕的长度
		// 第五个参数:显示的字体
		// (Canvas->SizeX/2.0f) 就是在屏幕中间
		// 使用 Canvas 需要引入头文件:#include "Engine/Canvas.h"
		// 向下,向右为正方向,为了移动正中心,进行了减法运算到屏幕中间
		DrawText(TEXT("Welcome To PacMan!\n\nN to start a new game \nP to pause the game"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
		break;
	case EGameState::EPlaying:
	{// 这个括号需要
		// 获得主角,0 表示第一个玩家,场景只有一个玩家
		APacManCharacter* PacMan = Cast<APacManCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));
		if (PacMan)
		{
			// FString::FromInt() 将 int类型的变量转换成字符串
			FString LiveString = TEXT("Lives: ") + FString::FromInt(PacMan->Lives);
			DrawText(LiveString, FColor::Green, 50, 50, HUDFont);

			FString CollectablesToEatString = TEXT("CollectablesToEat: ") + FString::FromInt(PacMan->CollectablesToEat);
			DrawText(CollectablesToEatString, FColor::Green, Canvas->SizeX - 150, 50, HUDFont);
		}
	}// 这个括号需要
	break;
	case EGameState::EPause:
		DrawText(TEXT("P To Continue"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
		break;
	case EGameState::EWin:
		DrawText(TEXT("You Win!\n\n R for another"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
		break;
	case EGameState::EGameOver:
		DrawText(TEXT("CameOver!\n\nR for another"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
		break;
	default:
		break;
	}
}

PacManGameModeBase.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Public/Enemy.h"
#include "PacManGameModeBase.generated.h"

/**
 * 
 */
enum class EGameState : short {
	EMenu,
	EPlaying,
	EPause,
	EWin,
	EGameOver
};

UCLASS()
class PACMAN_API APacManGameModeBase : public AGameModeBase
{
	GENERATED_BODY()
public:
	// 但是由于我们的碰撞在,吃食物在,当前的状态是EPlaying,游戏中的时候才能吃食物,物体才会被销毁,把当前的状态设置成EPlaying,这样方便测试了
	virtual void BeginPlay() override;
	// 获得当前状态 加 const 不能修改我们的成员
	EGameState GetCurrentState() const;
	// 设置当前状态
	void SetCurrentState(EGameState value);
	// 易受攻击的方法
	void SetEnemyVulnerable();

private:
	// 要知道当前状态,才能做出不同的反应
	EGameState currentState;
	// 定义一个数组,保存所有的敌人
	TArray<class AEnemy*> Enemys;
};
// 为了程序更加快捷,定义一个 内联 
// APcManGameModeBase 和 命名的项目名称一致,视频中的AMyPacManGameModeBase,项目名称为:MyPacMan
FORCEINLINE EGameState APacManGameModeBase::GetCurrentState() const
{
	return currentState;
};

PacManGameModeBase.cpp

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


#include "PacManGameModeBase.h"
#include "Public/EngineUtils.h"

// 设置当前状态
void APacManGameModeBase::SetCurrentState(EGameState value)
{
	currentState = value;
	// 判断暂停等状态
	// 输入switch,Tab 两下,在switch_on中按住Tab输入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::EWin:
		// 遍历所有的敌人
		for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
		{
			// 销毁敌人
			(*Iter)->Destroy();
		}
		break;
	case EGameState::EGameOver:
		// 遍历所有的敌人
		for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
		{
			// 销毁敌人
			(*Iter)->Destroy();
		}
		break;
	default:
		break;
	}
}
// 易受攻击的事情,用 GameMode 来处理所有的敌人,在 Character 中直接调用 GameMode 的这个方法,就可以把所有的敌人都设置成易受到攻击的状态,而不必分别设置了
// 设置成易受攻击,被主角吃的行为就好了
void APacManGameModeBase::SetEnemyVulnerable()
{
	// 遍历所有的Enemy,调用它身上的SetVulnerable方法就可以了
	// Iter 表示存在的话,Iter++
	for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
	{
		// 调用它身上的SetVulnerable方法
		(*Iter)->SetVulnerable();
	}
}

void APacManGameModeBase::BeginPlay()
{
	// 设置成菜单模式,开局有UI提示
	SetCurrentState(EGameState::EMenu);
	// 使用迭代器,需要引入头文件:Public/EngineUtils.h
	// 获得所有的敌人 ;起个名字:enemyItr; GetWorld():从世界得到; enemyItr 存在的话就去遍历
	for (TActorIterator<AEnemy> enemyItr(GetWorld()); enemyItr; ++enemyItr)
	{
		// 强制转化成 AEnemy
		AEnemy* enemy = Cast<AEnemy>(*enemyItr);
		// 转化后,看存不存在
		if (enemy)
		{
			// 如果存在的话,就添加到数组里面
			Enemys.Add(enemy);
		}
	}
}

使用 UGameplayStatics 需要引入头文件 #include "Kismet/GameplayStatics.h"

使用 UNavigationSystemV1* 需要引入头文件 #include "NavigationSystem.h"

使用 GetWorldTimerManager() 需要引入头文件 #include "Public/TimerManager.h"

使用 ConstructorHelpers 需要引入头文件:#include "Public/UObject/ConstructorHelpers.h" 

使用 ConstructorHelpers 需要引入头文件 #include "UObject/ConstructorHelpers.h"

使用 GetCapsuleComponent,需要引入头文件:#include "Components/CapsuleComponent.h"

使用 GetCharacterMovement(),需要引入头文件:#include "GameFramework/CharacterMovementComponent.h"

使用 GetWorld() 需要引入头文件:#include "Engine/World.h"

使用 TActorIterator 需要引入头文件:#include "Public/EngineUtils.h"

使用 InputComponent.BindAxis() 和 BindAction() 需要引入头文件:#include "Components/InputComponent.h"

使用 Canvas 需要引入头文件:#include "Engine/Canvas.h"

使用 USphereComponent* 需要引入头文件:#include "Components/SphereComponent.h"

使用 UStaticMeshComponent* 需要引入头文件:#include "Components/StaticMeshComponent.h"

UPROPERTY(VisibleAnywhere, Category = Collectable)

UPROPERTY(EditDefaultsOnly, Category = Collectable)

UPROPERTY(EditAnywhere, Category = Collectable)

UPROPERTY(VisibleAnywhere,Category=Body)

UFUNCTION()
        void OnCollision(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);

UPROPERTY(EditAnyWhere,BlueprintReadWrite,Category=HUDFont)

[展开全文]

显示UI,需要继承HUD这个类:

创建基于PacManHUD的蓝图类

我们自定义的HUDFont需要指定字体:视图选项中 勾选 显示引擎内容,方可选择 RobotoDistanceField 字体。

修改完蓝图需要编辑保存

对 BP_PacManGameMode 的 HUD Class 指定成我们刚刚转化的 BP_PacManHUD 。

Canvas->SizeX / 2.0f , Canvas->SizeY / 2.0f 显示的文字不在正中间

PacManHUD.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "PacManHUD.generated.h"

/**
 * 
 */
UCLASS()
class PACMAN_API APacManHUD : public AHUD
{
	GENERATED_BODY()
public:
	// UFont 字体,显示的文字,字体,为了寻找字体方便,修改字体方便,一个蓝图类来继承它,在蓝图中修改字体
	// EditAnyWhere 在哪块都能修改
	// BlueprintReadWrite 表示可以在蓝图中修改这个属性/定义
	// Category 分类,分到HUDFont类中去
	UPROPERTY(EditAnyWhere,BlueprintReadWrite,Category=HUDFont)
	UFont* HUDFont;
	
	// 介绍一个函数,这个函数很重要,在屏幕上显示文字,靠这个函数来实现的
	// DrawHUD 就像 Tick 一样,每帧调用一次,这样把我输入的文字显示在屏幕上
	virtual void DrawHUD() override;
};

PacManHUD.cpp

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


#include "Public/PacManHUD.h"
#include "PacManGameModeBase.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/Canvas.h"
#include "Public/PacManCharacter.h"

void APacManHUD::DrawHUD()
{
	// 获得当前的游戏模式
	class APacManGameModeBase* GameMode = Cast<APacManGameModeBase>(UGameplayStatics::GetGameMode(this));
	// 根据不同的状态,显示不同的文字
	switch (GameMode->GetCurrentState())
	{
	case EGameState::EMenu:
		// 显示文字的函数,
		// 第一个参数:需要显示的文字;
		// 第二个参数:文字的颜色;
		// 第三个参数:获得横的屏幕的宽度;
		// 第四个参数:获得纵的屏幕的长度
		// 第五个参数:显示的字体
		// (Canvas->SizeX/2.0f) 就是在屏幕中间
		// 使用 Canvas 需要引入头文件:#include "Engine/Canvas.h"
		// 向下,向右为正方向,为了移动正中心,进行了减法运算到屏幕中间
		DrawText(TEXT("Welcome To PacMan!\n\nN to start a new game \nP to pause the game"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
		break;
	case EGameState::EPlaying:
	{// 这个括号需要
		// 获得主角,0 表示第一个玩家,场景只有一个玩家
		APacManCharacter* PacMan = Cast<APacManCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));
		if (PacMan)
		{
			// FString::FromInt() 将 int类型的变量转换成字符串
			FString LiveString = TEXT("Lives: ") + FString::FromInt(PacMan->Lives);
			DrawText(LiveString, FColor::Green, 50, 50, HUDFont);

			FString CollectablesToEatString = TEXT("CollectablesToEat: ") + FString::FromInt(PacMan->CollectablesToEat);
			DrawText(CollectablesToEatString, FColor::Green, Canvas->SizeX - 150, 50, HUDFont);
		}
	}// 这个括号需要
	break;
	case EGameState::EPause:
		DrawText(TEXT("P To Continue"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
		break;
	case EGameState::EWin:
		DrawText(TEXT("You Win!\n\n R for another"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
		break;
	case EGameState::EGameOver:
		DrawText(TEXT("CameOver!\n\nR for another"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
		break;
	default:
		break;
	}
}

PacManCharacter.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "PacManGameModeBase.h"
#include "PacManCharacter.generated.h"

UCLASS()
class PACMAN_API APacManCharacter : public ACharacter
{
// 改成了Public可供PacManHUD显示调用
public:	
	// 需要吃的 豆 的数量
	int CollectablesToEat;
	// 当前的生命值
	int Lives;
};

 

[展开全文]

Enemy.cpp

void AEnemy::BeginPlay()
{
	Super::BeginPlay();
	// 刚开始的速度太大了,设定初始的速度小一些
	GetCharacterMovement()->MaxWalkSpeed = 150.0f;
}

void AEnemy::Killed()
{
	if (bIsDead)
	{
		return;
	}

	bIsDead = true;

	GetCharacterMovement()->MaxWalkSpeed = 300.0f;
	// 获得AI
	AAIEnemy* AI = Cast<AAIEnemy>(GetController());
	// 让敌人回家
	AI->GoHome();
}

void AEnemy::ReArm()
{
	bIsDead = false;

	GetCharacterMovement()->MaxWalkSpeed = 150.0f;

	if (bIsVulnerable)
	{
		SetInVulnerable();
	}
	// 重新出来
	SetMove(true);
}

AIEnemy.h

#include "PacManGameModeBase.h"

UCLASS()
class PACMAN_API AAIEnemy : public AAIController
{
	GENERATED_BODY()
private:
	APacManGameModeBase* GameMode;
};

AIEnemy.cpp

#include "Kismet/GameplayStatics.h"

void AAIEnemy::OnPossess(class APawn* InPawn)
{
	Super::OnPossess(InPawn);
	// 获取GameMode // UGameplayStatics 需要引入 #include "Kismet/GameplayStatics.h"
	GameMode = Cast<APacManGameModeBase>(UGameplayStatics::GetGameMode(this));
}

void AAIEnemy::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult & Result)
{
	// 当敌人不是死亡的时候 并且 游戏不是暂停的时候
	if (!Bot->bIsDead &&GameMode->GetCurrentState() != EGameState::EPause)
	{
		// 找新的随机点
		SearchNewPoint();
	}
}

void AAIEnemy::StopMove()
{
	//StopMovement();
	MoveToLocation(Bot->GetActorLocation());
}

 

[展开全文]

PacManCharacter.cpp

void APacManCharacter::MoveXAxis(float AxisValue)
{
	// 只有游戏开始的时候才能移动
	if (GameMode->GetCurrentState() == EGameState::EPlaying)
	{
		CurrentVelocity.X = AxisValue;
		AddMovementInput(CurrentVelocity);
	}
}

void APacManCharacter::MoveYAxis(float AxisValue)
{
	// 只有游戏开始的时候才能移动
	if (GameMode->GetCurrentState() == EGameState::EPlaying)
	{
		CurrentVelocity.Y = AxisValue;
		AddMovementInput(CurrentVelocity);
	}
}

Enemy.cpp

AEnemy::AEnemy()
{
	// 启用碰撞函数
	SetActorEnableCollision(true);
}
void AEnemy::BeginPlay()
{
	Super::BeginPlay();
	// 注册碰撞函数
	GetCapsuleComponent()->OnComponentBeginOverlap.AddDynamic(this, &AEnemy::OnCollision);
}

// 是否移动
void AEnemy::SetMove(bool bMoveIt)
{
	// 获得AI
	// 最后括号里面的AIControllerClass弃用 换成 GetController()
	AAIEnemy* AI = Cast<AAIEnemy>(GetController());
	// 移动
	if (bMoveIt)
	{
		// 找下一个点
		AI->SearchNewPoint();
	}
	else
	{
		// 停止移动
		AI->StopMove();
	}
}

项目设置---引擎---Collision---新建对象通道

新建通道:

Preset:

新建

新建概述文件:

只需要和墙、地板发生碰撞,静态

Block :区块

区块:就是阻碍的意思

 

 

EnemyBody---碰撞预设值 改成 Enemy

CapsuleComponent---碰撞预设值 也改成 Enemy

 

解决场景中的敌人不移动的办法:

把场景中的敌人都删除,然后再重新拖入试一下

这样场景中的敌人就可以移动了

 

注册碰撞函数

启用碰撞函数:

主角的碰撞预设值是:Pawn

重叠:Overlap,一致才能发生作用

 

[展开全文]

控制游戏的暂停:就是控制怪物的移动

输入switch,Tab 两下,在switch_on中按住Tab输入value,然后回车,自动补全

Enemy.cpp

#include "Public/AIEnemy.h"

AEnemy::AEnemy()
{
	// 获得AI,设置好AIController
	AIControllerClass = AAIEnemy::StaticClass();
}

// 是否移动
void AEnemy::SetMove(bool bMoveIt)
{
	// 获得AI
	AAIEnemy* AI = Cast<AAIEnemy>(AIControllerClass);
	// 移动
	if (bMoveIt)
	{
		// 找下一个点
		AI->SearchNewPoint();
	}
	else
	{
		// 停止移动
		AI->StopMove();
	}
}

PacManGameModeBase.cpp

// 设置当前状态
void APacManGameModeBase::SetCurrentState(EGameState value)
{
	currentState = value;
	// 判断暂停等状态
	// 输入switch,Tab 两下,在switch_on中按住Tab输入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::EWin:
		// 遍历所有的敌人
		for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
		{
			// 销毁敌人
			(*Iter)->Destroy();
		}
		break;
	case EGameState::EGameOver:
		// 遍历所有的敌人
		for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
		{
			// 销毁敌人
			(*Iter)->Destroy();
		}
		break;
	default:
		break;
	}
}

void APacManGameModeBase::BeginPlay()
{
	// 设置成菜单模式,开局有UI提示
	SetCurrentState(EGameState::EMenu);
}

 

[展开全文]

PacManGameModeBase.h

#include "Public/Enemy.h"

UCLASS()
class PACMAN_API APacManGameModeBase : public AGameModeBase
{
	GENERATED_BODY()
public:
	// 易受攻击的方法
	void SetEnemyVulnerable();

private:
	// 定义一个数组,保存所有的敌人
	TArray<class AEnemy*> Enemys;
};

PacManGameMode.cpp

#include "Public/EngineUtils.h"

// 易受攻击的事情,用 GameMode 来处理所有的敌人,在 Character 中直接调用 GameMode 的这个方法,就可以把所有的敌人都设置成易受到攻击的状态,而不必分别设置了
// 设置成易受攻击,被主角吃的行为就好了
void APacManGameModeBase::SetEnemyVulnerable()
{
	// 遍历所有的Enemy,调用它身上的SetVulnerable方法就可以了
	// Iter 表示存在的话,Iter++
	for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
	{
		// 调用它身上的SetVulnerable方法
		(*Iter)->SetVulnerable();
	}
}

void APacManGameModeBase::BeginPlay()
{
	SetCurrentState(EGameState::EPlaying);
	// 使用迭代器,需要引入头文件:Public/EngineUtils.h
	// 获得所有的敌人 ;起个名字:enemyItr; GetWorld():从世界得到; enemyItr 存在的话就去遍历
	for (TActorIterator<AEnemy> enemyItr(GetWorld()); enemyItr; ++enemyItr)
	{
		// 强制转化成 AEnemy
		AEnemy* enemy = Cast<AEnemy>(*enemyItr);
		// 转化后,看存不存在
		if (enemy)
		{
			// 如果存在的话,就添加到数组里面
			Enemys.Add(enemy);
		}
	}
}

PacManCharacter.cpp

void APacManCharacter::OnCollision(UPrimitiveComponent * HitComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
	// 当游戏正在运行才去检测碰撞
	if (GameMode->GetCurrentState() == EGameState::EPlaying)
	{
		// 调用易受攻击的状态,什么时候调用:就是在主角吃到了超级大力丸的时候调用,把敌人设置成易受攻击的状态,找到主角,找到碰撞函数 OnCollision
		// 碰到是食物的话,就把它销毁
		// 当碰撞的对象是 ACollectables 类时销毁掉
		// 使用自己定义的类AColletables,也需要引入对应的头文件: #include "Public/Collectables.h"
		if (OtherActor->IsA(ACollectables::StaticClass()))
		{
			// 吃到豆子
			// 强制转化成豆子
			ACollectables* collectable = Cast<ACollectables>(OtherActor);
			// 判断是不是大力丸
			if (collectable->bIsSuperCollectable)
			{
				// 把敌人变成易受攻击的状态
				GameMode->SetEnemyVulnerable();
			}
			// 销毁豆子
			OtherActor->Destroy();
			// 检测一下豆子的数量是否为 0
			if (--CollectablesToEat == 0)
			{
				GameMode->SetCurrentState(EGameState::EWin);
			}
			UE_LOG(LogTemp, Warning, TEXT("Remain Collectable is %d"), CollectablesToEat);
		}
	}
}

GameMode 是定制游戏规则的地方

按下 P 键,所有的敌人都不能移动

GameMode 来管理所有的敌人

把敌人易受攻击的状态设置

单独一个一个设置太麻烦了

在GameMode 里面获得所有敌人

调用易受攻击的方法就可以了

GameModeBase 获得所有敌人,需要引入头文件:敌人

[展开全文]

使用默认的摄像机是第一人称

在【模式】里搜索Camera

 

设置默认的摄像机

选中 CameraActor ,点击【蓝图】---【打开关卡蓝图】

右键空白区域,选择【创建一个到CameraActor的引用】

获得了 摄像机 的引用

Get Player Controller

 

 

 

取消勾选【情景关联】

view target

Set view Target with Blend

 

绑定了:

与当前视野一致:

 

修改默认的面向:

修改PlayerStart

 

[展开全文]

AIEnemy.h

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

#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "Enemy.h"
#include "AIEnemy.generated.h"

/**
 * 
 */
UCLASS()
class PACMAN_API AAIEnemy : public AAIController
{
	GENERATED_BODY()
	
public:
	// 要重写的函数
	// 相当于beginplay,每当开启一个AI的时候调用,首先执行OnPossess
	void OnPossess(class APawn* InPawn) override;
	// 移动停止后调用
	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;
};

AIEnemy.cpp

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


#include "Public/AIEnemy.h"
#include "NavigationSystem.h"
#include "Public/TimerManager.h"

void AAIEnemy::OnPossess(class APawn* InPawn)
{
	Super::OnPossess(InPawn);
	// 获得Enemy,肉体
	Bot = Cast<AEnemy>(InPawn);
	HomeLocation = Bot->GetActorLocation();
	SearchNewPoint();
}

void AAIEnemy::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult & Result)
{
	if (!Bot->bIsDead)
	{
		SearchNewPoint();
	}
}

void AAIEnemy::SearchNewPoint()
{
	// 判断导航网格在不在
	UNavigationSystemV1* NavMesh = UNavigationSystemV1::GetCurrent(this);
	// 如果在
	if (NavMesh)
	{
		// 设置搜索半径
		const float SearchRadius = 1000.0f;
		// 设置一个点
		FNavLocation RandomPt;
		
		// 以敌人的位置为中心,这个半径为范围,找一个点,
		const bool bFound = NavMesh->GetRandomReachablePointInRadius(Bot->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();
}

Enemy.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Components/StaticMeshComponent.h"
#include "Enemy.generated.h"

UCLASS()
class PACMAN_API AEnemy : public ACharacter
{
public:	
	// 敌人是否死亡
	bool bIsDead;
};

设置导航网格:

模式---体积---Nav Mesh Bounds Volume

拖到场景中,设置 Brush Settings

选择视口,按下 P 键,显示导肮网格的样子

上面的能拐弯,有绿色的线

需要设置导航网格

编辑---项目设置---引擎--导航网格物体

把Generation下的 Cell Size 改成 5

Cell Height 改成 2

这样导航网格不会穿墙

绿色就是可以通过的路径

这些绿色就是导航网格的范围

 

使用 GetWorldTimerManager(),需要引入头文件:#include "Public/TimerManager.h"

[展开全文]
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "Enemy.h"
#include "AIEnemy.generated.h"

/**
 * 
 */
UCLASS()
class PACMAN_API AAIEnemy : public AAIController
{
	GENERATED_BODY()
	
public:
	// 要重写的函数
	// 相当于beginplay,每当开启一个AI的时候调用,首先执行OnPossess
	void OnPossess(class APawn* InPawn) override;
	// 移动停止后调用
	virtual void OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result) override;

	void SearchNewPoint();

private:
	class AEnemy* Bot;
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "Public/AIEnemy.h"
#include "NavigationSystem.h"

void AAIEnemy::OnPossess(class APawn* InPawn)
{
	Super::OnPossess(InPawn);
	// 获得Enemy,肉体
	Bot = Cast<AEnemy>(InPawn);
}

void AAIEnemy::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult & Result)
{
}

void AAIEnemy::SearchNewPoint()
{
	// 判断导航网格在不在
	UNavigationSystemV1* NavMesh = UNavigationSystemV1::GetCurrent(this);
	// 如果在
	if (NavMesh)
	{
		// 设置搜索半径
		const float SearchRadius = 1000.0f;
		// 设置一个点
		FNavLocation RandomPt;
		
		// 以敌人的位置为中心,这个半径为范围,找一个点,
		const bool bFound = NavMesh->GetRandomReachablePointInRadius(Bot->GetActorLocation(), SearchRadius, RandomPt);
		
		// 如果能找到
		if (bFound)
		{
			// 移动到这个点
			MoveToLocation(RandomPt);
		}
	}
}

注意:

Possess()使用OnPossess代替

使用UNavigationSystemV1*,需要引入头文件:#include "NavigationSystem.h"

[展开全文]
public:
	// 是否移动
	void SetMove(bool MoveIt);
	// 被杀死,回出生点
	void Killed();
	// 速度回到150
	void ReArm();

	UFUNCTION()
		void OnCollision(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);

private:	
	// 敌人是否死亡
	bool bIsDead;

void AEnemy::SetMove(bool MoveIt)
{
}

void AEnemy::Killed()
{
	if (bIsDead)
	{
		return;
	}

	bIsDead = true;

	GetCharacterMovement()->MaxWalkSpeed = 300.0f;
}

void AEnemy::ReArm()
{
	bIsDead = false;

	GetCharacterMovement()->MaxWalkSpeed = 150.0f;

	if (bIsVulnerable)
	{
		SetInVulnerable();
	}
}

void AEnemy::OnCollision(UPrimitiveComponent * HitComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
	// #include "Public/PacManCharacter.h"
	if (OtherActor->IsA(APacManCharacter::StaticClass()))
	{
		if (bIsVulnerable)
		{
			Killed();
		}
		else
		{
			APacManCharacter* PacMan = Cast<APacManCharacter>(OtherActor);
			PacMan->Killed();
		}
	}
}

 

[展开全文]

 

计时器的功能

当敌人处在易受到攻击的时候

不会一直处在易受攻击的状态

过了10秒之后返回正常的状态

这个时候需要10秒的计时

private:
	FTimerHandle TimeVulnerable;
	// 定义一个变量来判断当前的状态
	bool bIsVulnerable;
#include "Public/TimerManager.h"
#include "GameFramework/CharacterMovementComponent.h"
void AEnemy::SetVulnerable()
{
	// 吃到豆子可以增加10
	// 过一段时间可以不受攻击
	// 使用GetWorldTimerManager(),需要引入头文件:#include "Public/TimerManager.h"
	// 第一个参数是TimeVulnerable;第二个参数:作用在哪个类;第三个参数:作用于哪个函数;第四个参数:时间间隔;第五个参数:是否循环
	GetWorldTimerManager().SetTimer(TimeVulnerable, this, &AEnemy::SetInVulnerable, 10.0f, false);

	if (bIsVulnerable)
	{
		return;
	}
	bIsVulnerable = true;
	// 连续吃到豆子,敌人当前的状态已经设置成了易受攻击的状态
	// 没必要执行 EnemyBody->SetMaterial(0, VulnerableMaterial);
	// 定义一个变量来判断当前的状态,已经是易受到攻击的状态,没必要设置材质了
	// 时间段可以从10秒变成20秒外,剩下的都不需要再改变了
	EnemyBody->SetMaterial(0, VulnerableMaterial);

	// 使用GetCharacterMovement(),需要引入的头文件:#include "GameFramework/CharacterMovementComponent.h"
	GetCharacterMovement()->MaxWalkSpeed = 50.0f;
}
void AEnemy::SetInVulnerable()
{
	GetWorldTimerManager().ClearTimer(TimeVulnerable);
	bIsVulnerable = false;
	EnemyBody->SetMaterial(0, DefaultMaterial);
	GetCharacterMovement()->MaxWalkSpeed = 150.0f;
}

// 使用GetWorldTimerManager(),需要引入头文件:#include "Public/TimerManager.h"

// 使用GetCharacterMovement(),需要引入的头文件:#include "GameFramework/CharacterMovementComponent.h"

[展开全文]

// 使得碰撞体和圆柱体重合在一起

// CapsuleComponent 和 UStaticMeshComponent 重合

public:
     // 易受攻击
	void SetVulnerable();
	// 不易受攻击
	void SetInVulnerable();

private:
	// 默认材质
	class UMaterialInterface* DefaultMaterial;
	// 能被主角吃掉的时候的材质
	// 容易收到主角攻击
	class UMaterialInterface* VulnerableMaterial;

 

AEnemy::AEnemy()
{
	// 使得碰撞体和圆柱体重合在一起
	// CapsuleComponent 和 UStaticMeshComponent 重合
	EnemyBody->SetRelativeLocation(FVector(0, 0, -50));

 	static ConstructorHelpers::FObjectFinder<UMaterial> VulnerableMat(TEXT("'/Game/Materials.M_Enemy_Vulnerable"));
}

void AEnemy::BeginPlay()
{
	Super::BeginPlay();
	// 获得当前的材质
	DefaultMaterial = EnemyBody->GetMaterial(0);
}

void AEnemy::SetVulnerable()
{
	EnemyBody->SetMaterial(0, VulnerableMaterial);
	// 过一段时间可以不受攻击
}

void AEnemy::SetInVulnerable()
{
}

找内容浏览器下的文件

需要引入#include "UObject/ConstructorHelpers.h"

// 创建样子

// 找到对应的材质

static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderObj(TEXT("'/Game/StarterContent/Shapes/Shape_Cylinder'"));

 

static ConstructorHelpers::FObjectFinder<UMaterial> VulnerableMat(TEXT("'/Game/Materials.M_Enemy_Vulnerable‘"));

 

 

 

Vulnerable adj. 易受攻击的;易受伤害的;脆弱的;有局方的;有身价的

InVulnerable adj. 不能伤害的;刀枪不入的;无懈可击的;不可攻破的

[展开全文]

选中C++类,点击添加新项

新建C++类---继承自Character类---Enemy

#include "Public/Enemy.h"

 

新建C++类--- 需要勾选【显示所有类】里搜索---AIController

 

Enemy.h

Enemy.cpp

是肉体

AIEnemy.h

AIEnemy.cpp

是灵魂,用AI来控制

 

#include "Public/AIEnemy.h"

 

Enemy 脚本

继承自Character的类上有CapsuleComponent组件

只需要写控制样子的组件

 

Enemy.h

#include "Components/StaticMeshComponent.h"

UPROPERTY(VisibleAnywhere,Category=body)

UStaticMeshComponent* EnemyBody;

 

Enemy.cpp

#include "UObject/ConstructorHelpers.h"

 

AEnemey::AEnemy()

{

PrimaryActorTick.bCanEverTick = true;

EnemyBody=CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Body"));

 

static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderObj(TEXT("'/Game/StarterContent/Shapes/Shape_Cylinder'"));

 

if(CylinderObje.Succeeded())

{

EnemyBody->SetStaticMesh(CylinderObj.Object);

}

EnemyBody->SetRelativeScale3D(FVector(0.7f, 0.7f, 1.0f));

EnemyBody->SetupAttachment(RootComponent);

GetCapsuleComponent()->SetCapsuleRadius(40.0f);

GetCapsuleComponent()->SetCapsuleHalfHeight(50.0f);

}

 

// 使用GetCapsuleComponent,需要引入头文件:#include "Components/CapsuleComponent.h"

 

 

 

static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderObj(TEXT("'/Game/StarterContent/Shapes/Shape_Cylinder'"));

#include "Components/StaticMeshComponent.h"
public:	
	UPROPERTY(VisibleAnywhere,Category=Body)
	UStaticMeshComponent* EnemyBody;


#include "UObject/ConstructorHelpers.h"
#include "Components/CapsuleComponent.h"
AEnemy::AEnemy()
{
	// 显示组件
	EnemyBody = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Body"));
	// 创建样子
	// 找到对应的材质
	static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderObj(TEXT("'/Game/StarterContent/Shapes/Shape_Cylinder'"));
	// 成功找到就
	// Succeeded()需要加括号
	if (CylinderObj.Succeeded())
	{
		// 指定它的样子
		EnemyBody->SetStaticMesh(CylinderObj.Object);
	}
	// 设置它的大小
	EnemyBody->SetRelativeScale3D(FVector(0.7f, 0.7f, 1.0f));
	// 绑定到根组件上
	// AttachTo 过时了,换成:SetupAttachment
	EnemyBody->SetupAttachment(RootComponent);
	// 设置碰撞体的大小
	// 使用GetCapsuleComponent,需要引入头文件:#include "Components/CapsuleComponent.h"
	GetCapsuleComponent()->SetCapsuleRadius(40.0f);
	GetCapsuleComponent()->SetCapsuleHalfHeight(50.0f);
}

[展开全文]
private:
	// 初始的位置
	FVector StartPoint;
public:
	// 主角死亡
	void Killed();


void APacManCharacter::BeginPlay()
{
	Super::BeginPlay();
	// 初始化角色生命值,被怪物碰到,血量减一,回到出生点
	Lives = 3;
	// 获得游戏一开始的位置
	StartPoint = GetActorLocation();

	UE_LOG(LogTemp, Warning, TEXT("StartPoint:%s"), *StartPoint.ToString());
}


void APacManCharacter::Killed()
{
	if (--Lives == 0)
	{
		GameMode->SetCurrentState(EGameState::EGameOver);
	}
	else
	{
		SetActorLocation(StartPoint);
	}
}

 

[展开全文]
    // 最后2个参数可写可不写 
	// 需要注意的是 class AActor* otherActor ,碰撞的物体是不是敌人or食物
	// 新增:使用了反射机制,需要添加关键字 UFUNCTION()
	UFUNCTION()
	void OnCollision(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);
// PacManGameModeBase.h
public:
	// 但是由于我们的碰撞在,吃食物在,当前的状态是EPlaying,
    // 游戏中的时候才能吃食物,物体才会被销毁,把当前的状态设置成EPlaying,
    // 这样方便测试了
	virtual void BeginPlay() override;
 
 
 
 // PacManGameModeBase.cpp
 void APacManGameModeBase::BeginPlay()
{
	SetCurrentState(EGameState::EPlaying);
}

 

PacManCharacter.h

private:
	// 需要吃的 豆 的数量
	int CollectablesToEat;
	// 当前的生命值
	int Lives;

PacManCharacter.cpp

#include "Public/EngineUtils.h"

void APacManCharacter::BeginPlay()
{
	Super::BeginPlay();
	// 迭代器:
	// 使用TActorIterator 需要引入的头文件:#include "Public/EngineUtils.h"
	for (TActorIterator<ACollectables>CollectableItr(GetWorld()); CollectableItr; ++CollectableItr)
	{
		CollectablesToEat++;
	}
	UE_LOG(LogTemp, Warning, TEXT("Total Collectable is %d"), CollectablesToEat);
}



void APacManCharacter::OnCollision(UPrimitiveComponent * HitComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
	// 当游戏正在运行才去检测碰撞
	if (GameMode->GetCurrentState() == EGameState::EPlaying)
	{
		// 当碰撞的对象是 ACollectables 类时销毁掉
		// 使用自己定义的类AColletables,也需要引入对应的头文件: #include "Public/Collectables.h"
		if (OtherActor->IsA(ACollectables::StaticClass()))
		{
			OtherActor->Destroy();
			if (--CollectablesToEat == 0)
			{
				GameMode->SetCurrentState(EGameState::EWin);
			}
			UE_LOG(LogTemp, Warning, TEXT("Remain Collectable is %d"), CollectablesToEat);
		}
	}
}

// 使用TActorIterator 需要引入的头文件:#include "Public/EngineUtils.h"

[展开全文]

PacManCharacter.h

public:
    // 最后2个参数可写可不写 
	// 需要注意的是 class AActor* otherActor ,碰撞的物体是不是敌人or食物
	void OnCollision(class UPrimitiveComponent* HitComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);

PacManCharacter.cpp

#include "Public/Collectables.h"
#include "Components/CapsuleComponent.h"

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

	// 使用 GetCapsuleComponent() ,需要引入的头文件:#include "Components/CapsuleComponent.h"
	// 第一个参数是当前类,第二个参数是 要绑定的函数
	GetCapsuleComponent()->OnComponentBeginOverlap.AddDynamic(this, &APacManCharacter::OnCollision);
}

void APacManCharacter::OnCollision(UPrimitiveComponent * HitComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
	// 当游戏正在运行才去检测碰撞
	if (GameMode->GetCurrentState() == EGameState::EPlaying)
	{
		// 当碰撞的对象是 ACollectables 类时销毁掉
		// 使用自己定义的类AColletables,也需要引入对应的头文件: #include "Public/Collectables.h"
		if (OtherActor->IsA(ACollectables::StaticClass()))
		{
			OtherActor->Destroy();
		}
	}
}

 

主角是 CapsuleComponent的碰撞体

[展开全文]

 

使用 UGameplayStatics :: GetGameMode,需要引入的头文件是:#include "Kismet/GameplayStatics.h"

 

PacManCharacter.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "PacManGameModeBase.h"
#include "PacManCharacter.generated.h"

UCLASS()
class PACMAN_API APacManCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	APacManCharacter();

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 MoveXAxis(float AxisValue);
	void MoveYAxis(float AxisValue);
	// 重启游戏
	void ReStart();
	// 重玩一局游戏
	void NewGame();
	// 暂停游戏
	void Pause();
private:
	// 定义一个向量
	FVector CurrentVelocity;

	APacManGameModeBase* GameMode;
};

private:
    APacManGameModeBase* GameMode;

 

PacManCharacter.cpp

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


#include "Public/PacManCharacter.h"
#include "Components/InputComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/World.h"
// Sets default values
APacManCharacter::APacManCharacter()
{
 	// 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;

}

// Called when the game starts or when spawned
void APacManCharacter::BeginPlay()
{
	Super::BeginPlay();
	// 获得当前的游戏模式
	// 使用 UGameplayStatics 需要引入的头文件:#include "Kismet/GameplayStatics.h"
	// 需要强制转换,使用 Cast <APacManGameModeBase>()
	GameMode = Cast<APacManGameModeBase>(UGameplayStatics::GetGameMode(this));
}

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

}

// Called to bind functionality to input
void APacManCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	// 第一个参数,绑定的轴的名字,与编辑器里面对应的;
	// 第二个参数,this,绑定这个类上;
	// 第三个参数,绑定的哪个函数
	// InputComponent 报红,引入#include "Components/InputComponent.h"头文件解决
	// 传递的引用,需要加上 & 符号
	PlayerInputComponent->BindAxis("MoveX", this, &APacManCharacter::MoveXAxis);
	PlayerInputComponent->BindAxis("MoveY", this, &APacManCharacter::MoveYAxis);

	// 绑定行为,多了一个参数,IE_Pressed 按下的时候触发
	PlayerInputComponent->BindAction("NewGame", IE_Pressed, this, &APacManCharacter::NewGame);
	PlayerInputComponent->BindAction("ReStart", IE_Pressed, this, &APacManCharacter::ReStart);
	PlayerInputComponent->BindAction("Pause", IE_Pressed, this, &APacManCharacter::Pause);
}

void APacManCharacter::MoveXAxis(float AxisValue)
{
	CurrentVelocity.X = AxisValue;
	AddMovementInput(CurrentVelocity);
}

void APacManCharacter::MoveYAxis(float AxisValue)
{
	CurrentVelocity.Y = AxisValue;
	AddMovementInput(CurrentVelocity);
}
// 重启游戏
void APacManCharacter::ReStart()
{
	// 使用GetWorld()需要引入头文件:#include "Engine/World.h"
	GetWorld()->GetFirstPlayerController()->ConsoleCommand(TEXT("RestartLevel"));
}
// 重来一局游戏
void APacManCharacter::NewGame()
{
	if (GameMode->GetCurrentState() == EGameState::EMenu)
	{
		GameMode->SetCurrentState(EGameState::EPlaying);
	}
}
// 暂停游戏
void APacManCharacter::Pause()
{
	if (GameMode->GetCurrentState() == EGameState::EPlaying)
	{
		GameMode->SetCurrentState(EGameState::EPause);
	}
	else if (GameMode->GetCurrentState() == EGameState::EPause)
	{
		GameMode->SetCurrentState(EGameState::EPlaying);
	}
}

#include "Kismet/GameplayStatics.h"
#include "Engine/World.h"

 

void APacManCharacter::BeginPlay()
{
    Super::BeginPlay();
    // 获得当前的游戏模式
    // 使用 UGameplayStatics 需要引入的头文件:#include "Kismet/GameplayStatics.h"
    // 需要强制转换,使用 Cast <APacManGameModeBase>()
    GameMode = Cast<APacManGameModeBase>(UGameplayStatics::GetGameMode(this));
}

 

void APacManCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    // 第一个参数,绑定的轴的名字,与编辑器里面对应的;
    // 第二个参数,this,绑定这个类上;
    // 第三个参数,绑定的哪个函数
    // InputComponent 报红,引入#include "Components/InputComponent.h"头文件解决
    // 传递的引用,需要加上 & 符号
    PlayerInputComponent->BindAxis("MoveX", this, &APacManCharacter::MoveXAxis);
    PlayerInputComponent->BindAxis("MoveY", this, &APacManCharacter::MoveYAxis);

    // 绑定行为,多了一个参数,IE_Pressed 按下的时候触发
    PlayerInputComponent->BindAction("NewGame", IE_Pressed, this, &APacManCharacter::NewGame);
    PlayerInputComponent->BindAction("ReStart", IE_Pressed, this, &APacManCharacter::ReStart);
    PlayerInputComponent->BindAction("Pause", IE_Pressed, this, &APacManCharacter::Pause);
}

 

 

// 重启游戏
void APacManCharacter::ReStart()
{
    // 使用GetWorld()需要引入头文件:#include "Engine/World.h"
    GetWorld()->GetFirstPlayerController()->ConsoleCommand(TEXT("RestartLevel"));
}
// 重来一局游戏
void APacManCharacter::NewGame()
{
    if (GameMode->GetCurrentState() == EGameState::EMenu)
    {
        GameMode->SetCurrentState(EGameState::EPlaying);
    }
}
// 暂停游戏
void APacManCharacter::Pause()
{
    if (GameMode->GetCurrentState() == EGameState::EPlaying)
    {
        GameMode->SetCurrentState(EGameState::EPause);
    }
    else if (GameMode->GetCurrentState() == EGameState::EPause)
    {
        GameMode->SetCurrentState(EGameState::EPlaying);
    }
}

 

 

引擎- 输入

Actiong Mappings

NewGame 按键N

ReStart 按键 R

Pause 按键 P

[展开全文]

1

5个状态:

①当我们刚进入游戏的时候,给我们一个提示说,欢迎来到PacMan,menu菜单

②游戏中 的状态,

③游戏暂停,

④游戏胜利,

⑤GameOver

状态之间可以切换

按下P键,暂停

按下N键,重新开始游戏

 

状态放到 GameMode 的里面 PacManGameModeBase.cpp

GameMode 用来处理游戏规则的

 

在 PacManGaemModeBase.h 文件中新增

enum class EGameState : short {

EMenu,

EPlaying,

EPause,

EWin,

EGameOver

};

 

 

要知道当前状态,才能做出不同的反应

private:

// 要知道当前状态,才能做出不同的反应

EGameState currentState;

 

 

2个方法:获得当前状态,给当前状态设值

 

public:

// 获得当前状态 加 const 不能修改我们的成员

EGameState GetCurrentState() const;

// 设置当前状态

void SetCurrentState(EGameState value);

 

 

// 为了程序更加快捷,定义一个 内联

// APcManGameModeBase 和 命名的项目名称一致,视频中的AMyPacManGameModeBase,项目名称为:MyPacMan

FORCEINLINE EGameState APacManGameModeBase::GetCurrentState() const

{

return currentState;

};

 

 

去PacManGameModeBase.cpp 中实现

void APacManGameModeBase::SetCurrentState(EGameState value)

{

currentState = value;

}

 

 

在PacManCharacter.h 中

public:

void ReStart();

void NewGame();

void Pause();

 

 

在PacManCharacter.cpp 中

// 重启游戏

void APacManCharacter :: ReStart()

{

}

// 重来一局游戏

void APacManCharacter :: NewGame()

{

}

// 暂停游戏

void APacManCharacter :: Pause()

{

}

PacManGameModeBase.h

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

#pragma once

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

/**
 * 
 */
enum class EGameState : short {
	EMenu,
	EPlaying,
	EPause,
	EWin,
	EGameOver
};

UCLASS()
class PACMAN_API APacManGameModeBase : public AGameModeBase
{
	GENERATED_BODY()
public:
	// 获得当前状态 加 const 不能修改我们的成员
	EGameState GetCurrentState() const;
	// 设置当前状态
	void SetCurrentState(EGameState value);
private:
	// 要知道当前状态,才能做出不同的反应
	EGameState currentState;
};
// 为了程序更加快捷,定义一个 内联 
// APcManGameModeBase 和 命名的项目名称一致,视频中的AMyPacManGameModeBase,项目名称为:MyPacMan
FORCEINLINE EGameState APacManGameModeBase::GetCurrentState() const
{
	return currentState;
};

PacManGameModeBase.cpp

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


#include "PacManGameModeBase.h"
// 设置当前状态
void APacManGameModeBase::SetCurrentState(EGameState value)
{
	currentState = value;
}

PacManCharacter.h

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

#pragma once

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

UCLASS()
class PACMAN_API APacManCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	APacManCharacter();

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 MoveXAxis(float AxisValue);
	void MoveYAxis(float AxisValue);
	// 重启游戏
	void ReStart();
	// 重玩一局游戏
	void NewGame();
	// 暂停游戏
	void Pause();

	// 定义一个向量
	FVector CurrentVelocity;
};

PacManCharacter.cpp

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


#include "Public/PacManCharacter.h"
#include "Components/InputComponent.h"

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

}

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

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

}

// Called to bind functionality to input
void APacManCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	// 第一个参数,绑定的轴的名字,与编辑器里面对应的;
	// 第二个参数,this,绑定这个类上;
	// 第三个参数,绑定的哪个函数
	// InputComponent 报红,引入#include "Components/InputComponent.h"头文件解决
	// 传递的引用,需要加上 & 符号
	PlayerInputComponent->BindAxis("MoveX", this, &APacManCharacter::MoveXAxis);
	PlayerInputComponent->BindAxis("MoveY", this, &APacManCharacter::MoveYAxis);

}

void APacManCharacter::MoveXAxis(float AxisValue)
{
	CurrentVelocity.X = AxisValue;
	AddMovementInput(CurrentVelocity);
}

void APacManCharacter::MoveYAxis(float AxisValue)
{
	CurrentVelocity.Y = AxisValue;
	AddMovementInput(CurrentVelocity);
}
// 重启游戏
void APacManCharacter::ReStart()
{
}
// 重来一局游戏
void APacManCharacter::NewGame()
{
}
// 暂停游戏
void APacManCharacter::Pause()
{
}

 

[展开全文]

正黄色:

RGB={1,1,0}

设置GameMode,定制游戏规则的一个类:

先找到C++类下,MyPacMan文件夹下的 MyPacManGameModeBase

双击 MyPacManGameModeBase 进入代码编辑

以 GameMode 为父类 ,创建一个基于它的C++蓝图类

右键MyPacManGameModeBase,【创建基于MyPacManGameModeBase的蓝图类】

命名您的新 MyPacManGameModeBase

蓝图类命名加前缀 BP_

保存位置在BluePrints文件夹下

/*

此项目创建的名称为MyPacMan

我的项目创建的名称为:PacMan

所有没有My

但是引擎好像默认就加上了My,疑问???

*/

创建后:自动打开蓝图编辑器:

Default Pawn Class 就是 默认创建的角色

修改成,我们创建的BP_PacManCharacter

修改后,先编辑,再保存

修改默认的地图模式

Default Modes

Unreal 编辑器---【编辑】---【项目设置】---【项目-- 地图&模式】

选择我们刚刚创建的 BP_MyPacManGameModeBase

Player Start 用来确定主角刚开始的位置的

删除了 Player Start ,系统默认生成主角的位置在(0,0,0)

 

不小心删除掉了 Player Start ,在【模式】--【基本】---【玩家起始】拖到 关卡 中

Player Start 报BADsize,是因为碰撞体 和 其他碰撞体重合了

移动到不重合的地方就没有报 BADsize

淡蓝色的箭头代表生成的主角 正对的方向:

前后键控制Y轴的移动

把Y轴当成它的正方向

却发现X轴是它的正方向

Player Start 和 BP_PacManCharacter 也是把X轴当成了 正方向

修改前:

修改后:

X轴是前后

Y轴是左右

 

2个碰撞体一碰就不能往前走了

[展开全文]

授课教师

SIKI学院老师

课程特色

图文(1)
下载资料(1)
视频(30)

学员动态