前兩天我們完成了環境設定與專案初始化。這裡有一個特別要說明的地方:
我使用的是 Unreal Engine 5.6 的 Third Person Template (C++)。
和舊版不同,這個模板已經自帶了:
基礎的角色控制
簡單近戰與連擊系統
敵人 AI
基本 UI
範例地圖
幾乎涵蓋了本系列挑戰中規劃的核心要素。
所以這個 30 天挑戰的重點,不是「從零開始重造輪子」,而是 解析 → 修改 → 擴充。
換句話說,這更貼近真實遊戲開發:大多時候我們都是 接手一個現有專案,然後改造成自己的遊戲。
今天要做的第一步,就是來「讀懂角色類別」,尤其是角色怎麼透過 C++ 綁定輸入,讓我們能控制移動、跳躍與攻擊。
專案建立後,我們要修改/新增的 Code 都在 Games\YourProjectName\Source
底下
(我的遊戲專案叫 iThome30days 所以是 Games\iThome30days\Source
)
在 Unreal 的 C++ 專案中,跟一般 C++ 專案一樣,每個類別通常由兩個檔案組成:
.h
(Header).cpp
(Source).h
裡使用宏(Macro),例如 UCLASS
、UPROPERTY
、UFUNCTION
,讓編輯器和藍圖可以識別類別及其成員。如果參考 Unreal Engine 的 C++ 官方文件 可以學得更扎實。
Macro | 作用 | 用法 / 說明 |
---|---|---|
UCLASS |
讓 UE5 識別 C++ 類別 | 放在類別前面,讓編輯器能看到類別,可設置 abstract 、config 等屬性 |
GENERATED_BODY() |
生成 UE5 需要的額外程式碼 | 每個 UCLASS 裡必須有,用於序列化、藍圖支援 |
UPROPERTY |
讓變數在 UE5 可見 | 可設置 VisibleAnywhere 、EditAnywhere 、BlueprintReadOnly 等屬性 |
UFUNCTION |
讓函式可在藍圖呼叫或覆寫 | 常用 BlueprintCallable (可在藍圖呼叫)或 BlueprintNativeEvent (可在藍圖覆寫) |
FORCEINLINE |
強制編譯器內聯函式 | 用於簡單 getter,提高效能 |
iThome30dayCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Logging/LogMacros.h"
#include "iThome30daysCharacter.generated.h"
#pragma once
:避免重複 include。CoreMinimal.h
:UE5 常用型別、Macro 的基礎標頭。GameFramework/Character.h
:引入 ACharacter
,我們的 Class 要繼承它。Logging/LogMacros.h
:用來建立與使用 UE_LOG
。generated.h
:UE 反射系統必須最後 include,讓編譯器自動生成必要程式碼。class USpringArmComponent;
class UCameraComponent;
class UInputAction;
struct FInputActionValue;
只告訴編譯器「有這些型別存在」,不用完整 include,減少編譯依賴。
DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);
宣告一個 log category,方便用 UE_LOG(LogTemplateCharacter, ...)
來輸出訊息。
UCLASS(abstract)
class AiThome30daysCharacter : public ACharacter
{
GENERATED_BODY()
...
UCLASS(abstract)
:讓 UE5 反射系統識別這是個類別,並標記為抽象類別(不可直接 spawn,需要子類別)。GENERATED_BODY()
:UE 必須有,用來插入反射系統產生的程式碼。UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
USpringArmComponent* CameraBoom;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
UCameraComponent* FollowCamera;
UPROPERTY
:讓變數可以被序列化、在編輯器/藍圖顯示。VisibleAnywhere
:只能查看,不能直接修改。BlueprintReadOnly
:藍圖可讀取。再下面還有輸入行為:
UPROPERTY(EditAnywhere, Category="Input")
UInputAction* JumpAction;
EditAnywhere
:允許在編輯器調整。AiThome30daysCharacter();
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
UFUNCTION(BlueprintCallable, Category="Input")
virtual void DoMove(float Right, float Forward);
UFUNCTION(BlueprintCallable)
:允許藍圖呼叫。DoMove
/ DoLook
/ DoJumpStart
/ DoJumpEnd
:將輸入實際作用到角色上。iThome30dayCharacter.cpp
AddMovementInput(Direction, Value);
ACharacter
Jump();
StopJumping();
ACharacter
bUseControllerRotationYaw = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
USpringArmComponent* CameraBoom;
UCameraComponent* FollowCamera;
UInputAction* MoveAction;
UInputAction* JumpAction;
UInputAction* LookAction;
SetupPlayerInputComponent()
配合Move()
,JumpAction 會呼叫 DoJumpStart()
UFUNCTION(BlueprintCallable)
void DoMove(float Right, float Forward);
UE_LOG(LogiThome30days, Error, TEXT("'%s' Failed to find an Enhanced Input component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));
UE_LOG(日誌分類, 日誌級別(
Log, Warning, Error
), 輸出字串(用TEXT()包起來
), ...)
今天我們快速解析了角色程式碼與 API 的核心結構,從輸入綁定到藍圖互動都有了基礎理解。
明天將進一步探討 相機 & 移動系統,包含鏡頭控制、嘗試修改 FOV,並加入衝刺功能,讓角色操控更貼近實際遊戲體驗。