Puzzle 拼图游戏目录
UE5 游戏模板 —— Puzzle 拼图游戏
Puzzle 作为最简单的UE提供的模板项目,开始我们第一个UE项目的学习!
1、GameMode
GameMode在网络游戏中,只会在服务端存在,服务端权威。
而在单机游戏或p2p这种本地既是服务器又是客户端的情况客户端也会存在。
虚幻引擎的架构设计是统一的,无论单机还是多人游戏,都共享同一套底层框架:
核心任务流程
1.定义游戏规则和流程 (Primary Game Rules & Flow):
- 这是GameMode最核心的任务。
开始游戏(BeginPlay/StartMatch): 决定游戏何时正式开始(例如,等待玩家准备完毕?倒计时结束?)。
进行游戏(InProgress): 管理游戏过程中的规则和主要状态。
结束游戏(EndMatch): 决定游戏何时以及为何结束(例如,时间耗尽、玩家达成目标、所有玩家失败)。
处理胜负/结果(HandleMatchHasEnded): 根据自定义逻辑(如分数、存活状态、任务完成度)判断哪一方(玩家、队伍)获胜或游戏结果,并处理结算逻辑。
重新开始游戏(ResetLevel / RestartGame): 控制一轮结束后如何重置游戏状态,让玩家重新开始。
2.设置和初始化默认 Actor (Default Actor Setup):
- 默认 Pawn 类 (DefaultPawnClass): 指定玩家出生时控制的默认角色类型(如人类角色、载具)。
玩家控制器类 (PlayerControllerClass): 指定玩家控制器类,处理玩家输入和玩家逻辑。
游戏状态类 (GameStateClass): 指定GameState类,用于在服务器和所有客户端之间同步游戏的整体状态信息(如游戏阶段、得分、剩余时间)。GameMode负责创建和管理GameState实例。
HUD 类 (HUDClass): 指定用于绘制游戏内 UI 的 HUD 类(注意:通常在 PlayerController 中具体处理)。
旁观者类 (SpectatorClass): 指定玩家在旁观模式下使用的 Pawn 类型。
3.管理玩家登录/登出 (Player Login/Logout):
- PlayerController实例的创建。
PlayerState实例的创建和管理(用于同步单个玩家的状态,如名称、分数)。
Pawn的初始生成:当玩家加入游戏时,GameMode负责调用SpawnDefaultPawnFor或自定义逻辑生成玩家的初始角色。
处理玩家退出时的清理工作(销毁对应的PlayerController、PlayerState、Pawn)。
4.生成点管理 (Spawn Points):
- 选择玩家出生位置: GameMode通常通过调用FindPlayerStart函数来决定玩家生成在哪个PlayerStart(重生点)上。你可以重写此函数来实现自定义的出生点选择逻辑(如团队区分、避免重生在敌人眼前、随机选择等)。
生成玩家 Pawn: 如上所述,在玩家登录或重生时,负责实际创建玩家Pawn的实例。
5.多人游戏特定规则 (Multiplayer):
- 定义最大玩家数 (MaxPlayers): 设置服务器允许的最大玩家数量。
等待玩家连接 (bDelayedStart): 控制是否在达到最小玩家人数或等待时间结束前才开始游戏。
无缝旅行(Sesamless Travel)管理: 处理地图切换时的玩家状态保存和恢复逻辑。
6.配置游戏参数 (Game Configuration):
- 可以通过GameMode类的属性或蓝图来配置各种游戏参数,如每回合时长、初始生命值、分数规则、是否允许暂停、团队设置等。这些参数可以影响GameState中同步的数据或GameMode本身的规则逻辑。
7.蓝图扩展和事件绑定 (Blueprint Extensibility):
- 虽然核心逻辑在C++中定义,但GameMode及其子类通常暴露给蓝图,允许设计师和程序员在蓝图中更便捷地:
覆盖关键虚拟函数(如HandleStartingNewPlayer, PostLogin, Logout, Killed)。
响应引擎发出的事件(如OnMatchStateSet当游戏阶段改变时)。
调用自定义函数来实现游戏特定的规则。
访问和修改GameState中的同步数据。
在实际的Puzzle Game中,我们指定了默认的Pawn和PlayerController
- 这种指定的优势是直接用了C++的类型去指定很快捷
- 但是这种指定缺少了可拓展性,我们通常使用派生自C++的子类蓝图去指定
这种方式通常比上面的方式好一些,可以指定派生类蓝图
2、PlayerController
PlayerController是个关键类,尤其在处理玩家输入和客户端逻辑方面。需要区分单机和多人模式下的不同职责:在多人游戏中,PlayerController实例会同时存在于服务器和客户端,这点和GameMode很不同。
核心职责
1. 输入处理
- 接收玩家输入(键盘、鼠标、手柄等),转化为游戏内的操作指令(移动、跳跃、射击等)。
- 通过重写 SetupInputComponent() 绑定输入事件。
void AMyPlayerController::SetupInputComponent() {
Super::SetupInputComponent();
InputComponent->BindAxis("MoveForward", this, &AMyPlayerController::MoveForward);
InputComponent->BindAction("Fire", IE_Pressed, this, &AMyPlayerController::HandleFire);
}
2. 视角控制
- 管理玩家摄像机(如第一/第三人称视角切换、镜头旋转)。
- 通过 SetViewTarget() 动态切换摄像机目标(如过场动画切镜头)。
3. 视角控制
- 通过 Possess() 控制特定Pawn(角色/载具):
APlayerController* PC = GetWorld()->GetFirstPlayerController();
PC->Possess(MyCharacter); // 接管角色控制权
- 死亡后通过 GameMode 调用 RestartPlayer() 重新生成并接管新Pawn。
4. 用户界面管理
- 创建并管理HUD(抬头显示器)、UMG控件(如菜单、血条)。
- 示例:显示交互提示:
void AMyPlayerController::ShowInteractionPrompt(FString Text) {
if (InteractionWidget) InteractionWidget->SetPromptText(Text);
}
5. 多人游戏网络同步
- 客户端(Client):发送输入指令到服务器。
- 服务器(Server):验证并执行指令(防作弊)。
- 使用RPC(远程过程调用)同步操作。
// 客户端调用RPC(在服务器执行)
UFUNCTION(Server, Reliable)
void Server_RequestShoot();
简单来说
回到项目中是这样的,在构造函数中初始了鼠标和点击事件
bEnableClickEvents = true;
bEnableTouchEvents = true;
这两个设置为true主要目的是下图中的绑定点击事件
3、游戏中的Actor
拼图块
在拼图块的构造函数中,主要进行了资源的静态加载和创建组件对组件进行初始化数值
其余的函数主要是设置点击后的材质切换,注意这里的材质需要加上UPROPERTY 是因为避免材质被主动释放,加上属性宏标签防止释放。这里还有一个细节为什么一个是UMaterial 其余的是UMaterialInstance 是因为,UMaterialInstance是材质实例,是由UMaterial转换过来的,只需要修改颜色值即可。
拼图管理器
用于创建管理拼图块
4、Pawn
Pawn可以是人、动物、NPC、等… …
Pawn 自然有些游戏行为去操纵,在PuzzleGame这个例子中的输入都由Pawn进行,在Pawn中主要是发射射线检测碰撞。
DeprojectMousePositionToWorld 函数将鼠标的位置转换成世界坐标,返回世界坐标和方向我们根据这两个值计算出终点值进行射线检测碰撞
顺带说一下UE的旧版Input系统的赋值,后续的FirstPersonGame会说新版的输入系统