视频教程链接:

优酷链接

油管链接 【需要梯子】

首先来看效果图:

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

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

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

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

Pickup.h

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

Pickup.cpp

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

BatteryPick.h

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

BatteryPick.cpp

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

SpawnVolume.h

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

SpawnVolume.cpp

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

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

BatteryCollectorGameMode.cpp

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

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

BatteryCollectorCharacter.cpp

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

以及 蓝图 的设计

Battery_BP

ThirdPersonCharacter 的 Construction Scirpt

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

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

ThirdPersonCharacter

BatteryHUD

UE_LOG 的使用方法

字符串处理

为什么要用 TSubclassOf

(完)

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

  1. android:自己动手编译Android源码(超详细)

    自己动手编译Android源码(超详细) 涅槃1992 2016.06.20 02:12* 字数 4330 阅读 86819评论 89喜欢 339赞赏 7 在Android Studio代码调试一文中 ...

  2. 基础IO(文件接口、安装内核源码超详细步骤图解、静态库与动态库)

    基础IO C语言的文件操作接口 fopen fclose fread fwrite fseek 系统调用文件接口 open close read write lseek 安装内核源码 文件描述符&am ...

  3. Android Studio App开发之网络通信中使用POST方式调用HTTP接口实现应用更新功能(附源码 超详细必看)

    运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一.POST方式调用HTTP接口 POST方式把接口地址与请求报文分开,允许使用自定义的报文格式,由此扩大了该方式的应用场景.POST请求与GET ...

  4. OpenCV中拆分通道、合并通道、alpha通道的讲解及实战演示(附python源码 超详细)

    需要源码和图片请点赞关注收藏后评论区留言私信~~~ 在BGR色彩空间中,图像的通道由B通道.G通道和B通道构成,下面将介绍OpenCV提供的方法拆分和合并通道 一.拆分通道 为了拆分图像中的通道 Op ...

  5. LSTM神经网络实现对股市收盘价格的预测实战(python实现 附源码 超详细)

    源码或数据集请点赞关注收藏后评论区留言或者私信博主要 由于独特的设计结构 LSTM适合于处理和预测时间序列中间隔和延迟非常长的重要事件. LSTM是一种含有LSTM区块(blocks)或其他的一种类神 ...

  6. Android App开发实战项目之购物车(附源码 超详细必看)

    需要源码请点赞关注收藏后评论区留言~~~ 一.需求描述 电商App的购物车可谓是司空见惯了,可以知道购物车除了底部有一个结算行,其余部分主要是已加入购物车的商品列表,然后每个商品左边是商品小图,右边是 ...

  7. Android Studio App开发入门之在活动之间传递消息(附源码 超详细必看)(包括显示和隐式Intent,向上一个和下一个Activity发送数据)

     运行有问题或需要源码请点赞关注收藏后评论区留言~~ 显示Intent和隐式Intent Intent是各个组件之间的信息沟通的桥梁,既能在Activity之间沟通,又能在Activity与Servi ...

  8. 【Android App】物联网中指南针、计步器、感光器、陀螺仪的讲解及实战演示(附源码 超详细必看)

    需要源码请点赞关注收藏后评论区留言~~~ 一.指南针-磁场传感器 顾名思义,指南针只要找到朝南的方向就好了. 可是在App中并非使用一个方向传感器这么简单,事实上单独的方向传感器已经弃用,取而代之的是 ...

  9. Python对阿里巴巴、谷歌、腾讯等六家公司股票数据进行分析与可视化实战(附源码 超详细)

    需要源码请点赞关注收藏后评论区留言私信~~~ 下面针对阿里巴巴.谷歌.亚马逊.Facebook.苹果和腾讯六家公司股票数据进行了分析与可视化描述,数据分析前需要安装互联数据获取包pandas-data ...

最新文章

  1. python mixup
  2. lufylegend基础知识1
  3. 1072 Gas Station (30 分)【难度: 中 / 知识点: Dijkstra + 枚举】
  4. 谭浩强课后题之----求阶乘和
  5. 关于Fragment、Tabhost和FragmentPagerAdapter来实现导航栏的效果
  6. html设置返回的样式,jQuery设置或返回元素样式属性。
  7. 第一行代码阅读笔记---基本知识
  8. 我的2015plan
  9. 数据模型与决策_好的数据模型最终都为业务而生
  10. 数据库习题(填空题三)
  11. 修复Lvgl的roller控件点击位置向上偏移的问题
  12. 300W-LP数据库介绍
  13. 大学英语和计算机统考真题,2019年6月网络教育统考《大学英语B》复习题(四)...
  14. lamp技术研发人员的必备
  15. 【后台技术】异步编程指北,问题和重点
  16. Mac Excel快捷键
  17. php中的 notice,PHP中出现Notice: Undefined index的三种解决办法
  18. Itext中强行调整行高缩小行间距
  19. 人生若只如初见 何事秋风悲画扇
  20. oracle中查看pga大小,关于oracle pga大小限制

热门文章

  1. mac terminal解压缩命令
  2. Barrett模乘与蒙哥马利模乘算法(附源码实现)
  3. 数字媒体--基于语音识别、全息投影及虚拟现实技术的建筑体验与展示系统(记录,较简陋)
  4. 科技新发现:新型高熵合金的诞生!
  5. 快递100Python爬虫(一)
  6. 将从数据库取到图片的url 在前台循环遍历显示图片
  7. 网络工程师常用的7款软件
  8. 免安装mysql5.7.22_Windows下安装配置免安装MySQL5.7服务器
  9. 红包封面限时领取,数量有限,先到先得~
  10. 龙尚科技助力煤矿智能化建设进入新发展阶段