Epic 官方视频教程《 Battery Collector》源码+超详细注释【C++】【UE4】

视频教程链接:

优酷链接

油管链接 【需要梯子】


首先来看效果图:

1. 电池随机从天空掉落,玩家按C键收集电池的能量(闪电粒子效果)来补充血条(每秒都会自动衰减)



2.玩家的颜色随着血条的减少,逐渐变黑



3.当血条为空时,玩家(黑色的那一坨)死亡,游戏结束;如果玩家提前集满血条则获胜。



以下是完整的源代码,并配套详细解释:


Pickup.h

[cpp] view plain copy
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #pragma once// 防止多次引用头文件  
  4.   
  5. #include "GameFramework/Actor.h"  
  6. #include "Pickup.generated.h"   // 必须是最后 include 的头文件,它是UHT(Unreal Header Tool)根据你声明的宏自动生成的  
  7. // 该宏将类暴露给 Unreal 的反射系统,允许我们在运行时检查和迭代对象的属性(比如 GC 中的对对象引用计数的管理)  
  8. // 1. 意味着当你创建一个对象,UE4会帮助你进行内存管理(智能指针来自动对垃圾内存进行回收),但前提是遵循了 UE4 的构造/销毁规范(比如自己手动 new 出的对象就不能被 UE4 回收);  
  9. // 2. 默认使得该类可以被编辑器和蓝图访问;  
  10. // 3. 如果将 Bluerpintable 改为 Blueprinttype,那么该类在蓝图中就只能作为 variable 访问了。(还有 NotBlueprintType 类型;Blueprintable 和 BlueprintType 兼容)  
  11. UCLASS(Blueprintable)   
  12. class BATTERYCOLLECTOR_API APickup : public AActor // BATTERYCOLLECTOR_API也是由UHT创建的宏,确保该类正确输出到 DLL 中  
  13. {  
  14.     GENERATED_BODY()// 导入一些 UE 系统需要的自动生成的方法。与 GENERATED_CLASS_BODY 的区别 http://blog.csdn.net/a359877454/article/details/52511893  
  15.   
  16.     // 为类声明一个 Log Category  
  17.     DECLARE_LOG_CATEGORY_CLASS(Pickup, Log, All);  
  18.   
  19. public:   
  20.     // Sets default values for this actor's properties  
  21.     APickup();  
  22.   
  23.     // Called when the game starts or when spawned  
  24.     virtual void BeginPlay() override;  
  25.       
  26.     // Called every frame  
  27.     virtual void Tick( float DeltaSeconds ) override;  
  28.   
  29.     // 返回 pickup 模型(注意,内联方法和UFUNCTION不兼容)  
  30.     FORCEINLINE class UStaticMeshComponent* GetMesh() const { return PickupMesh; }  
  31.   
  32.     // BlueprintPure / BlueprintCallable 表示两者都可以从蓝图端被调用,  
  33.     // BlueprintPure 意味着该方法不会修改成员数据,且只要 output pin 被使用其就会执行(它也没有exec input pin);  
  34.     // 但 BlueprintCallable 的执行需要连接exec input pin ,然后结果由 output pin 输出。  
  35.     UFUNCTION(BlueprintPure, Category = "Pickup")  
  36.     bool IsActive();  
  37.   
  38.     // 安全地修改 bIsActive 成员【UFUNCTION 的好处:1.通过添加一些额外的代码,使其可以被蓝图重写;2.在游戏运行时,可以通过命令行来调用,便于调试】  
  39.     UFUNCTION(BlueprintCallable, Category = "Pickup")  
  40.     void SetActive(bool NewPickupState);  
  41.   
  42.     // 当 pickup 被收集时需要调用的方法  
  43.     // BlueprintNativeEvent:表示该方法既在 C++ 中定义一些行为,也可以被蓝图中定义一些行为 (C++方法为蓝图同名方法的父方法)  
  44.     // 注意和 BlueprintImplementableEvents 的区别(既可以通知蓝图有C++层的事件发生,还可以为它额外提供一些信息)  
  45.     UFUNCTION(BlueprintNativeEvent)  
  46.     void WasCollected();  
  47.     virtual void WasCollected_Implementation(); // ❤  
  48.   
  49. protected:  
  50.     // pickup 是否被激活  
  51.     bool bIsActive;  
  52.   
  53. private:  
  54.     // 关卡中的可拾取物(静态模型)—— pickup  
  55.     // 1. VisibleAnywhere:表示 PickupMesh 属性在 Class Defaults 和它的实例(将蓝图拖动到关卡中) 中都是可见的,但不可编辑【 参见 search “放大镜”右侧的 "Open Selection in Property Matrix" 图标】  
  56.         // http://blog.csdn.net/xi_niuniu/article/details/54409648  
  57.         //   
  58.     // 2. 如果没有 AllowPrivateAccess 的话,BlueprintReadOnly 在 private 下就会编译失败(通常是 public),其作用使得 PickMesh 在蓝图编辑器中可以 Get 到  
  59.   
  60.     // 3. BlueprintReadOnly :表示在蓝图下只能 Get,不能 Set; BlueprintDefaultsOnly: 表示在运行前蓝图中的默认值是可以修改的,但运行中蓝图就不能修改它  
  61.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pickup", meta = (AllowPrivateAccess = "true"))  
  62.     class UStaticMeshComponent* PickupMesh;// Actor subobject  
  63.       
  64. };  

Pickup.cpp

[cpp] view plain copy
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #include "BatteryCollector.h"       // 切记放在第一个  
  4. #include "Pickup.h"  
  5.   
  6. DEFINE_LOG_CATEGORY_CLASS(APickup,  Pickup)  
  7. //#define _DEBUG_ 1  
  8.   
  9. // Sets default values  
  10. APickup::APickup()  
  11. {  
  12.     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
  13.     PrimaryActorTick.bCanEverTick = false// true  
  14.   
  15.     // 所有 pickup 默认为 true  
  16.     bIsActive = true;  
  17.   
  18.     // 创建一个静态模型  
  19.     PickupMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PickupMesh"));// subobject的名字为“PickupMesh”  
  20.     RootComponent = PickupMesh;  
  21. }  
  22.   
  23. // Called when the game starts or when spawned  
  24. void APickup::BeginPlay()  
  25. {  
  26.     Super::BeginPlay();  
  27.       
  28. }  
  29.   
  30. // Called every frame  
  31. void APickup::Tick( float DeltaTime )  
  32. {  
  33.     Super::Tick( DeltaTime );  
  34.   
  35. }  
  36.   
  37. bool APickup::IsActive()  
  38. {  
  39.     return bIsActive;  
  40. }  
  41.   
  42.   
  43. void APickup::SetActive(bool NewPickupState)  
  44. {  
  45.     bIsActive = NewPickupState;  
  46.   
  47. }  
  48.   
  49. void APickup::WasCollected_Implementation()  
  50. {  
  51. #ifdef _DEBUG_  
  52.     FString PickupDebugString = GetName();  
  53.     UE_LOG(Pickup, Warning, TEXT("You have collected %s"), *PickupDebugString);  
  54. #endif  
  55. }  

BatteryPick.h

[cpp] view plain copy
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #pragma once  
  4.   
  5. #include "Pickup.h"  
  6. #include "BatteryPickup.generated.h"  
  7.   
  8. /** 
  9.  *  
  10.  */  
  11. UCLASS()  
  12. class BATTERYCOLLECTOR_API ABatteryPickup : public APickup  
  13. {  
  14.     GENERATED_BODY()  
  15.       
  16. public:  
  17.     ABatteryPickup();  
  18.       
  19.     // BlueprintNativeEvent  
  20.     void WasCollected_Implementation() override;  
  21.   
  22.     // 获取 battery power(注意:此方法蓝图不可调用)  
  23.     float GetPower();  
  24.   
  25. protected:  
  26.     // BlueprintProtected:表示只有继承了这个类的蓝图才可以修改这个变量  
  27.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))  
  28.     float BatteryPower;  
  29.   
  30. };  

BatteryPick.cpp

[cpp] view plain copy
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #include "BatteryCollector.h"       // 切记放在第一个  
  4. #include "BatteryPickup.h"  
  5.   
  6. ABatteryPickup::ABatteryPickup()  
  7. {  
  8.     // 记得为 SM_Batter_Medium 设置 Collision  
  9.     GetMesh()->SetSimulatePhysics(true);  
  10.   
  11.     BatteryPower = 150.f;  
  12. }  
  13.   
  14. void ABatteryPickup::WasCollected_Implementation()  
  15. {  
  16.     // 调用父类的方法  
  17.     Super::WasCollected_Implementation();  
  18.     // 销毁 battery  
  19.     Destroy();// 相关方法:SetLifeSpan,允许在destroy方法调用之后,坚挺若干时间  
  20. }  
  21.   
  22. float ABatteryPickup::GetPower()  
  23. {  
  24.     return BatteryPower;  
  25. }  

SpawnVolume.h

[cpp] view plain copy
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #pragma once  
  4.   
  5. #include "GameFramework/Actor.h"  
  6. #include "SpawnVolume.generated.h"  
  7.   
  8. UCLASS()  
  9. class BATTERYCOLLECTOR_API ASpawnVolume : public AActor  
  10. {  
  11.     GENERATED_BODY()  
  12.       
  13.     DECLARE_LOG_CATEGORY_CLASS(SpawnVolume, Log, All);  // 为类声明一个 Log Category  
  14.   
  15. public:   
  16.     // Sets default values for this actor's properties  
  17.     ASpawnVolume();  
  18.   
  19.     // Called when the game starts or when spawned  
  20.     virtual void BeginPlay() override;  
  21.       
  22.     // Called every frame  
  23.     virtual void Tick( float DeltaSeconds ) override;  
  24.   
  25.     FORCEINLINE class UBoxComponent* GetWhereToSpawn() const { return WhereToSpawn; }  
  26.   
  27.     // 返回一个 BoxComponent 范围内的随机点  
  28.     // 注意:它不会改变 SpawnVolume,也不会改变 SpawnVolume 的行为  
  29.     UFUNCTION(BlueprintPure, Category = "Spawning")  
  30.     FVector GetRandomPointInVolume();  
  31.   
  32.     // 是否继续产生电池  
  33.     UFUNCTION(BlueprintCallable, Category = "Spawning")  
  34.     void SetSpawningActive(bool bShouldSpawn);  
  35.   
  36. protected:  
  37.     // 产生的 pickup,同时限制了蓝图上显示的类型必须是继承自 Pickup 类  
  38.     UPROPERTY(EditAnywhere, Category = "Spawning")  
  39.     TSubclassOf<class APickup> WhatToSpawn;// 具体可以参考 <UE4 Scripting with C++ Cookbook> p49 (NewObject<>, ConstructObject<>, ConditionalBeginDestroy)  
  40.         // 如果是自定义的C++类指针,且非UObject的派生类(已有引用计数),那么可以使用TSharedPtr, TWeakPtr, TAutoPtr(非线程安全)TScopedPointer 来自动管理内存  
  41.     // 计时器的句柄,可以用它来 cancel 定时器  
  42.     FTimerHandle SpawnTimer;  
  43.   
  44.     // 最小延迟  
  45.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning")  
  46.     float SpawnDelayRangeLow;  
  47.   
  48.     // 最大延迟  
  49.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning")  
  50.     float SpawnDelayRangeHigh;  
  51.   
  52. private:  
  53.     // Box 组件,指定 Pickup 在哪里产生  
  54.     // VisibleAnywhere  
  55.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Spawning", meta = (AllowPrivateAccess = "true"))  
  56.     class UBoxComponent* WhereToSpawn;  
  57.   
  58.     void SpawnPickup();  
  59.   
  60.     // 真实的延迟  
  61.     float SpawnDelay;  
  62. };  

SpawnVolume.cpp

[cpp] view plain copy
  1. // Fill out your copyright notice in the Description page of Project Settings.  
  2.   
  3. #include "BatteryCollector.h"       // 切记放在第一个  
  4.   
  5. #include "EngineGlobals.h"  
  6. #include "Runtime/Engine/Classes/Engine/Engine.h"  
  7. //#include "UnrealMathUtility.h"  
  8. #include "Kismet/KismetMathLibrary.h"  
  9.   
  10. #include "Pickup.h"  
  11. #include "SpawnVolume.h"  
  12.   
  13.   
  14. DEFINE_LOG_CATEGORY_CLASS(ASpawnVolume,  SpawnVolume)  
  15. //#define _DEBUG_ 1  
  16.   
  17. // Sets default values  
  18. ASpawnVolume::ASpawnVolume()  
  19. {  
  20.     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  
  21.     PrimaryActorTick.bCanEverTick = false;  
  22.   
  23.     WhereToSpawn = CreateDefaultSubobject<UBoxComponent>(TEXT("WhereToSpawn"));  
  24.     RootComponent = WhereToSpawn;  
  25.   
  26.     SpawnDelayRangeLow = 1.f;  
  27.     SpawnDelayRangeHigh = 4.5f;  
  28. }  
  29.   
  30. // Called when the game starts or when spawned  
  31. void ASpawnVolume::BeginPlay()  
  32. {  
  33.     Super::BeginPlay();  
  34. }  
  35.   
  36. // Called every frame  
  37. void ASpawnVolume::Tick( float DeltaTime )  
  38. {  
  39.     Super::Tick( DeltaTime );  
  40.   
  41. }  
  42.   
  43. FVector ASpawnVolume::GetRandomPointInVolume()  
  44. {  
  45.     FVector SpawnOrigin = WhereToSpawn->Bounds.Origin;  
  46.     FVector SpawnExtend = WhereToSpawn->Bounds.BoxExtent;  
  47.   
  48.     // ❤  
  49.     //FBox box = FBox::BuildAABB(SpawnOrigin, SpawnExtend);  
  50.     //FVector SpawnRand = FMath::RandPointInBox(box);  
  51.     FVector SpawnRand = UKismetMathLibrary::RandomPointInBoundingBox(SpawnOrigin, SpawnExtend);  
  52.   
  53. #ifdef _DEBUG_  
  54.     // Output Log  
  55.     //UE_LOG(SpawnVolume,   
  56.     //  Warning,   
  57.     //  TEXT("SpawnRand is (%3.2f, %3.2f, %3.2f)"),   
  58.     //  SpawnRand.X, SpawnRand.Y, SpawnRand.Z);  
  59.     // Screen Log  
  60.     GEngine->AddOnScreenDebugMessage(-1,   
  61.         5.f,   
  62.         FColor::Yellow,   
  63.         FString::Printf(TEXT("SpawnRand: x: %f, y: %f, z: %f"),   
  64.             SpawnRand.X, SpawnRand.Y, SpawnRand.Z));  
  65. #endif  
  66.     return SpawnRand;  
  67. }  
  68.   
  69. void ASpawnVolume::SpawnPickup()  
  70. {  
  71.     if (WhatToSpawn != NULL)  
  72.     {  
  73.         UWorld* const World = GetWorld();// 当前的 UWorld 实例  
  74.         if (World)  
  75.         {  
  76.             FActorSpawnParameters SpawnParams;  
  77.             SpawnParams.Owner = this;  
  78.             SpawnParams.Instigator = Instigator;  
  79.   
  80.             // 随机 pickup 的位置  
  81.             FVector SpawnLocation = GetRandomPointInVolume();  
  82.   
  83.             // 随机 pickup 的方向  
  84.             FRotator SpawnRotation;  
  85.             SpawnRotation.Pitch = FMath::FRand() * 360.f;   // 绕 Y 轴旋转 Right Axis  
  86.             SpawnRotation.Yaw = FMath::FRand() * 360.f; // 绕 Z 轴旋转 Up Axis  
  87.             SpawnRotation.Roll = FMath::FRand() * 360.f;    // 绕 X 轴旋转 Forward Axis  
  88.   
  89.             // 生产 pickup  
  90.             APickup* const SpawnedPickup = World->SpawnActor<APickup>(WhatToSpawn, SpawnLocation, SpawnRotation, SpawnParams);  
  91.   
  92.             // 重新随机一个延时  
  93.             SpawnDelay = FMath::FRandRange(SpawnDelayRangeLow, SpawnDelayRangeHigh);  
  94.             // 绑定一个延时方法到全局计时器(不循环执行)  
  95.             GetWorldTimerManager().SetTimer(SpawnTimer, this, &ASpawnVolume::SpawnPickup, SpawnDelay, false);  
  96.   
  97.         }  
  98.     }  
  99. }  
  100.   
  101. void ASpawnVolume::SetSpawningActive(bool bShouldSpawn)  
  102. {  
  103.     if (bShouldSpawn)  
  104.     {  
  105.         // 随机一个延时  
  106.         SpawnDelay = FMath::FRandRange(SpawnDelayRangeLow, SpawnDelayRangeHigh);  
  107.         // 绑定一个延时方法到全局定时器(不循环执行),即在 SpawnDelay 秒之后执行 SpawnPickup 方法  
  108.         GetWorldTimerManager().SetTimer(SpawnTimer, this, &ASpawnVolume::SpawnPickup, SpawnDelay, false);  
  109.     }  
  110.     else  
  111.     {  
  112.         // 清除定时器  
  113.         GetWorldTimerManager().ClearTimer(SpawnTimer);  
  114.     }  
  115. }  

BatteryCollectorGameMode.h

[cpp] view plain copy
  1. // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.  
  2. #pragma once  
  3. #include "GameFramework/GameModeBase.h"  
  4. #include "BatteryCollectorGameMode.generated.h"  
  5.   
  6. // 用于存储 gameplay 当前状态的枚举  
  7. UENUM(BlueprintType)  
  8. enum class EBatteryPlayState  
  9. {  
  10.     EPlaying,  
  11.     EGameOver,  
  12.     EWon,  
  13.     EUnknow  
  14. };  
  15.   
  16. UCLASS(minimalapi)  
  17. class ABatteryCollectorGameMode : public AGameModeBase  
  18. {  
  19.     GENERATED_BODY()  
  20.   
  21.     // 为类声明一个 Log Category  
  22.     DECLARE_LOG_CATEGORY_CLASS(BatteryCollectorGameMode, Log, All);  
  23.   
  24. public:  
  25.     ABatteryCollectorGameMode();        // GameMode类负责定义游戏的规则  
  26.   
  27.     // 该方法在构造方法之后,在 tick 方法之前执行,  
  28.     // 那时所有东西已经注册完毕了。  
  29.     // 确保执行该方法时 character 已经构建完成  
  30.     virtual void BeginPlay() override;  
  31.   
  32.     virtual void Tick(float DeltaTime) override;  
  33.   
  34.     UFUNCTION(BlueprintPure, Category = "Power")  
  35.     float GetPowerToWin() const;  
  36.   
  37.     // 获取当前的游戏状态  
  38.     UFUNCTION(BlueprintPure, Category = "Power")  
  39.     EBatteryPlayState GetCurrentState() const;  
  40.   
  41.     void SetCurrentState(EBatteryPlayState NewState);  
  42.   
  43. protected:  
  44.     // character 的 power 的衰减率  
  45.     //(可以通过设置不同的衰减率,设计不同难度的关卡,只需要切换GameMode即可)  
  46.     // 该属性只有Class Defulats Detail 窗口可以编辑,拖入关卡的蓝图实例 Detail 窗口无法编辑(如果可拖拽到关卡中)  
  47.     UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))  
  48.     float DecayRate;  
  49.   
  50.     UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))  
  51.     float PowerToWin;  
  52.   
  53.     // 用于 HUD 的 Widget 类(限制必须继承与 UUserWidget)  
  54.     UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))  
  55.     TSubclassOf<class UUserWidget> HUDWidgetClass;  
  56.   
  57.     // HUD 实例  
  58.     UPROPERTY() // 利用 GC 【GetWorld()->ForceGarbageCollection(true) 会强制 GC】  
  59.     class UUserWidget* CurrentWidget;     
  60.   
  61. private:  
  62.     // 记录当前游戏的状态  
  63.     EBatteryPlayState CurrentState;  
  64.     // 记录关卡中所有的 SpawnActor 【即使不想在蓝图中编辑,也最好声明为 UPROPERTY(),让UE4管理 TArray 的内存】  
  65.     TArray<class ASpawnVolume*> SpawnVolumeActors;  
  66.   
  67.     void HandleNewState(EBatteryPlayState NewState);  
  68.   
  69. };  

BatteryCollectorGameMode.cpp

[cpp] view plain copy
  1. // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.  
  2.   
  3. #include "BatteryCollector.h"   // 切记放在第一个  
  4.   
  5. #include "EngineGlobals.h"  
  6. #include "Runtime/Engine/Classes/Engine/Engine.h"  
  7. #include "Kismet/GameplayStatics.h"  
  8. #include "Runtime/UMG/Public/Blueprint/UserWidget.h"    // 不是 <Blueprint/UserWidget>  
  9.   
  10. #include "BatteryCollectorGameMode.h"  
  11. #include "BatteryCollectorCharacter.h"  
  12. #include "SpawnVolume.h"  
  13.   
  14. //#define _DEBUG_ 1  
  15. DEFINE_LOG_CATEGORY_CLASS(ABatteryCollectorGameMode, BatteryCollectorGameMode)  
  16.   
  17. ABatteryCollectorGameMode::ABatteryCollectorGameMode()  
  18. {  
  19.     PrimaryActorTick.bCanEverTick = true;   // ❤  
  20.   
  21.     // 将蓝图玩家类设为默认的 pawn class。 类似还有 ContructorHelpers::FObjectFinder,用于 Load Asset  
  22.     static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));  
  23.     if (PlayerPawnBPClass.Class != NULL)  
  24.     {  
  25.         DefaultPawnClass = PlayerPawnBPClass.Class;  
  26.     }  
  27.   
  28.     DecayRate = .01f;   // 0.01f;  
  29. }  
  30.   
  31. void ABatteryCollectorGameMode::BeginPlay()  
  32. {  
  33.     Super::BeginPlay();  
  34.   
  35.     // 查找所有的 Spawn Volume Actor  
  36.     TArray<AActor*> FoundActors;  
  37.     // GetWorld 返回缓存的世界指针;  
  38.     // 返回指定的类在世界中的所有 actor;  
  39.     // 该方法会遍历整个关卡,比较耗性能  
  40.     UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASpawnVolume::StaticClass(), FoundActors);  
  41.   
  42.     for (auto Actor : FoundActors)  
  43.     {  
  44.         // 如果转换成功,即该 Actor 是 SpawnVolumeActor 类型  
  45.         ASpawnVolume* SpawnVolumeActor = Cast<ASpawnVolume>(Actor);  
  46.         if (SpawnVolumeActor)  
  47.         {  
  48.             // 确保不会重复添加  
  49.             SpawnVolumeActors.AddUnique(SpawnVolumeActor);  
  50.         }  
  51.     }  
  52.   
  53.     // 初始设置游戏状态  
  54.     SetCurrentState(EBatteryPlayState::EPlaying);  
  55.   
  56.     ABatteryCollectorCharacter* MyCharacter = Cast<ABatteryCollectorCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));  
  57.     if (MyCharacter)  
  58.     {  
  59.         PowerToWin = MyCharacter->GetInitialPower() * 1.25f;  
  60.     }  
  61.   
  62.     // UMG  
  63.     if(HUDWidgetClass != nullptr)  
  64.     {  
  65.         CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), HUDWidgetClass);  
  66.         if (CurrentWidget != nullptr)  
  67.         {  
  68.             CurrentWidget->AddToViewport();  
  69.         }  
  70.     }  
  71. }  
  72.   
  73. void ABatteryCollectorGameMode::Tick(float DeltaTime)  
  74. {  
  75.     Super::Tick(DeltaTime);  
  76.   
  77. #ifdef _DEBUG_  
  78.     // Screen Log  
  79.     //GEngine->AddOnScreenDebugMessage(-1,   
  80.     //  5.f,   
  81.     //  FColor::Red,   
  82.     //  FString::Printf(TEXT("GameMode: %s"), TEXT("Tick")));  
  83.   
  84.         GEngine->AddOnScreenDebugMessage(-1,   
  85.         5.f,   
  86.         FColor::Red,   
  87.         FString::Printf(TEXT("GameMode: Tick")));  
  88. #endif  
  89.   
  90.     // 获取指定 index 的 player pawn,并转化为 ABatteryCollectorCharacter  
  91.     ABatteryCollectorCharacter* MyCharacter = Cast<ABatteryCollectorCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));  
  92.     if (MyCharacter)  
  93.     {  
  94.         // 如果玩家的能量已经集齐到一定值则胜利  
  95.         if (MyCharacter->GetCurrentPower() > PowerToWin)  
  96.         {  
  97.             SetCurrentState(EBatteryPlayState::EWon);  
  98.         }  
  99.         // 如果玩家能量为正  
  100.         else if (MyCharacter->GetCurrentPower() > 0.f)  
  101.         {  
  102.             // 玩家能量随时间衰减  
  103.             MyCharacter->UpdatePower(-DeltaTime*DecayRate*(MyCharacter->GetInitialPower()));  
  104.         }  
  105.         else  
  106.         {  
  107.             SetCurrentState(EBatteryPlayState::EGameOver);  
  108.         }  
  109.               
  110.     }  
  111. }  
  112.   
  113. float ABatteryCollectorGameMode::GetPowerToWin() const  
  114. {  
  115.     return PowerToWin;  
  116. }  
  117.   
  118. EBatteryPlayState ABatteryCollectorGameMode::GetCurrentState() const  
  119. {  
  120.     return CurrentState;  
  121. }  
  122.   
  123. void ABatteryCollectorGameMode::SetCurrentState(EBatteryPlayState NewState)  
  124. {  
  125.     CurrentState = NewState;  
  126.   
  127.     HandleNewState(CurrentState);  
  128. }  
  129.   
  130.   
  131. void ABatteryCollectorGameMode::HandleNewState(EBatteryPlayState NewState)  
  132. {  
  133.     switch (NewState)  
  134.     {  
  135.         case EBatteryPlayState::EPlaying:  
  136.         {  
  137.             for (ASpawnVolume* Volume : SpawnVolumeActors)  
  138.             {  
  139.                 Volume->SetSpawningActive(true);  
  140.             }  
  141.         }  
  142.         break;  
  143.         case EBatteryPlayState::EWon:  
  144.         {  
  145.             for (ASpawnVolume* Volume : SpawnVolumeActors)  
  146.             {  
  147.                 Volume->SetSpawningActive(false);  
  148.             }  
  149.         }  
  150.         break;  
  151.         case EBatteryPlayState::EGameOver:  
  152.         {  
  153.             for (ASpawnVolume* Volume : SpawnVolumeActors)  
  154.             {  
  155.                 Volume->SetSpawningActive(false);  
  156.             }  
  157.   
  158.             APlayerController* PlayerController = UGameplayStatics::GetPlayerController(this, 0);  
  159.             if (PlayerController)  
  160.             {  
  161.                 // 禁用部分输入,但不会隐藏玩家和 HUD  
  162.                 PlayerController->SetCinematicMode(truefalsefalsetruetrue);  
  163.             }  
  164.   
  165.             // 加入布娃娃系统  
  166.             // 必须确保它是 Chacater 而不是 Pawn,因为 Character 具有额外的动作和骨骼模型  
  167.             ACharacter* MyCharacter = UGameplayStatics::GetPlayerCharacter(this, 0);  
  168.             if (MyCharacter)  
  169.             {  
  170.                 // 使得玩家可以像布娃娃一样“摊倒”  
  171.                 //(确保 Mannequin/Character/Mesh/ 目录下的 SK_Mannequin 已创建了 Physics Asset)  
  172.                 // (确保 ThirdPersonCPP/Blueprints/ 目录下的 ThirdPersonCharacter,其中 Mesh 组件的 Collision 被激活 [自定义预设])  
  173.                 MyCharacter->GetMesh()->SetSimulatePhysics(true);  
  174.                 // 禁用跳跃动作  
  175.                 MyCharacter->GetMovementComponent()->MovementState.bCanJump = false;  
  176.             }  
  177.         }  
  178.         break;  
  179.         default:  
  180.         case EBatteryPlayState::EUnknow:  
  181.         {  
  182.             // 保留  
  183.         }  
  184.         break;  
  185.     }  
  186. }  

BatteryCollectorCharacter.h

[cpp] view plain copy
  1. // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.  
  2. #pragma once  
  3. #include "GameFramework/Character.h"  
  4. #include "BatteryCollectorCharacter.generated.h"  
  5.   
  6. UCLASS(config=Game)  
  7. class ABatteryCollectorCharacter : public ACharacter  
  8. {  
  9.     GENERATED_BODY()  
  10.   
  11.     /** Camera boom positioning the camera behind the character */  
  12.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))  
  13.     class USpringArmComponent* CameraBoom;  
  14.   
  15.     /** Follow camera */  
  16.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))  
  17.     class UCameraComponent* FollowCamera;  
  18.   
  19.     // CollectionSphere 组件  
  20.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Power, meta = (AllowPrivateAccess = "true"))  
  21.     class USphereComponent* CollectionSphere;  
  22.   
  23. public:  
  24.     ABatteryCollectorCharacter();  
  25.   
  26.     /** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */  
  27.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)  
  28.     float BaseTurnRate;  
  29.   
  30.     /** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */  
  31.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)  
  32.     float BaseLookUpRate;  
  33.   
  34.     // power 的 Getter 和 Setter  
  35.     UFUNCTION(BlueprintPure, Category = "Power")  
  36.     float GetInitialPower();  
  37.   
  38.     UFUNCTION(BlueprintPure, Category = "Power")  
  39.     float GetCurrentPower();  
  40.   
  41.     UFUNCTION(BlueprintCallable, Category = "Power")  
  42.     void UpdatePower(float PowerChange);  
  43.   
  44. protected:  
  45.   
  46.     /** Resets HMD orientation in VR. */  
  47.     void OnResetVR();  
  48.   
  49.     /** Called for forwards/backward input */  
  50.     void MoveForward(float Value);  
  51.   
  52.     /** Called for side to side input */  
  53.     void MoveRight(float Value);  
  54.   
  55.     /**  
  56.      * Called via input to turn at a given rate.  
  57.      * @param Rate  This is a normalized rate, i.e. 1.0 means 100% of desired turn rate 
  58.      */  
  59.     void TurnAtRate(float Rate);  
  60.   
  61.     /** 
  62.      * Called via input to turn look up/down at a given rate.  
  63.      * @param Rate  This is a normalized rate, i.e. 1.0 means 100% of desired turn rate 
  64.      */  
  65.     void LookUpAtRate(float Rate);  
  66.   
  67.     /** Handler for when a touch input begins. */  
  68.     void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);  
  69.   
  70.     /** Handler for when a touch input stops. */  
  71.     void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);  
  72.   
  73.     // APawn interface  
  74.     virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;  
  75.     // End of APawn interface  
  76.   
  77.     // 按下按键,收集一个在 collection sphere 范围内的 pickup  
  78.     UFUNCTION(BlueprintCallable, Category = "Pickups")  
  79.     void CollectPickups();  
  80.   
  81.     // character 初始的 power  
  82.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))  
  83.     float InitialPower;  
  84.   
  85.     // 玩家的速度因子  
  86.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))  
  87.     float SpeedFactor;  
  88.   
  89.     // 基础速度  
  90.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))  
  91.     float BaseSpeed;  
  92.   
  93.     // BlueprintImplementableEvent:定义一个蓝图事件, 意味着我们不用在 C++ 中定义该方法,交给蓝图去实现  
  94.     UFUNCTION(BlueprintImplementableEvent, Category = "Power")  
  95.     void PowerChangeEffect();  
  96.   
  97. private:  
  98.     // character 当前的 power  
  99.     UPROPERTY(VisibleAnywhere, Category="Power")  
  100.     float CharacterPower;  
  101.   
  102. public:  
  103.     /** Returns CameraBoom subobject **/  
  104.     FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }  
  105.   
  106.     /** Returns FollowCamera subobject **/  
  107.     FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }  
  108.   
  109.     // 返回 CollectionSphere 组件  
  110.     FORCEINLINE class USphereComponent* GetCollectionSphere() const { return CollectionSphere; }  
  111. };  

BatteryCollectorCharacter.cpp

[cpp] view plain copy
  1. // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.  
  2.   
  3. #include "BatteryCollector.h"       // 切记放在第一个  
  4.   
  5. #include "Kismet/HeadMountedDisplayFunctionLibrary.h"  
  6.   
  7. #include "BatteryCollectorCharacter.h"  
  8. #include "Pickup.h"  
  9. #include "BatteryPickup.h"  
  10.   
  11. //////////////////////////////////////////////////////////////////////////  
  12. // ABatteryCollectorCharacter  
  13.   
  14. ABatteryCollectorCharacter::ABatteryCollectorCharacter()  
  15. {  
  16.     // Set size for collision capsule  
  17.     GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);  
  18.   
  19.     // set our turn rates for input  
  20.     BaseTurnRate = 45.f;  
  21.     BaseLookUpRate = 45.f;  
  22.   
  23.     // Don't rotate when the controller rotates. Let that just affect the camera.  
  24.     bUseControllerRotationPitch = false;  
  25.     bUseControllerRotationYaw = false;  
  26.     bUseControllerRotationRoll = false;  
  27.   
  28.     // Configure character movement  
  29.     GetCharacterMovement()->bOrientRotationToMovement = true// Character moves in the direction of input...  
  30.     GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate  
  31.     GetCharacterMovement()->JumpZVelocity = 600.f;  
  32.     GetCharacterMovement()->AirControl = 0.2f;  
  33.   
  34.     // Create a camera boom (pulls in towards the player if there is a collision)  
  35.     CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));  
  36.     CameraBoom->SetupAttachment(RootComponent);  
  37.     CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character  
  38.     CameraBoom->bUsePawnControlRotation = true// Rotate the arm based on the controller  
  39.   
  40.     // Create a follow camera  
  41.     FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));  
  42.     FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation  
  43.     FollowCamera->bUsePawnControlRotation = false// Camera does not rotate relative to arm  
  44.   
  45.     // 创建 collection sphere 组件  
  46.     CollectionSphere = CreateDefaultSubobject<USphereComponent>(TEXT("CollectionSphere"));  
  47.     // 并将其添加到 RootComponent 上  
  48.     //CollectionSphere->AttachTo(RootComponent); // 过时的方法  
  49.     // https://forums.unrealengine.com/showthread.php?112644-4-12-Transition-Guide  
  50.     CollectionSphere->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform);  
  51.     // collection sphere 的半径范围  
  52.     CollectionSphere->SetSphereRadius(200.f);  
  53.   
  54.     // Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)  
  55.     // are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)  
  56.   
  57.     // 为 power 赋值  
  58.     InitialPower = 2000.f;  
  59.     CharacterPower = InitialPower;  
  60.   
  61.     SpeedFactor = .75f;  
  62.     BaseSpeed = 10.f;  
  63.   
  64.   
  65. }  
  66.   
  67. //////////////////////////////////////////////////////////////////////////  
  68. // Input  
  69.   
  70. void ABatteryCollectorCharacter::SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent)  
  71. {  
  72.     // Set up gameplay key bindings  
  73.     check(PlayerInputComponent);  
  74.     PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);  
  75.     PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);  
  76.   
  77.     // Collect 键盘响应事件的绑定(Action 用于短促的响应,Axis 用于持续的响应)  
  78.     PlayerInputComponent->BindAction("Collect", IE_Pressed, this, &ABatteryCollectorCharacter::CollectPickups);  
  79.   
  80.     PlayerInputComponent->BindAxis("MoveForward"this, &ABatteryCollectorCharacter::MoveForward);  
  81.     PlayerInputComponent->BindAxis("MoveRight"this, &ABatteryCollectorCharacter::MoveRight);  
  82.   
  83.     // We have 2 versions of the rotation bindings to handle different kinds of devices differently  
  84.     // "turn" handles devices that provide an absolute delta, such as a mouse.  
  85.     // "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick  
  86.     PlayerInputComponent->BindAxis("Turn"this, &APawn::AddControllerYawInput);  
  87.     PlayerInputComponent->BindAxis("TurnRate"this, &ABatteryCollectorCharacter::TurnAtRate);  
  88.     PlayerInputComponent->BindAxis("LookUp"this, &APawn::AddControllerPitchInput);  
  89.     PlayerInputComponent->BindAxis("LookUpRate"this, &ABatteryCollectorCharacter::LookUpAtRate);  
  90.   
  91.     // handle touch devices  
  92.     PlayerInputComponent->BindTouch(IE_Pressed, this, &ABatteryCollectorCharacter::TouchStarted);  
  93.     PlayerInputComponent->BindTouch(IE_Released, this, &ABatteryCollectorCharacter::TouchStopped);  
  94.   
  95.     // VR headset functionality  
  96.     PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &ABatteryCollectorCharacter::OnResetVR);  
  97. }  
  98.   
  99.   
  100. void ABatteryCollectorCharacter::OnResetVR()  
  101. {  
  102.     UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();  
  103. }  
  104.   
  105. void ABatteryCollectorCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)  
  106. {  
  107.     Jump();  
  108. }  
  109.   
  110. void ABatteryCollectorCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)  
  111. {  
  112.     StopJumping();  
  113. }  
  114.   
  115. void ABatteryCollectorCharacter::TurnAtRate(float Rate)  
  116. {  
  117.     // calculate delta for this frame from the rate information  
  118.     AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());  
  119. }  
  120.   
  121. void ABatteryCollectorCharacter::LookUpAtRate(float Rate)  
  122. {  
  123.     // calculate delta for this frame from the rate information  
  124.     AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());  
  125. }  
  126.   
  127. void ABatteryCollectorCharacter::MoveForward(float Value)  
  128. {  
  129.     if ((Controller != NULL) && (Value != 0.0f))  
  130.     {  
  131.         // find out which way is forward  
  132.         const FRotator Rotation = Controller->GetControlRotation();  
  133.         const FRotator YawRotation(0, Rotation.Yaw, 0);  
  134.   
  135.         // get forward vector  
  136.         const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);  
  137.         AddMovementInput(Direction, Value);  
  138.     }  
  139. }  
  140.   
  141. void ABatteryCollectorCharacter::MoveRight(float Value)  
  142. {  
  143.     if ( (Controller != NULL) && (Value != 0.0f) )  
  144.     {  
  145.         // find out which way is right  
  146.         const FRotator Rotation = Controller->GetControlRotation();  
  147.         const FRotator YawRotation(0, Rotation.Yaw, 0);  
  148.   
  149.         // get right vector  
  150.         const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);  
  151.         // add movement in that direction  
  152.         AddMovementInput(Direction, Value);  
  153.     }  
  154. }  
  155.   
  156. void ABatteryCollectorCharacter::CollectPickups()  
  157. {  
  158.     // 遍历所有覆盖区域内的 Actor,将它们存储进数组  
  159.     TArray<AActor *> CollectedActors;  
  160.     CollectionSphere->GetOverlappingActors(CollectedActors);  
  161.   
  162.     // 记录收集的电池的能量  
  163.     float CollectedPower = 0.f;  
  164.   
  165.     // 遍历数组  
  166.     for (int32 iCollected = 0; iCollected < CollectedActors.Num(); ++iCollected )  
  167.     {  
  168.         // 将 Actor 转化为 APickup  
  169.         APickup *const TestPickup = Cast<APickup>(CollectedActors[iCollected]);  
  170.         // 检查转化是否成功,还有  pickup 是否即将被销毁,是否是激活状态  
  171.         if (TestPickup && !TestPickup->IsPendingKill() && TestPickup->IsActive())  
  172.         {  
  173.             // 收集 pickup(注意:此处不是调用的 xxx_Implementation)  
  174.             TestPickup->WasCollected();  
  175.   
  176.             // 检查 pickup 是否是电池  
  177.             ABatteryPickup* const TestBattery = Cast<ABatteryPickup>(TestPickup);  
  178.             if (TestBattery)  
  179.             {  
  180.                 // 累加收集的能量  
  181.                 CollectedPower += TestBattery->GetPower();  
  182.             }  
  183.   
  184.             // 冻结 pickup  
  185.             TestPickup->SetActive(false);  
  186.         }  
  187.     }  
  188.   
  189.     if (CollectedPower > 0.f)  
  190.     {  
  191.         UpdatePower(CollectedPower);  
  192.     }  
  193.   
  194. }  
  195.   
  196. float ABatteryCollectorCharacter::GetInitialPower()  
  197. {  
  198.     return InitialPower;  
  199. }  
  200.   
  201. float ABatteryCollectorCharacter::GetCurrentPower()  
  202. {  
  203.     return CharacterPower;  
  204. }  
  205.   
  206. void ABatteryCollectorCharacter::UpdatePower(float PowerChange)  
  207. {  
  208.     CharacterPower = CharacterPower + PowerChange;  
  209.     // 所有 Character类都有 GetCharacterMovement 方法  
  210.     // 根据 power 更新玩家的速度  
  211.     GetCharacterMovement()->MaxWalkSpeed = BaseSpeed + SpeedFactor * CharacterPower;  
  212.   
  213.     // 调用蓝图实现的方法  
  214.     PowerChangeEffect();  
  215. }  

以及 蓝图 的设计


Battery_BP



ThirdPersonCharacter 的 Construction Scirpt

注意:该材质是动态材质实例(Dynamic Material Instance),可以实时改变

Construction Script 是一种当绑定对象上的属性发生变化就会执行的脚本(不包括关卡中的移动),和 C++ 中的构造函数有点相似



ThirdPersonCharacter



BatteryHUD



附 

UE_LOG 的使用方法

字符串处理 

为什么要用 TSubclassOf

(完)


展开阅读全文

没有更多推荐了,返回首页