Epic 官方视频教程《 Battery Collector》源码+超详细注释【C++】【UE4】
视频教程链接:
优酷链接
油管链接 【需要梯子】
首先来看效果图:
1. 电池随机从天空掉落,玩家按C键收集电池的能量(闪电粒子效果)来补充血条(每秒都会自动衰减)
2.玩家的颜色随着血条的减少,逐渐变黑
3.当血条为空时,玩家(黑色的那一坨)死亡,游戏结束;如果玩家提前集满血条则获胜。
以下是完整的源代码,并配套详细解释:
Pickup.h
- // Fill out your copyright notice in the Description page of Project Settings.
- #pragma once// 防止多次引用头文件
- #include "GameFramework/Actor.h"
- #include "Pickup.generated.h" // 必须是最后 include 的头文件,它是UHT(Unreal Header Tool)根据你声明的宏自动生成的
- // 该宏将类暴露给 Unreal 的反射系统,允许我们在运行时检查和迭代对象的属性(比如 GC 中的对对象引用计数的管理)
- // 1. 意味着当你创建一个对象,UE4会帮助你进行内存管理(智能指针来自动对垃圾内存进行回收),但前提是遵循了 UE4 的构造/销毁规范(比如自己手动 new 出的对象就不能被 UE4 回收);
- // 2. 默认使得该类可以被编辑器和蓝图访问;
- // 3. 如果将 Bluerpintable 改为 Blueprinttype,那么该类在蓝图中就只能作为 variable 访问了。(还有 NotBlueprintType 类型;Blueprintable 和 BlueprintType 兼容)
- UCLASS(Blueprintable)
- class BATTERYCOLLECTOR_API APickup : public AActor // BATTERYCOLLECTOR_API也是由UHT创建的宏,确保该类正确输出到 DLL 中
- {
- GENERATED_BODY()// 导入一些 UE 系统需要的自动生成的方法。与 GENERATED_CLASS_BODY 的区别 http://blog.csdn.net/a359877454/article/details/52511893
- // 为类声明一个 Log Category
- DECLARE_LOG_CATEGORY_CLASS(Pickup, Log, All);
- public:
- // Sets default values for this actor's properties
- APickup();
- // Called when the game starts or when spawned
- virtual void BeginPlay() override;
- // Called every frame
- virtual void Tick( float DeltaSeconds ) override;
- // 返回 pickup 模型(注意,内联方法和UFUNCTION不兼容)
- FORCEINLINE class UStaticMeshComponent* GetMesh() const { return PickupMesh; }
- // BlueprintPure / BlueprintCallable 表示两者都可以从蓝图端被调用,
- // BlueprintPure 意味着该方法不会修改成员数据,且只要 output pin 被使用其就会执行(它也没有exec input pin);
- // 但 BlueprintCallable 的执行需要连接exec input pin ,然后结果由 output pin 输出。
- UFUNCTION(BlueprintPure, Category = "Pickup")
- bool IsActive();
- // 安全地修改 bIsActive 成员【UFUNCTION 的好处:1.通过添加一些额外的代码,使其可以被蓝图重写;2.在游戏运行时,可以通过命令行来调用,便于调试】
- UFUNCTION(BlueprintCallable, Category = "Pickup")
- void SetActive(bool NewPickupState);
- // 当 pickup 被收集时需要调用的方法
- // BlueprintNativeEvent:表示该方法既在 C++ 中定义一些行为,也可以被蓝图中定义一些行为 (C++方法为蓝图同名方法的父方法)
- // 注意和 BlueprintImplementableEvents 的区别(既可以通知蓝图有C++层的事件发生,还可以为它额外提供一些信息)
- UFUNCTION(BlueprintNativeEvent)
- void WasCollected();
- virtual void WasCollected_Implementation(); // ❤
- protected:
- // pickup 是否被激活
- bool bIsActive;
- private:
- // 关卡中的可拾取物(静态模型)—— pickup
- // 1. VisibleAnywhere:表示 PickupMesh 属性在 Class Defaults 和它的实例(将蓝图拖动到关卡中) 中都是可见的,但不可编辑【 参见 search “放大镜”右侧的 "Open Selection in Property Matrix" 图标】
- // http://blog.csdn.net/xi_niuniu/article/details/54409648
- //
- // 2. 如果没有 AllowPrivateAccess 的话,BlueprintReadOnly 在 private 下就会编译失败(通常是 public),其作用使得 PickMesh 在蓝图编辑器中可以 Get 到
- // 3. BlueprintReadOnly :表示在蓝图下只能 Get,不能 Set; BlueprintDefaultsOnly: 表示在运行前蓝图中的默认值是可以修改的,但运行中蓝图就不能修改它
- UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pickup", meta = (AllowPrivateAccess = "true"))
- class UStaticMeshComponent* PickupMesh;// Actor subobject
- };
Pickup.cpp
- // Fill out your copyright notice in the Description page of Project Settings.
- #include "BatteryCollector.h" // 切记放在第一个
- #include "Pickup.h"
- DEFINE_LOG_CATEGORY_CLASS(APickup, Pickup)
- //#define _DEBUG_ 1
- // Sets default values
- APickup::APickup()
- {
- // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
- PrimaryActorTick.bCanEverTick = false; // true
- // 所有 pickup 默认为 true
- bIsActive = true;
- // 创建一个静态模型
- PickupMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PickupMesh"));// subobject的名字为“PickupMesh”
- RootComponent = PickupMesh;
- }
- // Called when the game starts or when spawned
- void APickup::BeginPlay()
- {
- Super::BeginPlay();
- }
- // Called every frame
- void APickup::Tick( float DeltaTime )
- {
- Super::Tick( DeltaTime );
- }
- bool APickup::IsActive()
- {
- return bIsActive;
- }
- void APickup::SetActive(bool NewPickupState)
- {
- bIsActive = NewPickupState;
- }
- void APickup::WasCollected_Implementation()
- {
- #ifdef _DEBUG_
- FString PickupDebugString = GetName();
- UE_LOG(Pickup, Warning, TEXT("You have collected %s"), *PickupDebugString);
- #endif
- }
BatteryPick.h
- // Fill out your copyright notice in the Description page of Project Settings.
- #pragma once
- #include "Pickup.h"
- #include "BatteryPickup.generated.h"
- /**
- *
- */
- UCLASS()
- class BATTERYCOLLECTOR_API ABatteryPickup : public APickup
- {
- GENERATED_BODY()
- public:
- ABatteryPickup();
- // BlueprintNativeEvent
- void WasCollected_Implementation() override;
- // 获取 battery power(注意:此方法蓝图不可调用)
- float GetPower();
- protected:
- // BlueprintProtected:表示只有继承了这个类的蓝图才可以修改这个变量
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))
- float BatteryPower;
- };
BatteryPick.cpp
- // Fill out your copyright notice in the Description page of Project Settings.
- #include "BatteryCollector.h" // 切记放在第一个
- #include "BatteryPickup.h"
- ABatteryPickup::ABatteryPickup()
- {
- // 记得为 SM_Batter_Medium 设置 Collision
- GetMesh()->SetSimulatePhysics(true);
- BatteryPower = 150.f;
- }
- void ABatteryPickup::WasCollected_Implementation()
- {
- // 调用父类的方法
- Super::WasCollected_Implementation();
- // 销毁 battery
- Destroy();// 相关方法:SetLifeSpan,允许在destroy方法调用之后,坚挺若干时间
- }
- float ABatteryPickup::GetPower()
- {
- return BatteryPower;
- }
SpawnVolume.h
- // Fill out your copyright notice in the Description page of Project Settings.
- #pragma once
- #include "GameFramework/Actor.h"
- #include "SpawnVolume.generated.h"
- UCLASS()
- class BATTERYCOLLECTOR_API ASpawnVolume : public AActor
- {
- GENERATED_BODY()
- DECLARE_LOG_CATEGORY_CLASS(SpawnVolume, Log, All); // 为类声明一个 Log Category
- public:
- // Sets default values for this actor's properties
- ASpawnVolume();
- // Called when the game starts or when spawned
- virtual void BeginPlay() override;
- // Called every frame
- virtual void Tick( float DeltaSeconds ) override;
- FORCEINLINE class UBoxComponent* GetWhereToSpawn() const { return WhereToSpawn; }
- // 返回一个 BoxComponent 范围内的随机点
- // 注意:它不会改变 SpawnVolume,也不会改变 SpawnVolume 的行为
- UFUNCTION(BlueprintPure, Category = "Spawning")
- FVector GetRandomPointInVolume();
- // 是否继续产生电池
- UFUNCTION(BlueprintCallable, Category = "Spawning")
- void SetSpawningActive(bool bShouldSpawn);
- protected:
- // 产生的 pickup,同时限制了蓝图上显示的类型必须是继承自 Pickup 类
- UPROPERTY(EditAnywhere, Category = "Spawning")
- TSubclassOf<class APickup> WhatToSpawn;// 具体可以参考 <UE4 Scripting with C++ Cookbook> p49 (NewObject<>, ConstructObject<>, ConditionalBeginDestroy)
- // 如果是自定义的C++类指针,且非UObject的派生类(已有引用计数),那么可以使用TSharedPtr, TWeakPtr, TAutoPtr(非线程安全)TScopedPointer 来自动管理内存
- // 计时器的句柄,可以用它来 cancel 定时器
- FTimerHandle SpawnTimer;
- // 最小延迟
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning")
- float SpawnDelayRangeLow;
- // 最大延迟
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning")
- float SpawnDelayRangeHigh;
- private:
- // Box 组件,指定 Pickup 在哪里产生
- // VisibleAnywhere
- UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Spawning", meta = (AllowPrivateAccess = "true"))
- class UBoxComponent* WhereToSpawn;
- void SpawnPickup();
- // 真实的延迟
- float SpawnDelay;
- };
SpawnVolume.cpp
- // Fill out your copyright notice in the Description page of Project Settings.
- #include "BatteryCollector.h" // 切记放在第一个
- #include "EngineGlobals.h"
- #include "Runtime/Engine/Classes/Engine/Engine.h"
- //#include "UnrealMathUtility.h"
- #include "Kismet/KismetMathLibrary.h"
- #include "Pickup.h"
- #include "SpawnVolume.h"
- DEFINE_LOG_CATEGORY_CLASS(ASpawnVolume, SpawnVolume)
- //#define _DEBUG_ 1
- // Sets default values
- ASpawnVolume::ASpawnVolume()
- {
- // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
- PrimaryActorTick.bCanEverTick = false;
- WhereToSpawn = CreateDefaultSubobject<UBoxComponent>(TEXT("WhereToSpawn"));
- RootComponent = WhereToSpawn;
- SpawnDelayRangeLow = 1.f;
- SpawnDelayRangeHigh = 4.5f;
- }
- // Called when the game starts or when spawned
- void ASpawnVolume::BeginPlay()
- {
- Super::BeginPlay();
- }
- // Called every frame
- void ASpawnVolume::Tick( float DeltaTime )
- {
- Super::Tick( DeltaTime );
- }
- FVector ASpawnVolume::GetRandomPointInVolume()
- {
- FVector SpawnOrigin = WhereToSpawn->Bounds.Origin;
- FVector SpawnExtend = WhereToSpawn->Bounds.BoxExtent;
- // ❤
- //FBox box = FBox::BuildAABB(SpawnOrigin, SpawnExtend);
- //FVector SpawnRand = FMath::RandPointInBox(box);
- FVector SpawnRand = UKismetMathLibrary::RandomPointInBoundingBox(SpawnOrigin, SpawnExtend);
- #ifdef _DEBUG_
- // Output Log
- //UE_LOG(SpawnVolume,
- // Warning,
- // TEXT("SpawnRand is (%3.2f, %3.2f, %3.2f)"),
- // SpawnRand.X, SpawnRand.Y, SpawnRand.Z);
- // Screen Log
- GEngine->AddOnScreenDebugMessage(-1,
- 5.f,
- FColor::Yellow,
- FString::Printf(TEXT("SpawnRand: x: %f, y: %f, z: %f"),
- SpawnRand.X, SpawnRand.Y, SpawnRand.Z));
- #endif
- return SpawnRand;
- }
- void ASpawnVolume::SpawnPickup()
- {
- if (WhatToSpawn != NULL)
- {
- UWorld* const World = GetWorld();// 当前的 UWorld 实例
- if (World)
- {
- FActorSpawnParameters SpawnParams;
- SpawnParams.Owner = this;
- SpawnParams.Instigator = Instigator;
- // 随机 pickup 的位置
- FVector SpawnLocation = GetRandomPointInVolume();
- // 随机 pickup 的方向
- FRotator SpawnRotation;
- SpawnRotation.Pitch = FMath::FRand() * 360.f; // 绕 Y 轴旋转 Right Axis
- SpawnRotation.Yaw = FMath::FRand() * 360.f; // 绕 Z 轴旋转 Up Axis
- SpawnRotation.Roll = FMath::FRand() * 360.f; // 绕 X 轴旋转 Forward Axis
- // 生产 pickup
- APickup* const SpawnedPickup = World->SpawnActor<APickup>(WhatToSpawn, SpawnLocation, SpawnRotation, SpawnParams);
- // 重新随机一个延时
- SpawnDelay = FMath::FRandRange(SpawnDelayRangeLow, SpawnDelayRangeHigh);
- // 绑定一个延时方法到全局计时器(不循环执行)
- GetWorldTimerManager().SetTimer(SpawnTimer, this, &ASpawnVolume::SpawnPickup, SpawnDelay, false);
- }
- }
- }
- void ASpawnVolume::SetSpawningActive(bool bShouldSpawn)
- {
- if (bShouldSpawn)
- {
- // 随机一个延时
- SpawnDelay = FMath::FRandRange(SpawnDelayRangeLow, SpawnDelayRangeHigh);
- // 绑定一个延时方法到全局定时器(不循环执行),即在 SpawnDelay 秒之后执行 SpawnPickup 方法
- GetWorldTimerManager().SetTimer(SpawnTimer, this, &ASpawnVolume::SpawnPickup, SpawnDelay, false);
- }
- else
- {
- // 清除定时器
- GetWorldTimerManager().ClearTimer(SpawnTimer);
- }
- }
BatteryCollectorGameMode.h
- // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
- #pragma once
- #include "GameFramework/GameModeBase.h"
- #include "BatteryCollectorGameMode.generated.h"
- // 用于存储 gameplay 当前状态的枚举
- UENUM(BlueprintType)
- enum class EBatteryPlayState
- {
- EPlaying,
- EGameOver,
- EWon,
- EUnknow
- };
- UCLASS(minimalapi)
- class ABatteryCollectorGameMode : public AGameModeBase
- {
- GENERATED_BODY()
- // 为类声明一个 Log Category
- DECLARE_LOG_CATEGORY_CLASS(BatteryCollectorGameMode, Log, All);
- public:
- ABatteryCollectorGameMode(); // GameMode类负责定义游戏的规则
- // 该方法在构造方法之后,在 tick 方法之前执行,
- // 那时所有东西已经注册完毕了。
- // 确保执行该方法时 character 已经构建完成
- virtual void BeginPlay() override;
- virtual void Tick(float DeltaTime) override;
- UFUNCTION(BlueprintPure, Category = "Power")
- float GetPowerToWin() const;
- // 获取当前的游戏状态
- UFUNCTION(BlueprintPure, Category = "Power")
- EBatteryPlayState GetCurrentState() const;
- void SetCurrentState(EBatteryPlayState NewState);
- protected:
- // character 的 power 的衰减率
- //(可以通过设置不同的衰减率,设计不同难度的关卡,只需要切换GameMode即可)
- // 该属性只有Class Defulats Detail 窗口可以编辑,拖入关卡的蓝图实例 Detail 窗口无法编辑(如果可拖拽到关卡中)
- UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))
- float DecayRate;
- UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))
- float PowerToWin;
- // 用于 HUD 的 Widget 类(限制必须继承与 UUserWidget)
- UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))
- TSubclassOf<class UUserWidget> HUDWidgetClass;
- // HUD 实例
- UPROPERTY() // 利用 GC 【GetWorld()->ForceGarbageCollection(true) 会强制 GC】
- class UUserWidget* CurrentWidget;
- private:
- // 记录当前游戏的状态
- EBatteryPlayState CurrentState;
- // 记录关卡中所有的 SpawnActor 【即使不想在蓝图中编辑,也最好声明为 UPROPERTY(),让UE4管理 TArray 的内存】
- TArray<class ASpawnVolume*> SpawnVolumeActors;
- void HandleNewState(EBatteryPlayState NewState);
- };
BatteryCollectorGameMode.cpp
- // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
- #include "BatteryCollector.h" // 切记放在第一个
- #include "EngineGlobals.h"
- #include "Runtime/Engine/Classes/Engine/Engine.h"
- #include "Kismet/GameplayStatics.h"
- #include "Runtime/UMG/Public/Blueprint/UserWidget.h" // 不是 <Blueprint/UserWidget>
- #include "BatteryCollectorGameMode.h"
- #include "BatteryCollectorCharacter.h"
- #include "SpawnVolume.h"
- //#define _DEBUG_ 1
- DEFINE_LOG_CATEGORY_CLASS(ABatteryCollectorGameMode, BatteryCollectorGameMode)
- ABatteryCollectorGameMode::ABatteryCollectorGameMode()
- {
- PrimaryActorTick.bCanEverTick = true; // ❤
- // 将蓝图玩家类设为默认的 pawn class。 类似还有 ContructorHelpers::FObjectFinder,用于 Load Asset
- static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));
- if (PlayerPawnBPClass.Class != NULL)
- {
- DefaultPawnClass = PlayerPawnBPClass.Class;
- }
- DecayRate = .01f; // 0.01f;
- }
- void ABatteryCollectorGameMode::BeginPlay()
- {
- Super::BeginPlay();
- // 查找所有的 Spawn Volume Actor
- TArray<AActor*> FoundActors;
- // GetWorld 返回缓存的世界指针;
- // 返回指定的类在世界中的所有 actor;
- // 该方法会遍历整个关卡,比较耗性能
- UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASpawnVolume::StaticClass(), FoundActors);
- for (auto Actor : FoundActors)
- {
- // 如果转换成功,即该 Actor 是 SpawnVolumeActor 类型
- ASpawnVolume* SpawnVolumeActor = Cast<ASpawnVolume>(Actor);
- if (SpawnVolumeActor)
- {
- // 确保不会重复添加
- SpawnVolumeActors.AddUnique(SpawnVolumeActor);
- }
- }
- // 初始设置游戏状态
- SetCurrentState(EBatteryPlayState::EPlaying);
- ABatteryCollectorCharacter* MyCharacter = Cast<ABatteryCollectorCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));
- if (MyCharacter)
- {
- PowerToWin = MyCharacter->GetInitialPower() * 1.25f;
- }
- // UMG
- if(HUDWidgetClass != nullptr)
- {
- CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), HUDWidgetClass);
- if (CurrentWidget != nullptr)
- {
- CurrentWidget->AddToViewport();
- }
- }
- }
- void ABatteryCollectorGameMode::Tick(float DeltaTime)
- {
- Super::Tick(DeltaTime);
- #ifdef _DEBUG_
- // Screen Log
- //GEngine->AddOnScreenDebugMessage(-1,
- // 5.f,
- // FColor::Red,
- // FString::Printf(TEXT("GameMode: %s"), TEXT("Tick")));
- GEngine->AddOnScreenDebugMessage(-1,
- 5.f,
- FColor::Red,
- FString::Printf(TEXT("GameMode: Tick")));
- #endif
- // 获取指定 index 的 player pawn,并转化为 ABatteryCollectorCharacter
- ABatteryCollectorCharacter* MyCharacter = Cast<ABatteryCollectorCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));
- if (MyCharacter)
- {
- // 如果玩家的能量已经集齐到一定值则胜利
- if (MyCharacter->GetCurrentPower() > PowerToWin)
- {
- SetCurrentState(EBatteryPlayState::EWon);
- }
- // 如果玩家能量为正
- else if (MyCharacter->GetCurrentPower() > 0.f)
- {
- // 玩家能量随时间衰减
- MyCharacter->UpdatePower(-DeltaTime*DecayRate*(MyCharacter->GetInitialPower()));
- }
- else
- {
- SetCurrentState(EBatteryPlayState::EGameOver);
- }
- }
- }
- float ABatteryCollectorGameMode::GetPowerToWin() const
- {
- return PowerToWin;
- }
- EBatteryPlayState ABatteryCollectorGameMode::GetCurrentState() const
- {
- return CurrentState;
- }
- void ABatteryCollectorGameMode::SetCurrentState(EBatteryPlayState NewState)
- {
- CurrentState = NewState;
- HandleNewState(CurrentState);
- }
- void ABatteryCollectorGameMode::HandleNewState(EBatteryPlayState NewState)
- {
- switch (NewState)
- {
- case EBatteryPlayState::EPlaying:
- {
- for (ASpawnVolume* Volume : SpawnVolumeActors)
- {
- Volume->SetSpawningActive(true);
- }
- }
- break;
- case EBatteryPlayState::EWon:
- {
- for (ASpawnVolume* Volume : SpawnVolumeActors)
- {
- Volume->SetSpawningActive(false);
- }
- }
- break;
- case EBatteryPlayState::EGameOver:
- {
- for (ASpawnVolume* Volume : SpawnVolumeActors)
- {
- Volume->SetSpawningActive(false);
- }
- APlayerController* PlayerController = UGameplayStatics::GetPlayerController(this, 0);
- if (PlayerController)
- {
- // 禁用部分输入,但不会隐藏玩家和 HUD
- PlayerController->SetCinematicMode(true, false, false, true, true);
- }
- // 加入布娃娃系统
- // 必须确保它是 Chacater 而不是 Pawn,因为 Character 具有额外的动作和骨骼模型
- ACharacter* MyCharacter = UGameplayStatics::GetPlayerCharacter(this, 0);
- if (MyCharacter)
- {
- // 使得玩家可以像布娃娃一样“摊倒”
- //(确保 Mannequin/Character/Mesh/ 目录下的 SK_Mannequin 已创建了 Physics Asset)
- // (确保 ThirdPersonCPP/Blueprints/ 目录下的 ThirdPersonCharacter,其中 Mesh 组件的 Collision 被激活 [自定义预设])
- MyCharacter->GetMesh()->SetSimulatePhysics(true);
- // 禁用跳跃动作
- MyCharacter->GetMovementComponent()->MovementState.bCanJump = false;
- }
- }
- break;
- default:
- case EBatteryPlayState::EUnknow:
- {
- // 保留
- }
- break;
- }
- }
BatteryCollectorCharacter.h
- // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
- #pragma once
- #include "GameFramework/Character.h"
- #include "BatteryCollectorCharacter.generated.h"
- UCLASS(config=Game)
- class ABatteryCollectorCharacter : public ACharacter
- {
- GENERATED_BODY()
- /** Camera boom positioning the camera behind the character */
- UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
- class USpringArmComponent* CameraBoom;
- /** Follow camera */
- UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
- class UCameraComponent* FollowCamera;
- // CollectionSphere 组件
- UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Power, meta = (AllowPrivateAccess = "true"))
- class USphereComponent* CollectionSphere;
- public:
- ABatteryCollectorCharacter();
- /** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
- UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
- float BaseTurnRate;
- /** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
- UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
- float BaseLookUpRate;
- // power 的 Getter 和 Setter
- UFUNCTION(BlueprintPure, Category = "Power")
- float GetInitialPower();
- UFUNCTION(BlueprintPure, Category = "Power")
- float GetCurrentPower();
- UFUNCTION(BlueprintCallable, Category = "Power")
- void UpdatePower(float PowerChange);
- protected:
- /** Resets HMD orientation in VR. */
- void OnResetVR();
- /** Called for forwards/backward input */
- void MoveForward(float Value);
- /** Called for side to side input */
- void MoveRight(float Value);
- /**
- * Called via input to turn at a given rate.
- * @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
- */
- void TurnAtRate(float Rate);
- /**
- * Called via input to turn look up/down at a given rate.
- * @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
- */
- void LookUpAtRate(float Rate);
- /** Handler for when a touch input begins. */
- void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);
- /** Handler for when a touch input stops. */
- void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);
- // APawn interface
- virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
- // End of APawn interface
- // 按下按键,收集一个在 collection sphere 范围内的 pickup
- UFUNCTION(BlueprintCallable, Category = "Pickups")
- void CollectPickups();
- // character 初始的 power
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))
- float InitialPower;
- // 玩家的速度因子
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))
- float SpeedFactor;
- // 基础速度
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))
- float BaseSpeed;
- // BlueprintImplementableEvent:定义一个蓝图事件, 意味着我们不用在 C++ 中定义该方法,交给蓝图去实现
- UFUNCTION(BlueprintImplementableEvent, Category = "Power")
- void PowerChangeEffect();
- private:
- // character 当前的 power
- UPROPERTY(VisibleAnywhere, Category="Power")
- float CharacterPower;
- public:
- /** Returns CameraBoom subobject **/
- FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
- /** Returns FollowCamera subobject **/
- FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
- // 返回 CollectionSphere 组件
- FORCEINLINE class USphereComponent* GetCollectionSphere() const { return CollectionSphere; }
- };
BatteryCollectorCharacter.cpp
- // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
- #include "BatteryCollector.h" // 切记放在第一个
- #include "Kismet/HeadMountedDisplayFunctionLibrary.h"
- #include "BatteryCollectorCharacter.h"
- #include "Pickup.h"
- #include "BatteryPickup.h"
- //
- // ABatteryCollectorCharacter
- ABatteryCollectorCharacter::ABatteryCollectorCharacter()
- {
- // Set size for collision capsule
- GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
- // set our turn rates for input
- BaseTurnRate = 45.f;
- BaseLookUpRate = 45.f;
- // Don't rotate when the controller rotates. Let that just affect the camera.
- bUseControllerRotationPitch = false;
- bUseControllerRotationYaw = false;
- bUseControllerRotationRoll = false;
- // Configure character movement
- GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
- GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
- GetCharacterMovement()->JumpZVelocity = 600.f;
- GetCharacterMovement()->AirControl = 0.2f;
- // Create a camera boom (pulls in towards the player if there is a collision)
- CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
- CameraBoom->SetupAttachment(RootComponent);
- CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character
- CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
- // Create a follow camera
- FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
- FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
- FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
- // 创建 collection sphere 组件
- CollectionSphere = CreateDefaultSubobject<USphereComponent>(TEXT("CollectionSphere"));
- // 并将其添加到 RootComponent 上
- //CollectionSphere->AttachTo(RootComponent); // 过时的方法
- // https://forums.unrealengine.com/showthread.php?112644-4-12-Transition-Guide
- CollectionSphere->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform);
- // collection sphere 的半径范围
- CollectionSphere->SetSphereRadius(200.f);
- // Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
- // are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)
- // 为 power 赋值
- InitialPower = 2000.f;
- CharacterPower = InitialPower;
- SpeedFactor = .75f;
- BaseSpeed = 10.f;
- }
- //
- // Input
- void ABatteryCollectorCharacter::SetupPlayerInputComponent(class UInputComponent *PlayerInputComponent)
- {
- // Set up gameplay key bindings
- check(PlayerInputComponent);
- PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
- PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
- // Collect 键盘响应事件的绑定(Action 用于短促的响应,Axis 用于持续的响应)
- PlayerInputComponent->BindAction("Collect", IE_Pressed, this, &ABatteryCollectorCharacter::CollectPickups);
- PlayerInputComponent->BindAxis("MoveForward", this, &ABatteryCollectorCharacter::MoveForward);
- PlayerInputComponent->BindAxis("MoveRight", this, &ABatteryCollectorCharacter::MoveRight);
- // We have 2 versions of the rotation bindings to handle different kinds of devices differently
- // "turn" handles devices that provide an absolute delta, such as a mouse.
- // "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
- PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
- PlayerInputComponent->BindAxis("TurnRate", this, &ABatteryCollectorCharacter::TurnAtRate);
- PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
- PlayerInputComponent->BindAxis("LookUpRate", this, &ABatteryCollectorCharacter::LookUpAtRate);
- // handle touch devices
- PlayerInputComponent->BindTouch(IE_Pressed, this, &ABatteryCollectorCharacter::TouchStarted);
- PlayerInputComponent->BindTouch(IE_Released, this, &ABatteryCollectorCharacter::TouchStopped);
- // VR headset functionality
- PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &ABatteryCollectorCharacter::OnResetVR);
- }
- void ABatteryCollectorCharacter::OnResetVR()
- {
- UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
- }
- void ABatteryCollectorCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
- {
- Jump();
- }
- void ABatteryCollectorCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
- {
- StopJumping();
- }
- void ABatteryCollectorCharacter::TurnAtRate(float Rate)
- {
- // calculate delta for this frame from the rate information
- AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
- }
- void ABatteryCollectorCharacter::LookUpAtRate(float Rate)
- {
- // calculate delta for this frame from the rate information
- AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
- }
- void ABatteryCollectorCharacter::MoveForward(float Value)
- {
- if ((Controller != NULL) && (Value != 0.0f))
- {
- // find out which way is forward
- const FRotator Rotation = Controller->GetControlRotation();
- const FRotator YawRotation(0, Rotation.Yaw, 0);
- // get forward vector
- const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
- AddMovementInput(Direction, Value);
- }
- }
- void ABatteryCollectorCharacter::MoveRight(float Value)
- {
- if ( (Controller != NULL) && (Value != 0.0f) )
- {
- // find out which way is right
- const FRotator Rotation = Controller->GetControlRotation();
- const FRotator YawRotation(0, Rotation.Yaw, 0);
- // get right vector
- const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
- // add movement in that direction
- AddMovementInput(Direction, Value);
- }
- }
- void ABatteryCollectorCharacter::CollectPickups()
- {
- // 遍历所有覆盖区域内的 Actor,将它们存储进数组
- TArray<AActor *> CollectedActors;
- CollectionSphere->GetOverlappingActors(CollectedActors);
- // 记录收集的电池的能量
- float CollectedPower = 0.f;
- // 遍历数组
- for (int32 iCollected = 0; iCollected < CollectedActors.Num(); ++iCollected )
- {
- // 将 Actor 转化为 APickup
- APickup *const TestPickup = Cast<APickup>(CollectedActors[iCollected]);
- // 检查转化是否成功,还有 pickup 是否即将被销毁,是否是激活状态
- if (TestPickup && !TestPickup->IsPendingKill() && TestPickup->IsActive())
- {
- // 收集 pickup(注意:此处不是调用的 xxx_Implementation)
- TestPickup->WasCollected();
- // 检查 pickup 是否是电池
- ABatteryPickup* const TestBattery = Cast<ABatteryPickup>(TestPickup);
- if (TestBattery)
- {
- // 累加收集的能量
- CollectedPower += TestBattery->GetPower();
- }
- // 冻结 pickup
- TestPickup->SetActive(false);
- }
- }
- if (CollectedPower > 0.f)
- {
- UpdatePower(CollectedPower);
- }
- }
- float ABatteryCollectorCharacter::GetInitialPower()
- {
- return InitialPower;
- }
- float ABatteryCollectorCharacter::GetCurrentPower()
- {
- return CharacterPower;
- }
- void ABatteryCollectorCharacter::UpdatePower(float PowerChange)
- {
- CharacterPower = CharacterPower + PowerChange;
- // 所有 Character类都有 GetCharacterMovement 方法
- // 根据 power 更新玩家的速度
- GetCharacterMovement()->MaxWalkSpeed = BaseSpeed + SpeedFactor * CharacterPower;
- // 调用蓝图实现的方法
- PowerChangeEffect();
- }
以及 蓝图 的设计
Battery_BP
ThirdPersonCharacter 的 Construction Scirpt
注意:该材质是动态材质实例(Dynamic Material Instance),可以实时改变
Construction Script 是一种当绑定对象上的属性发生变化就会执行的脚本(不包括关卡中的移动),和 C++ 中的构造函数有点相似
ThirdPersonCharacter
BatteryHUD
附
UE_LOG 的使用方法
字符串处理
为什么要用 TSubclassOf
(完)
Epic 官方视频教程《 Battery Collector》源码+超详细注释【C++】【UE4】相关推荐
- android:自己动手编译Android源码(超详细)
自己动手编译Android源码(超详细) 涅槃1992 2016.06.20 02:12* 字数 4330 阅读 86819评论 89喜欢 339赞赏 7 在Android Studio代码调试一文中 ...
- 基础IO(文件接口、安装内核源码超详细步骤图解、静态库与动态库)
基础IO C语言的文件操作接口 fopen fclose fread fwrite fseek 系统调用文件接口 open close read write lseek 安装内核源码 文件描述符&am ...
- Android Studio App开发之网络通信中使用POST方式调用HTTP接口实现应用更新功能(附源码 超详细必看)
运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一.POST方式调用HTTP接口 POST方式把接口地址与请求报文分开,允许使用自定义的报文格式,由此扩大了该方式的应用场景.POST请求与GET ...
- OpenCV中拆分通道、合并通道、alpha通道的讲解及实战演示(附python源码 超详细)
需要源码和图片请点赞关注收藏后评论区留言私信~~~ 在BGR色彩空间中,图像的通道由B通道.G通道和B通道构成,下面将介绍OpenCV提供的方法拆分和合并通道 一.拆分通道 为了拆分图像中的通道 Op ...
- LSTM神经网络实现对股市收盘价格的预测实战(python实现 附源码 超详细)
源码或数据集请点赞关注收藏后评论区留言或者私信博主要 由于独特的设计结构 LSTM适合于处理和预测时间序列中间隔和延迟非常长的重要事件. LSTM是一种含有LSTM区块(blocks)或其他的一种类神 ...
- Android App开发实战项目之购物车(附源码 超详细必看)
需要源码请点赞关注收藏后评论区留言~~~ 一.需求描述 电商App的购物车可谓是司空见惯了,可以知道购物车除了底部有一个结算行,其余部分主要是已加入购物车的商品列表,然后每个商品左边是商品小图,右边是 ...
- Android Studio App开发入门之在活动之间传递消息(附源码 超详细必看)(包括显示和隐式Intent,向上一个和下一个Activity发送数据)
运行有问题或需要源码请点赞关注收藏后评论区留言~~ 显示Intent和隐式Intent Intent是各个组件之间的信息沟通的桥梁,既能在Activity之间沟通,又能在Activity与Servi ...
- 【Android App】物联网中指南针、计步器、感光器、陀螺仪的讲解及实战演示(附源码 超详细必看)
需要源码请点赞关注收藏后评论区留言~~~ 一.指南针-磁场传感器 顾名思义,指南针只要找到朝南的方向就好了. 可是在App中并非使用一个方向传感器这么简单,事实上单独的方向传感器已经弃用,取而代之的是 ...
- Python对阿里巴巴、谷歌、腾讯等六家公司股票数据进行分析与可视化实战(附源码 超详细)
需要源码请点赞关注收藏后评论区留言私信~~~ 下面针对阿里巴巴.谷歌.亚马逊.Facebook.苹果和腾讯六家公司股票数据进行了分析与可视化描述,数据分析前需要安装互联数据获取包pandas-data ...
最新文章
- python mixup
- lufylegend基础知识1
- 1072 Gas Station (30 分)【难度: 中 / 知识点: Dijkstra + 枚举】
- 谭浩强课后题之----求阶乘和
- 关于Fragment、Tabhost和FragmentPagerAdapter来实现导航栏的效果
- html设置返回的样式,jQuery设置或返回元素样式属性。
- 第一行代码阅读笔记---基本知识
- 我的2015plan
- 数据模型与决策_好的数据模型最终都为业务而生
- 数据库习题(填空题三)
- 修复Lvgl的roller控件点击位置向上偏移的问题
- 300W-LP数据库介绍
- 大学英语和计算机统考真题,2019年6月网络教育统考《大学英语B》复习题(四)...
- lamp技术研发人员的必备
- 【后台技术】异步编程指北,问题和重点
- Mac Excel快捷键
- php中的 notice,PHP中出现Notice: Undefined index的三种解决办法
- Itext中强行调整行高缩小行间距
- 人生若只如初见 何事秋风悲画扇
- oracle中查看pga大小,关于oracle pga大小限制