Skip to main content
 首页 » 资源教程 » UE4教程

UE4示例项目学习:用C++编写BatteryCollecter

2016年09月01日 17:40:3310100

这是一个官方的示例教程,基本是用纯C++来做的,对于刚入门的小伙伴来说,是快速上手的一个好教程

官网地址(需要翻墙哦):点击这里

然后点击3rd Person Power-Up Game with C++ 就可以了

优酷地址:点击这里


该项目完成的功能:

在场景中有多个电池的生成器,每隔一段时间会从天上掉电池下来.玩家可以通过c键去收集附近的电池,电池被收集以后会销毁,播放特效.同时为玩家增加能量,玩家的能量决定了游戏是否胜利还是失败.能量的多少还会影响玩家的移动速度.


废话不多,上项目.

1.新建项目,选择C++项目中的带有第三人称的模板.

2.开始写代码.

(1).Pickup 类,Pickup类是所有可以收集的物品的父类,在这个项目里面我们只有一个子类继承于他,但是如果你想让其他东西也可以被收集的话,可以让其他的类继承于它就可以了.

Pickup.h:


  1. #include "GameFramework/Actor.h"  

  2. #include "Pickup.generated.h"  

  3.   

  4. UCLASS()  

  5. class BATTERYCOLLECT_API APickup : public AActor  

  6. {  

  7.     GENERATED_BODY()  

  8.       

  9. public:   

  10.     APickup();  

  11.   

  12.     virtual void BeginPlay() override;  

  13.       

  14.     virtual void Tick( float DeltaSeconds ) override;  

  15.   

  16.     //获取该PickUp的Mesh  

  17.     FORCEINLINE class UStaticMeshComponent* GetMesh() const { return Pickup; };  

  18.   

  19.     //检查该Actor是否激活(是否能被收集)  

  20.     UFUNCTION(BlueprintPure, Category = "Pickup")  

  21.         bool IsActive();  

  22.     //设置Actor的Active  

  23.     UFUNCTION(BlueprintCallable, Category = "Pickup")  

  24.         void SetActive(bool NewState);  

  25.   

  26.     //被收集时候要做什么(该方法可以在蓝图中被复写)  

  27.     UFUNCTION(BlueprintNativeEvent)  

  28.         void WasCollect();  

  29.   

  30.     //WasCollect函数的实现  

  31.     virtual void WasCollect_Implementation();  

  32.   

  33. protected:  

  34.     //激活标志位  

  35.     bool bIsActive;  

  36.   

  37. private:  

  38.     UPROPERTY(VisibleAnyWhere,BluePrintReadOnly,Category = "Pickup",meta = (AllowPrivateAccess = "true"))  

  39.     class UStaticMeshComponent* Pickup;  

  40. };  




在Pickup.h文件中,我们定义了该父类的一些基本属性和方法,包括对物体可否收集的标志位active的设置,还有收集的实现方式的声明.下面开一下具体的Pickup.cpp文件



Pickup.cpp:


  1. #include "BatteryCollect.h"  

  2. #include "Pickup.h"  

  3.   

  4.   

  5. // Sets default values  

  6. APickup::APickup()  

  7. {  

  8.     //不需要每帧进行TICK  

  9.     PrimaryActorTick.bCanEverTick = false;  

  10.     //默认是可以收集的  

  11.     SetActive(true);  

  12.     //创建Mesh,不然会掉到地下去,然后将Mesh设置为默认的根节点  

  13.     Pickup = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PickupMesh"));  

  14.     RootComponent = Pickup;  

  15.   

  16. }  

  17.   

  18. // Called when the game starts or when spawned  

  19. void APickup::BeginPlay()  

  20. {  

  21.     Super::BeginPlay();  

  22. }  

  23.   

  24. // Called every frame  

  25. void APickup::Tick( float DeltaTime )  

  26. {  

  27.     Super::Tick( DeltaTime );  

  28.   

  29. }  

  30.   

  31. bool APickup:: IsActive()  

  32. {  

  33.     return bIsActive;  

  34. }  

  35.   

  36. void APickup::SetActive(bool NewState)  

  37. {  

  38.     bIsActive = NewState;  

  39. }  

  40.   

  41. //如果被收集了就打个LOG出来作为提示  

  42. void APickup::WasCollect_Implementation()  

  43. {  

  44.     FString debugStr = GetName();  

  45.     UE_LOG(LogClass, Log, TEXT("%s has been collected"),*debugStr);  

  46. }  

注释已经写的很到位了.不做过多的解释了,我们在cpp文件中把.h文件中定义的方法实现了.



下面主要来看具体的电池类,还是先看.h文件

BatteryPickup.h:


  1. #include "Pickup.h"  

  2. #include "BatteryPickup.generated.h"  

  3.   

  4. /** 

  5.  *  

  6.  */  

  7. UCLASS()  

  8. class BATTERYCOLLECT_API ABatteryPickup : public APickup  

  9. {  

  10.     GENERATED_BODY()  

  11.       

  12. public:  

  13.     ABatteryPickup();  

  14.       

  15.     //复写父类的收集方法,因为电池被收集的时候肯定不只是打个LOG这么简单的  

  16.     void WasCollect_Implementation() override;  

  17.   

  18.     //获得该电池拥有多少的电量,在蓝图中可以调用该方法,该方法位于Power目录下  

  19.     UFUNCTION(Blueprintpure, Category = "Power")  

  20.     float GetBatteryPower();  

  21.   

  22.   

  23. protected:  

  24.     //电量,该属性可以在普通窗口或者蓝图窗口中修改,目录为Power  

  25.     UPROPERTY(EditAnyWhere,BlueprintReadWrite,Category = "Power")  

  26.     float batteryPower;  

  27.   

  28. };  

BatteryPickup.cpp:

  1. ABatteryPickup::ABatteryPickup()  

  2. {  

  3.     PrimaryActorTick.bCanEverTick = false;  

  4.     SetActive(true);  

  5.     GetMesh()->SetSimulatePhysics(true);  

  6.     batteryPower = 150.0f;  

  7. }  

  8.   

  9. void ABatteryPickup::WasCollect_Implementation()  

  10. {  

  11.     Super::WasCollect_Implementation();  

  12.     Destroy();  

  13. }  

  14.   

  15. float ABatteryPickup::GetBatteryPower()  

  16. {  

  17.     return batteryPower;  

  18. }  

BatteryPickup和父类最大的不同点是,他定义了一个电量的属性,每个电池都会有电量,该电量会为人物充电.同时,他复写了父类的Was_Collecti_Implement方法,在被收集了以后会将自身销毁.在电池被收集的时候,我们希望他能有一特效,Was_Collect这个方法是可以在蓝图中复写的.所以我们在蓝图中创建特效,然后再执行代码中的方法就好了.

看下蓝图怎么写:

UE4示例项目学习:用C++编写BatteryCollecter UE4教程 第1张



再看一下生成这些电池,可收集物的出生器:SpawnVolumn

SpawnVolumn.h:


  1. #include "GameFramework/Actor.h"  

  2. #include "SpawnVolumn.generated.h"  

  3.   

  4. UCLASS()  

  5. class BATTERYCOLLECT_API ASpawnVolumn : public AActor  

  6. {  

  7.     GENERATED_BODY()  

  8.       

  9. public:   

  10.     // Sets default values for this actor's properties  

  11.     ASpawnVolumn();  

  12.   

  13.     // Called when the game starts or when spawned  

  14.     virtual void BeginPlay() override;  

  15.       

  16.     // Called every frame  

  17.     virtual void Tick( float DeltaSeconds ) override;  

  18.   

  19.     //获得出生点的位置  

  20.     FORCEINLINE class UBoxComponent* GetWhereToSpawn() const { return WhereToSpawn; };  

  21.   

  22.     //在一个圆形范围内获得一个点作为出生点的位置  

  23.     UFUNCTION(BlueprintPure,Category = "Spawning")  

  24.     FVector GetRandomPointInVolumn();  

  25.   

  26.     //设置是否应该继续生成收集物(如果已经游戏结束,那么就不应该再生成东西了)  

  27.     UFUNCTION()  

  28.     void SetShouldSpawn(bool shouldSpawn);  

  29.   

  30.     //查询是否应该继续生成收集物  

  31.     UFUNCTION()  

  32.     bool GetShouldSpawn();  

  33.   

  34. protected:  

  35.     //该生成器会生成什么样的收集物,要求必须是PickUp的子类  

  36.     UPROPERTY(EditAnyWhere,Category = "Spawning")  

  37.     TSubclassOf<class APickup> WhatToSpawn;  

  38.   

  39.     //生成收集物的定时器  

  40.     FTimerHandle SpawnTimer;  

  41.   

  42.     //定义一个区间,每次会从中随机作为下个收集物的生成时间间隔  

  43.     UPROPERTY(EditAnywhere , BlueprintReadWrite , Category = "Spawning")  

  44.     float delayTimeMin = 1.0f;  

  45.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning")  

  46.     float delayTimeMax = 4.5f;  

  47.   

  48.     //时间间隔  

  49.     float spawnDelay;  

  50. private:  

  51.     //出生点位置  

  52.     UPROPERTY(VisibleAnyWhere, BluePrintReadOnly, Category = "Spawning", meta = (AllowPrivateAccess = "true"))  

  53.         class UBoxComponent * WhereToSpawn;  

  54.   

  55.     //是否应该继续产生收集物  

  56.     UPROPERTY(VisibleAnyWhere, BluePrintReadOnly, Category = "Spawing", meta = (AllowPrivateAccess = "true"))  

  57.         bool bShouldSpawn;  

  58.   

  59.     //捡起生成物  

  60.     void SpawnPickup();  

  61. };  

SpawnVolumn是负责生成收集物的,所以我们就要知道以下几个信息,我们生成器应该生成什么物品,WhatToSpawn,在哪里生成,WhereToSpawn,什么时候生成,WhenToSpawn.这些东西都已经在.h文件中定义好了..下面看实现


SpawnVolumn.cpp:

  1. #include "BatteryCollect.h"  

  2. #include "SpawnVolumn.h"  

  3. #include "Kismet/KismetMathLibrary.h"  

  4. #include "BatteryPickup.h"  

  5.   

  6.   

  7. // Sets default values  

  8. ASpawnVolumn::ASpawnVolumn()  

  9. {  

  10.     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.  

  11.     PrimaryActorTick.bCanEverTick = true;  

  12.   

  13.     //该出生器放置的位置就是收集物出生的位置,只是收集物还会有一个范围的便宜  

  14.     WhereToSpawn = CreateDefaultSubobject<UBoxComponent>(TEXT("WhereToSpawn"));  

  15.     RootComponent = WhereToSpawn;  

  16. }  

  17.   

  18. // Called when the game starts or when spawned  

  19. void ASpawnVolumn::BeginPlay()  

  20. {  

  21.     Super::BeginPlay();  

  22. }  

  23.   

  24. // Called every frame     

  25. void ASpawnVolumn::Tick( float DeltaTime )  

  26. {  

  27.     Super::Tick( DeltaTime );  

  28. }  

  29.   

  30. //在BoxComponent内进行随机选点,然后作为收集物的出生点  

  31. FVector ASpawnVolumn::GetRandomPointInVolumn()  

  32. {  

  33.     FVector originPos = WhereToSpawn->Bounds.Origin;  

  34.     FVector randomPos = WhereToSpawn->Bounds.BoxExtent;  

  35.   

  36.     FVector finalPos = UKismetMathLibrary::RandomPointInBoundingBox(originPos,randomPos);  

  37.     return finalPos;  

  38. }  

  39.   

  40. //产生收集物  

  41. void ASpawnVolumn::SpawnPickup()  

  42. {  

  43.     //检查参数是否都正常设定好了  

  44.     if (WhereToSpawn == NULL && WhatToSpawn == NULL)  

  45.         return;  

  46.     UWorld* const world = GetWorld();  

  47.     if (world == NULL)  

  48.         return;  

  49.     //设定出生参数  

  50.     FActorSpawnParameters spawnParams;  

  51.     spawnParams.Owner = this;  

  52.     spawnParams.Instigator = Instigator;  

  53.     //获取出生位置  

  54.     FVector SpawnLocation = GetRandomPointInVolumn();  

  55.     //获取出生朝向  

  56.     FRotator SpawnRotator;  

  57.     SpawnRotator.Yaw = FMath::FRand() * 360.0f;  

  58.     SpawnRotator.Pitch = FMath::FRand() * 360.0f;  

  59.     SpawnRotator.Roll = FMath::FRand() * 360.0f;  

  60.     //产生收集物  

  61.     APickup* const SpawnPickup = world->SpawnActor<APickup>(WhatToSpawn, SpawnLocation, SpawnRotator, spawnParams);  

  62.     //设定好定时器,准备产生下一个收集物,如果不需要再产生的话就把这个定时器给禁用掉就好了  

  63.     spawnDelay = FMath::FRandRange(delayTimeMin, delayTimeMax);  

  64.     GetWorldTimerManager().SetTimer(SpawnTimer, this, &ASpawnVolumn::SpawnPickup, spawnDelay, false);  

  65. }  

  66.   

  67. //设置是否应该产生收集物,一般来说,在游戏开始的时候,我们会将该参数设置为TRUE,那么他就会自动不停生成收集物了.  

  68. void ASpawnVolumn::SetShouldSpawn(bool shouldSpawn)  

  69. {  

  70.     bShouldSpawn = shouldSpawn;  

  71.   

  72.     //如果应该产生,那么就调用产生收集物的代码  

  73.     if(shouldSpawn)  

  74.     {  

  75.         SpawnPickup();  

  76.     }  

  77.     //否则禁用定时器  

  78.     else  

  79.     {  

  80.         GetWorldTimerManager().ClearTimer(SpawnTimer);  

  81.     }  

  82. }  

  83.   

  84. bool ASpawnVolumn::GetShouldSpawn()  

  85. {  

  86.     return bShouldSpawn;  

  87. }  

SpanwlVolumn总的来说就是管理收集物的出生,仅此而已.到目前为止,我们定义了一个基本的收集物父类,然后有了一个具体的收集物子类,并且有一个出生期来负责他们的创建.


那么接下来就是如何将出生器给应用到我们的场景中了.很简单,把蓝图拖到场景中就可以啦.

主要参数设置如下:

UE4示例项目学习:用C++编写BatteryCollecter UE4教程 第2张


这个时候我们进入游戏,就可以看到天上在不停的掉东西下来啦,哦对了,具体的生成子类的Mesh也要设置好哦.

UE4示例项目学习:用C++编写BatteryCollecter UE4教程 第3张


现在我们已经有了那么多的收集物了,那么接下来我们就要能收集东西才可以,以及对游戏状态的一个判断.

接下来我们要实现的功能包括:

1.收集物品

2.收集物品增加能量,体现在人物材质,速度,UI上

3.游戏模式的设定(胜利,失败的判定条件)


我们先来看看游戏角色是怎么写的:

首先是.h文件

  1. // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.  

  2. #pragma once  

  3. #include "GameFramework/Character.h"  

  4. #include "BatteryCollectCharacter.generated.h"  

  5.   

  6. UCLASS(config=Game)  

  7. class ABatteryCollectCharacter : 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.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))  

  20.     class USphereComponent* CollectionSphere;  

  21.   

  22. public:  

  23.     ABatteryCollectCharacter();  

  24.   

  25.     /** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */  

  26.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)  

  27.     float BaseTurnRate;  

  28.   

  29.     /** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */  

  30.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)  

  31.     float BaseLookUpRate;  

  32.   

  33. protected:  

  34.   

  35.     /** Called for forwards/backward input */  

  36.     void MoveForward(float Value);  

  37.   

  38.     /** Called for side to side input */  

  39.     void MoveRight(float Value);  

  40.   

  41.     /**  

  42.      * Called via input to turn at a given rate.  

  43.      * @param Rate  This is a normalized rate, i.e. 1.0 means 100% of desired turn rate 

  44.      */  

  45.     void TurnAtRate(float Rate);  

  46.   

  47.     /** 

  48.      * Called via input to turn look up/down at a given rate.  

  49.      * @param Rate  This is a normalized rate, i.e. 1.0 means 100% of desired turn rate 

  50.      */  

  51.     void LookUpAtRate(float Rate);  

  52.   

  53.     /** Handler for when a touch input begins. */  

  54.     void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);  

  55.   

  56.     /** Handler for when a touch input stops. */  

  57.     void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);  

  58.   

  59. protected:  

  60.     // APawn interface  

  61.     virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;  

  62.     // End of APawn interface  

  63.     // 收集电池  

  64.     UFUNCTION(BlueprintCallable,Category = "Pickups")  

  65.     void CollectBattery();  

  66.   

  67. public:  

  68.     /** Returns CameraBoom subobject **/  

  69.     FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }  

  70.     /** Returns FollowCamera subobject **/  

  71.     FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }  

  72.   

  73.     FORCEINLINE class USphereComponent* GetCollectionSphere() const { return CollectionSphere; }  

  74.   

  75.     //可以在蓝图中调用,不会改变任何的属性  

  76.     UFUNCTION(BlueprintPure, Category = "Power")  

  77.     float GetInitialPower();  

  78.   

  79.     //可以在蓝图中调用  

  80.     UFUNCTION(BlueprintPure, Category = "Power")  

  81.     float GetCurPower();  

  82.   

  83.     //改变人物能量,可以在蓝图中调用  

  84.     UFUNCTION(BlueprintCallable, Category = "Power")  

  85.     void ChangeCurPower(float power);  

  86.   

  87.     //改变玩家材质球的颜色,该方法由蓝图来实现  

  88.     UFUNCTION(BlueprintImplementableEvent, Category = "Power")  

  89.     void PlayPowerAddEffect();  

  90.   

  91. private:  

  92.     //初始化能量  

  93.     UPROPERTY(EditAnyWhere, Category = "Power")  

  94.     float initialPower;  

  95.     //当前能量  

  96.     UPROPERTY(VisibleAnyWhere, Category = "Power")  

  97.     float curPower;  

  98.     //基础移动速度  

  99.     UPROPERTY(EditAnyWhere, Category = "Speed")  

  100.     float baseMoveSpeed;  

  101.     //每点能量对移动速度转化率  

  102.     UPROPERTY(EditAnyWhere, Category = "Speed")  

  103.     float speedFactor;  

  104. };  

我们可以看到,上面编辑器自动生成了很多的代码,因为我们创建的这个Actor是继承于Character的,它已经具有了一些最基本的功能,包括行走等等.代码还是比较简答的,不难理解.我在另一篇文章中解释了如果用蓝图,怎么去实现人物的基本操作.有兴趣的可以顺便一起看了.


然后我们看看cpp文件:

  1. #include "BatteryCollect.h"  

  2. #include "BatteryCollectCharacter.h"  

  3. #include "Pickup.h"  

  4. #include "BatteryPickup.h"  

  5.   

  6. //////////////////////////////////////////////////////////////////////////  

  7. // ABatteryCollectCharacter  

  8.   

  9. ABatteryCollectCharacter::ABatteryCollectCharacter()  

  10. {  

  11.     //自动生成的代码.为该Actor附加基本的功能  

  12.     GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);  

  13.   

  14.     // set our turn rates for input  

  15.     BaseTurnRate = 45.f;  

  16.     BaseLookUpRate = 45.f;  

  17.   

  18.     // Don't rotate when the controller rotates. Let that just affect the camera.  

  19.     bUseControllerRotationPitch = false;  

  20.     bUseControllerRotationYaw = false;  

  21.     bUseControllerRotationRoll = false;  

  22.   

  23.     // 配置角色移动的实训个  

  24.     GetCharacterMovement()->bOrientRotationToMovement = true// Character moves in the direction of input...      

  25.     GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate  

  26.     GetCharacterMovement()->JumpZVelocity = 600.f;  

  27.     GetCharacterMovement()->AirControl = 0.2f;  

  28.   

  29.     //相机操作  

  30.     // Create a camera boom (pulls in towards the player if there is a collision)  

  31.     CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));  

  32.     CameraBoom->AttachTo(RootComponent);  

  33.     CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character      

  34.     CameraBoom->bUsePawnControlRotation = true// Rotate the arm based on the controller  

  35.   

  36.     // Create a follow camera  

  37.     FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));  

  38.     FollowCamera->AttachTo(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation  

  39.     FollowCamera->bUsePawnControlRotation = false// Camera does not rotate relative to arm  

  40.   

  41.     //我们的自定义操作  

  42.     //为角色创建一个方形大小的盒子,当我们按下收集按钮的时候,在这个范围内的东西会被收集  

  43.     CollectionSphere = CreateDefaultSubobject<USphereComponent>(TEXT("CollectionSphere"));  

  44.     CollectionSphere->AttachTo(RootComponent);  

  45.     CollectionSphere->SetSphereRadius(200);  

  46.   

  47.     // 初始化  

  48.     baseMoveSpeed = 20.0f;  

  49.     speedFactor = 0.75f;  

  50.     initialPower = 2000.0;  

  51.     curPower = initialPower;  

  52.     // Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)   

  53.     // are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)  

  54. }  

  55.   

  56. //////////////////////////////////////////////////////////////////////////  

  57. // Input  

  58.   

  59. void ABatteryCollectCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)  

  60. {  

  61.     // Set up gameplay key bindings  

  62.     check(InputComponent);  

  63.     InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);  

  64.     InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);  

  65.   

  66.     InputComponent->BindAction("Collect",IE_Pressed,this,&ABatteryCollectCharacter::CollectBattery);  

  67.   

  68.     InputComponent->BindAxis("MoveForward"this, &ABatteryCollectCharacter::MoveForward);  

  69.     InputComponent->BindAxis("MoveRight"this, &ABatteryCollectCharacter::MoveRight);  

  70.   

  71.     // We have 2 versions of the rotation bindings to handle different kinds of devices differently  

  72.     // "turn" handles devices that provide an absolute delta, such as a mouse.  

  73.     // "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick  

  74.     InputComponent->BindAxis("Turn"this, &APawn::AddControllerYawInput);  

  75.     InputComponent->BindAxis("TurnRate"this, &ABatteryCollectCharacter::TurnAtRate);  

  76.     InputComponent->BindAxis("LookUp"this, &APawn::AddControllerPitchInput);  

  77.     InputComponent->BindAxis("LookUpRate"this, &ABatteryCollectCharacter::LookUpAtRate);  

  78.   

  79.     // handle touch devices  

  80.     InputComponent->BindTouch(IE_Pressed, this, &ABatteryCollectCharacter::TouchStarted);  

  81.     InputComponent->BindTouch(IE_Released, this, &ABatteryCollectCharacter::TouchStopped);  

  82. }  

  83.   

  84.   

  85. void ABatteryCollectCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)  

  86. {  

  87.     // jump, but only on the first touch  

  88.     if (FingerIndex == ETouchIndex::Touch1)  

  89.     {  

  90.         Jump();  

  91.     }  

  92. }  

  93.   

  94. void ABatteryCollectCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)  

  95. {  

  96.     if (FingerIndex == ETouchIndex::Touch1)  

  97.     {  

  98.         StopJumping();  

  99.     }  

  100. }  

  101.   

  102. void ABatteryCollectCharacter::TurnAtRate(float Rate)  

  103. {  

  104.     // calculate delta for this frame from the rate information  

  105.     AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());  

  106. }  

  107.   

  108. void ABatteryCollectCharacter::LookUpAtRate(float Rate)  

  109. {  

  110.     // calculate delta for this frame from the rate information  

  111.     AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());  

  112. }  

  113.   

  114. void ABatteryCollectCharacter::MoveForward(float Value)  

  115. {  

  116.     if ((Controller != NULL) && (Value != 0.0f))  

  117.     {  

  118.         // find out which way is forward  

  119.         const FRotator Rotation = Controller->GetControlRotation();  

  120.         const FRotator YawRotation(0, Rotation.Yaw, 0);  

  121.   

  122.         // get forward vector  

  123.         const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);  

  124.         AddMovementInput(Direction, Value);  

  125.     }  

  126. }  

  127.   

  128. void ABatteryCollectCharacter::MoveRight(float Value)  

  129. {  

  130.     if ( (Controller != NULL) && (Value != 0.0f) )  

  131.     {  

  132.         // find out which way is right  

  133.         const FRotator Rotation = Controller->GetControlRotation();  

  134.         const FRotator YawRotation(0, Rotation.Yaw, 0);  

  135.       

  136.         // get right vector   

  137.         const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);  

  138.         // add movement in that direction  

  139.         AddMovementInput(Direction, Value);  

  140.     }  

  141. }  

  142.   

  143. //收集物品  

  144. void ABatteryCollectCharacter::CollectBattery()  

  145. {  

  146.     TArray<AActor*> overlapActors;  

  147.     //查找所有在盒子范围内的物品  

  148.     CollectionSphere->GetOverlappingActors(overlapActors);  

  149.     forint i = 0; i < overlapActors.Num(); i++ )  

  150.     {  

  151.         //尝试将他们转成Pickup类型的物体  

  152.         APickup* TestPickup = Cast<APickup>(overlapActors[i]);  

  153.         if(TestPickup && !TestPickup->IsPendingKill() && TestPickup->IsActive())  

  154.         {  

  155.             //如果转换成功,就执行该物体的收集方法(实际上会去调用TestPickup->Was_Collect_Implemetation方法)  

  156.             TestPickup->WasCollect();  

  157.             //收集过一次就不能再收集了  

  158.             TestPickup->SetActive(false);  

  159.             ABatteryPickup* TestBatteryPickup = Cast<ABatteryPickup>(TestPickup);  

  160.             if (TestBatteryPickup)   

  161.             {  

  162.                 //如果是电池收集物的话,就改变玩家的能量,由于这里要对玩家进行操作,所以放在了这里,如果对玩家没有影响的话,可以将这种行为放在具体类的实现中会更好.  

  163.                 float power = TestBatteryPickup->GetBatteryPower();  

  164.                 ChangeCurPower(power);  

  165.             }  

  166.         }  

  167.     }  

  168. }  

  169.   

  170. float ABatteryCollectCharacter::GetCurPower()  

  171. {  

  172.     return curPower;  

  173. }  

  174.   

  175. float ABatteryCollectCharacter::GetInitialPower()  

  176. {  

  177.     return initialPower;  

  178. }  

  179.   

  180. //改变玩家能量  

  181. void ABatteryCollectCharacter::ChangeCurPower(float power)  

  182. {  

  183.     //增加能量  

  184.     curPower += power;  

  185.     //改变玩家移动速度  

  186.     GetCharacterMovement()->MaxWalkSpeed = baseMoveSpeed + curPower*speedFactor;  

  187.     //改变玩家材质球颜色  

  188.     PlayPowerAddEffect();  

  189. }  

和.h文件一下.前面有大量的自动生成的代码.我们不必去关注那些.角色在生成的时候,其实我们主要做了初始化的操作.其次.当我们按下收集的按钮时,会触发收集的事件,收集的事件主要执行的功能就是将范围内的东西进行筛选,判断什么东西可以被收集,如果可被收集,而且该物体还是电池,那么久改变玩家的能量,改变能量会带来移动速度的变化,同时也会改变玩家身上的颜色,该方法在.h文件中定义的是BlueprintImpentation的方式,所以我们会在蓝图中实现它.


那么我们来看一会蓝图中我们是怎么生成这个特效的.打开ThirdPersonCpp/Bluepirints/ThirdPersonCharacter的蓝图.蓝图编写如下:

UE4示例项目学习:用C++编写BatteryCollecter UE4教程 第4张

当调用该方法后,会根据当前的能量值来角色角色表面的颜色.

到这里为止.我们的人物就应该能够收集东西了,那么接下来我们还剩下最后一步,也就是游戏状态的设定.所以我们来看一下游戏状态的文件是怎么写的

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/GameMode.h"
#include "BatteryCollectGameMode.generated.h"


//定义一个游戏状态的枚举

  1. UENUM(BlueprintType)  

  2. enum class EBatteryGameState  

  3. {  

  4.     EUnKnown,  

  5.     EWin,  

  6.     ELose,  

  7.     EPlaying  

  8. };  

  9.   

  10.   

  11. UCLASS(minimalapi)  

  12. class ABatteryCollectGameMode : public AGameMode  

  13. {  

  14.     GENERATED_BODY()  

  15.   

  16.   

  17. public:  

  18.     ABatteryCollectGameMode();  

  19.   

  20.   

  21.     virtual void Tick(float DeltaSeconds) override;  

  22.   

  23.   

  24.     virtual void BeginPlay() override;  

  25.   

  26.   

  27.     //获取胜利所需能量值  

  28.     UFUNCTION(BlueprintPure, Category = "Power")  

  29.     float GetPowerToWin();  

  30.   

  31.   

  32.     //获取当前游戏状态  

  33.     UFUNCTION(BlueprintPure, Category = "Power")  

  34.     EBatteryGameState GetCurGameState() const;  

  35.   

  36.   

  37.     //设定新的游戏状态  

  38.     UFUNCTION(Category = "Power")  

  39.     void SetCurGameState(EBatteryGameState NewState);  

  40.   

  41.   

  42.   

  43.   

  44. protected :  

  45.     //能量衰减速度  

  46.     float DecayRate;  

  47.   

  48.   

  49.     //赢得游戏所需要的能量值  

  50.     UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))  

  51.     float PowerToWin;  

  52.   

  53.   

  54.     //所有widget的集合  

  55.     UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Power", Meta = (BlueprintProtected = "true"))  

  56.     TSubclassOf<class UUserWidget> HUDWigetClass;  

  57.   

  58.   

  59.     //当前激活的widget  

  60.     UPROPERTY()  

  61.     class UUserWidget* CurrentWidget;  

  62.   

  63.   

  64. private :  

  65.     //当前的游戏状态  

  66.     EBatteryGameState GameState;  

  67.            

  68.     //所有的出生器的集合  

  69.     TArray<class ASpawnVolumn*> SpawnVolumns;  

  70.   

  71.   

  72.     //进入新状态要触发的事件  

  73.     void HandleNewGameState(EBatteryGameState NewState);  

  74.   

  75.   

  76.     //设置所有的出生器是否能产生收集物  

  77.     void SetAllSpawnVolumnState(bool NewState);  

  78. };  

在这里我们定义了一个游戏状态的枚举来标识我们的游戏状态,当能量达到一定值后会导致我们进入新状态,从而进行一些操作.该操作包括UI上的变化,还有出生器是否继续产生收集物等等,


cpp文件:


  1. // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.  

  2.   

  3. #include "BatteryCollect.h"  

  4. #include "BatteryCollectGameMode.h"  

  5. #include "BatteryCollectCharacter.h"  

  6. #include "Kismet/GameplayStatics.h"  

  7. #include "Blueprint/UserWidget.h"  

  8. #include "SpawnVolumn.h"  

  9.   

  10. ABatteryCollectGameMode::ABatteryCollectGameMode()  

  11. {  

  12.     // set default pawn class to our Blueprinted character  

  13.     static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));  

  14.     if (PlayerPawnBPClass.Class != NULL)  

  15.     {  

  16.         DefaultPawnClass = PlayerPawnBPClass.Class;  

  17.     }  

  18.     //设定能量衰减速率  

  19.     DecayRate = 100.0f;  

  20. }  

  21.   

  22. void ABatteryCollectGameMode::BeginPlay()  

  23. {  

  24.     Super::BeginPlay();  

  25.   

  26.     //设定初始状态  

  27.     SetCurGameState(EBatteryGameState::EUnKnown);  

  28.   

  29.     //记录所有的生成器  

  30.     TArray<AActor*> spawnVolumn;  

  31.     UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASpawnVolumn::StaticClass(), spawnVolumn);  

  32.     for (auto Actor : spawnVolumn)  

  33.     {  

  34.         ASpawnVolumn* volumn = Cast<ASpawnVolumn>(Actor);  

  35.         if (volumn)  

  36.         {  

  37.             SpawnVolumns.AddUnique(volumn);  

  38.         }  

  39.     }  

  40.   

  41.     //开始游戏  

  42.     SetCurGameState(EBatteryGameState::EPlaying);  

  43.     ABatteryCollectCharacter* character = Cast<ABatteryCollectCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));  

  44.     if (character)  

  45.     {  

  46.         PowerToWin = character->GetInitialPower()*1.25;  

  47.         CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), HUDWigetClass);  

  48.         if(CurrentWidget != nullptr)  

  49.         {  

  50.             CurrentWidget->AddToViewport();  

  51.         }  

  52.     }  

  53. }  

  54.   

  55.   

  56. void ABatteryCollectGameMode::Tick(float DeltaSeconds)   

  57. {  

  58.     Super::Tick(DeltaSeconds);  

  59.     //每一帧都对能量进行判断  

  60.     ABatteryCollectCharacter* character = Cast<ABatteryCollectCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));  

  61.     if (character)  

  62.     {  

  63.         float curPower = character->GetCurPower();  

  64.         float minusPow = DeltaSeconds * DecayRate;  

  65.         float nowPow = curPower - minusPow;  

  66.         //改变颜色  

  67.         if (nowPow >= 0)  

  68.         {  

  69.             character->ChangeCurPower(-minusPow);  

  70.         }  

  71.         else  

  72.         {  

  73.             character->ChangeCurPower(-curPower);  

  74.         }  

  75.         //根据能量判断游戏成功还是失败  

  76.         if(nowPow>=GetPowerToWin())  

  77.         {  

  78.             SetCurGameState(EBatteryGameState::EWin);  

  79.         }  

  80.         else if(nowPow >= 0)  

  81.         {  

  82.             SetCurGameState(EBatteryGameState::EPlaying);  

  83.         }  

  84.         else  

  85.         {  

  86.             SetCurGameState(EBatteryGameState::ELose);  

  87.         }  

  88.   

  89.     }  

  90. }  

  91.   

  92. float ABatteryCollectGameMode::GetPowerToWin()  

  93. {  

  94.     return PowerToWin;  

  95. }  

  96.   

  97. //设置游戏状态  

  98. void ABatteryCollectGameMode::SetCurGameState(EBatteryGameState NewState)  

  99. {  

  100.     if(GameState == NewState)  

  101.         return;  

  102.     //进入新状态要处理的操作  

  103.     HandleNewGameState(NewState);  

  104. }  

  105.   

  106. EBatteryGameState ABatteryCollectGameMode::GetCurGameState() const  

  107. {  

  108.     return GameState;  

  109. }  

  110.   

  111. void ABatteryCollectGameMode::HandleNewGameState(EBatteryGameState NewState)  

  112. {  

  113.     GameState = NewState;  

  114.     //设置出生器状态  

  115.     switch (NewState)  

  116.     {  

  117.     case EBatteryGameState::EPlaying:  

  118.     {  

  119.         SetAllSpawnVolumnState(true);  

  120.         break;  

  121.     }  

  122.     case EBatteryGameState::EWin:  

  123.     {  

  124.         SetAllSpawnVolumnState(false);  

  125.         break;;  

  126.     }  

  127.     case EBatteryGameState::ELose:  

  128.     {  

  129.         SetAllSpawnVolumnState(false);  

  130.         //播放失败动作,并禁用移动等操作  

  131.         APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0);  

  132.         if(PlayerController)  

  133.         {  

  134.             PlayerController->SetCinematicMode(truefalsefalsetruetrue);  

  135.         }  

  136.         ACharacter* MyCharacter = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);  

  137.         if(MyCharacter)  

  138.         {  

  139.             MyCharacter->GetMesh()->SetSimulatePhysics(true);  

  140.             MyCharacter->GetMovementComponent()->MovementState.bCanJump = false;  

  141.         }  

  142.         break;;  

  143.     }  

  144.     default:  

  145.         break;  

  146.     }  

  147. }  

  148.   

  149. //设置所有的出生器是否可以生成收集物  

  150. void ABatteryCollectGameMode::SetAllSpawnVolumnState(bool NewState)  

  151. {  

  152.     for (int i=0;i<SpawnVolumns.Num();i++)  

  153.     {  

  154.         SpawnVolumns[i]->SetShouldSpawn(NewState);  

  155.     }  

  156. }  

代码里面已经写的很清楚了..没有必要再多少了...到目前为止,整个教程就已经结束了.唯一缺乏的是UI上的展示,但我想UI上的展示和这次的教程本身也是独立的.我也就先不把它整理出来了.也许以后会有一篇单独的仔细点的UI教程.


最后一步就是要去项目设置里面设置好我们要使用的游戏模式为我们前面所自定义的游戏模式.还有就是设置好C键对应收集的这一个行为.

最后再进行一下总结:

这个项目主要有3个4个部分组成

1.收集物

2.出生器

3.自定义角色

4.自定义游戏模式

收集物和出生器负责将收集物产生到场景中,由人物来进行收集,当我们人物进行收集以后,会产生能量的变化,能量的变化会带来游戏模式的代表,一旦游戏模式改变,就会有新的操作要执行.可能是胜利或者失败等等.整体的思路大概就是这样了.上几张效果图

正常游戏模式:

UE4示例项目学习:用C++编写BatteryCollecter UE4教程 第5张

失败:

UE4示例项目学习:用C++编写BatteryCollecter UE4教程 第6张

胜利:

UE4示例项目学习:用C++编写BatteryCollecter UE4教程 第7张


我们下次再见...嘿嘿嘿...


评论列表暂无评论
发表评论