UE4編程之C++建立一個FPS工程(二)角色網格、動畫、HUD、子彈類

轉自:http://blog.csdn.net/u011707076/article/details/44243103數組

 

緊接上回,本篇文章將和你們一同整理總結UE4關於角色網格、動畫、子彈類和HUD的簡單實現。瀏覽器

(五)角色添加網格ssh

  Character類爲咱們默認建立了一個SkeletaMeshComponent組件,因此咱們僅僅須要作的就是使用哪個靜態網格模型。接下來咱們爲咱們的FPSCharacter類建立一個藍圖,這樣咱們能夠簡單的把資源指定給靜態網格模型這裏,而且方便之後操做添加的組件。做爲開始,咱們首先要導入一個第三人稱的靜態網格模型,最後咱們設置成兩個網格,一個是咱們本身看的,另一個是其餘人看到的。編輯器

 1.添加一個完整的全身的網格,如今點擊這裏載入資源吧。我把咱們要用到的全部資源所有放進去了,GenericMale.fbx文件就是有全身網格的,HeroFPP.FBX是等下咱們要用到的本身看本身只有胳膊和武器的網格,Sphere.FBX是個球體網格模擬子彈,crosshair.TGA是瞄準的十字準心,FPP_Animations文件夾存放的是胳膊和武器的那個網格的用的的動畫。這是個人目錄結構,僅供參考。函數

  

   你們把他們都載入進來吧,注意一點就是,注意載入所有,好比載入網格,須要勾選載入材質,載入動做的時候,要選擇網格,以下:oop

  

        好了,關於資源載入的問題,就一次性說清,之後就不說了。測試

      2.在藍圖文件夾內,新建藍圖動畫

        

  

  這裏注意!在下拉框中選擇FPSCharacter,使其做爲藍圖的父類,給藍圖命名爲「BP_FPSCharacter」,打開它。this

  3.選中Mesh,指定網格,並把Mesh的z軸值改成-88spa

   

  完成之後就應該是這樣了,爲何是-88?由於是已經調整好的數值,當使用本身的網格模型時,須要本身調整,最終目標是 使得網格被外面的膠囊組件包裹,而且方向爲箭頭所指向的方向。這將會使得模型在世界中正確行走,方向正常。能夠直接在視圖中調整,也能夠直接設置數值。

 4.編譯保存藍圖,如今,咱們要告訴咱們的遊戲模型使用最新咱們建立的藍圖類來做爲遊戲角色的pawn物體,而不是咱們以前建立的父類:FPSCharacter。返回VS,在模式的CPP文件構造函數中,刪除

 

[cpp]  view plain copy print ?
 
  1. DefaultPawnClass = AFPSCharacter::StaticClass();  

  並添加以下代碼:

 

 

[cpp]  view plain copy print ?
 
  1. AMyNewFPSGameMode::AMyNewFPSGameMode(const FObjectInitializer& ObjectInitializer)  
  2.     : Super(ObjectInitializer)  
  3. {  
  4.     //DefaultPawnClass = AMyNewFPSCharacter::StaticClass();  
  5.   
  6.     static ConstructorHelpers::FObjectFinder<UBlueprint> PlayerPawnObject(TEXT("Blueprint'/Game/Blueprints/BP_FPSCharacter.BP_FPSCharacter'"));  
  7.     if (PlayerPawnObject.Object != NULL)  
  8.     {  
  9.         DefaultPawnClass = (UClass*)PlayerPawnObject.Object->GeneratedClass;  
  10.     }  
  11. }  

  (這裏提早說一下,你們無視個人類名,按照本身的來便可)
   這裏有很是重要的一個知識點:在C++中引用藍圖(這樣,就能夠打通C++和藍圖了!),更準確的說應該是引用資源,這裏的藍圖是以資源出現,等下咱們也會發如今引用十字瞄準點的時候,使  用的是一樣的方法,那就是: 

 

 

[cpp]  view plain copy print ?
 
  1. static ConstructorHelpers::FObjectFinder<UBlueprint> PlayerPawnObject(TEXT("Blueprint'/Game/Blueprints/BP_FPSCharacter.BP_FPSCharacter'"));  

  TEXT裏面的是資源的路徑,獲取資源路徑有個小技巧,就是在內容瀏覽器中右鍵資源,選中複製引用命令,就會把具體的位置複製到剪貼板中了。

 

  5.回到編輯器,選擇編譯,等待編譯成功進入遊戲測試。若是你移動攝像機,你會發現角色的影子,按下F1+Shift鍵,來得到鼠標指針,點擊上面的彈出,這  時候就能夠在世界中隨意的移動攝像機來看到角色的網格。

(六)更改攝像機試圖(添加攝像機組件)

     在上一節的結尾,默認的攝像機是在網格的脖子裏,如今咱們來建立一個正確的攝像機,而且能夠設置攝像機的一些屬性好比位置和視圖。咱們經過給咱們的FPSCharacter類添加一個攝像機組件來完成(CameraComponent )。首先,咱們爲FPSCharacter添加一個屬性來引用攝像機組件。

   1.在角色.h文件裏面添加下面的代碼來添加一個公有變量FirstPersonCameraComponent。

 

[cpp]  view plain copy print ?
 
  1. /** First person camera 
  2.     * 爲攝像機新建變量,引用攝像機。在構造函數中作初始化------C++中添加組件!,藍圖中賦值。 
  3.     */  
  4.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)  
  5.         TSubobjectPtr<UCameraComponent> FirstPersonCameraComponent;  

  2.咱們在FPSCharacter的構造函數中建立真正的組件,在FPSCharacter的構造函數中添加以下代碼,來建立一個攝像機組件而且把他依附到膠囊組件上。

 

 

[cpp]  view plain copy print ?
 
  1. AMyNewFPSCharacter::AMyNewFPSCharacter(const FObjectInitializer& ObjectInitializer)  
  2.     : Super(ObjectInitializer)  
  3. {  
  4.     /********************建立、初始化攝像機********************************/  
  5.     //建立攝像機組件  
  6.     FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));  
  7.     FirstPersonCameraComponent->AttachParent = CapsuleComponent;  
  8.   
  9.     // 攝像機的位置稍微比眼睛要高  
  10.     FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 50.0f + BaseEyeHeight);  
  11.     // 容許玩家控制旋轉  
  12.     FirstPersonCameraComponent->bUsePawnControlRotation = true;  
  13. }  

 

  3.咱們來調整攝像機的位置到眼睛的位置,在藍圖中也能夠做調整,這裏只用調整他的位置信息,不用調整旋轉,由於旋轉會被鼠標改變。這部分代碼就是上面的下兩行。

  4.好了,進遊戲測試吧,攝像機應該在角色的頭上,因此當你向下看的時候,將會看到角色的頭部!

(七)添加第一人稱網格

   常規的FPS要準備和處理兩套網格,一套是其餘人看到的本身,可是對於本身來講它是隱藏的,這個就是全身的網格;還有一種是隻有武器和手臂的網格,他們要依附在攝像機上,當玩家是第一人稱的時候才被看到。爲了實現這個,咱們首先要保持現有的網格,給它命名爲「Mesh」,而且爲咱們的第一人稱網格準備一個新的靜態網格模型。

  1.角色的.h文件中新建變量來引用新的網格。

 

[cpp]  view plain copy print ?
 
  1. /** 
  2.     * 出生的時候的網格,只有手臂和武器。 也是添加一個變量引用新的網格 
  3.     */  
  4.     UPROPERTY(VisibleDefaultsOnly, Category = Mesh)  
  5.         TSubobjectPtr<USkeletalMeshComponent> FirstPersonMesh;  

 2.在構造函數中,添加以下代碼來建立配置網格

 

 

[cpp]  view plain copy print ?
 
  1. ///********************建立、初始化只有手和武器的網格********************************/  
  2.     //// 建立一個網格組件,他只有手臂和武器  
  3.     FirstPersonMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));  
  4.     FirstPersonMesh->SetOnlyOwnerSee(true);// 僅僅只有這個Pawn才能夠看到  
  5.     FirstPersonMesh->AttachParent = FirstPersonCameraComponent;  
  6.     FirstPersonMesh->bCastDynamicShadow = false;  
  7.     FirstPersonMesh->CastShadow = false;  
  8.   
  9.   
  10.     ///********************除了本身,其餘人看到全身*******************************/  
  11.     //// 除了本身其餘人都能看到  
  12.     Mesh->SetOwnerNoSee(true);  

  上面其一部分代碼中,說明了僅僅只有這個Pawn才能夠看到,依附到第一人稱攝像機,取消陰影;後一部分代碼實現的功能呢,看註釋把。

 

 3.編譯C++代碼,打開BP_FPSCharacter 藍圖,選擇FirstPersonMesh組件,右面Detail面板中指定。調整其位置屬性爲:{240,0,35},旋轉屬性{-180, 50, -180}.最後應該獲得這麼一張圖

  

  OK,進入遊戲測試吧,

    

   F1+Shift 而且選擇彈出鼠標,這是你就不會再佔有角色了,這個時候,你能夠隨意走動,而且看到兩我的稱的網格.

   

(八)添加彈藥,實現射擊

   既然已經建立了角色,那咱們給角色實現一個簡單的武器,一枚簡單的長得很像手榴彈的子彈將會從屏幕中心射出,直到他碰到世界裏面的物體,纔會停下來。接下來咱們便添加輸入的事件,事件映射,而且爲子彈建立一個新的類。

  1.處理輸入,這裏就不贅述,簡述以下:添加事件映射,Fire,鼠標左鍵。

  2.添加子彈類,使用UE4的C++類嚮導,一樣簡述:父類爲Actor,命名FPSProjectile。

     3.首先,咱們應該肯定一個簡化表示用於碰撞模擬的物理對象。在這裏,咱們添加一個球體組件USphereComponent,在.h文件裏面定義變量:

 

[cpp]  view plain copy print ?
 
  1. /** 膠囊碰撞組件 */  
  2.     UPROPERTY(VisibleDefaultsOnly, Category = Projectile)  
  3.         TSubobjectPtr<USphereComponent> CollisionComp;      

  4.在CPP的構造函數裏面添加組件,咱們將把他做爲根組件,由於咱們要模擬它,而且在藍圖裏面稍後爲他添加可視化組件。

 

 

[cpp]  view plain copy print ?
 
  1. AFPSProjectile::AFPSProjectile(const FObjectInitializer& ObjectInitializer)  
  2.     : Super(ObjectInitializer)  
  3. {  
  4.     // 使用球體表明子彈  
  5.     CollisionComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));  
  6.     CollisionComp->InitSphereRadius(15.0f);  
  7.     RootComponent = CollisionComp;    
  8. }  

 這裏是第二次在C++裏面直接添加組件了,總結一下過程就是:聲明變量--添加組件!

 

 5.UE4裏面有一個自帶的組件叫作子彈移動組件ProjectileMovementComponent ,他能夠用於簡單的彈道式的移動,咱們把它添加到子彈類裏面。

  (1)在.h裏面添加變量

 

[cpp]  view plain copy print ?
 
  1. /**子彈移動組件 */  
  2.     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Movement)  
  3.         TSubobjectPtr<class UProjectileMovementComponent> ProjectileMovement;  

  (2).cpp構造函數裏面添加

 

 

[cpp]  view plain copy print ?
 
  1. // 使用子彈移動組件ProjectileMovementComponent控制子彈  
  2.     ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileComp"));  
  3.     ProjectileMovement->UpdatedComponent = CollisionComp;  
  4.     ProjectileMovement->InitialSpeed = 3000.f;  
  5.     ProjectileMovement->MaxSpeed = 3000.f;  
  6.     ProjectileMovement->bRotationFollowsVelocity = true;  
  7.     ProjectileMovement->bShouldBounce = true;  
  8.     ProjectileMovement->Bounciness = 0.3f;  

 6.日後想想,咱們還須要一個功能經過初速度設置來「啓動」咱們的彈丸。咱們自定義一個函數來處理他。首先.h裏面聲明一個公有函數:

 

 

[cpp]  view plain copy print ?
 
  1. /** 子彈初速度*/  
  2.     void InitVelocity(const FVector& ShootDirection);  

  cpp裏面添加實現函數

 

 

[cpp]  view plain copy print ?
 
  1. void AFPSProjectile::InitVelocity(const FVector& ShootDirection)  
  2. {  
  3.     if (ProjectileMovement)  
  4.     {  
  5.         // set the projectile's velocity to the desired direction 在所需方向上設置子彈的速度  
  6.         ProjectileMovement->Velocity = ShootDirection * ProjectileMovement->InitialSpeed;  
  7.     }  
  8. }  

 7.既然速度已經制定了,接下來咱們只須要設置一個啓動的方向,首先在角色類中添加函數處理輸入:

 

  .h:

 

[cpp]  view plain copy print ?
 
  1. void OnFire();  

  .cpp文件中添加實現函數,在實現這個函數的時候,有兩點須要考慮:

 

   (1)在哪裏產生子彈

   (2)產生什麼樣的子彈(即傳入子彈的類)。

  第一個問題,爲了制定產生的位置,咱們須要一個攝像機空間的偏移量做爲能夠編輯的參數,所以咱們能夠在角色藍圖BP_FPSCharacter 裏面設置它,而後就能夠根據這個數據來計算子彈的初始化速度。主要是第二個,這裏也是一個重點,簡單來講,它的實質是兩個C++的類是什麼樣的關係,如何通訊!咱們解決的辦法是,在C++裏面定義一個共有變量,而後等下再藍圖裏面指定它。因此在.h裏面添加這個共有變量把~

  角色.h  

 

[cpp]  view plain copy print ?
 
  1. /***************************這兩項要在藍圖裏指定******************************************/  
  2.     /** 須要實例化的子彈類 */  
  3.     UPROPERTY(EditDefaultsOnly, Category = Projectile)  
  4.         TSubclassOf<class AFPSProjectile> ProjectileClass;  
  5.   
  6.     /** Gun muzzle's offset from the camera location 
  7.     槍口到攝像機的位置偏移量*/  
  8.     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)  
  9.         FVector MuzzleOffset;  

  解釋一下變量前面的兩個宏,裏咱們使用只能在藍圖設置默認EditDefaultsOnly 的說明符,這就意味着你只能在藍圖類裏面設置子彈類爲默認,而在不是藍圖的每個實例中. 

 

  OnFire將會包含如下幾步:

 

  • 由於咱們的子彈產生位置是由相機空間決定,因此在咱們真正計算子彈位置以前須要查詢攝像機的位置信息.
  • 嘗試實例化子彈.
  • 使用咱們定義的函數給子彈一個初始化的速度.

 

  OK,最後就是這個函數的實現了!

 

[cpp]  view plain copy print ?
 
  1. void AMyNewFPSCharacter::OnFire()  
  2. {  
  3.     // try and fire a projectile  
  4.     if(ProjectileClass !=NULL)  
  5.     {  
  6.         // Get the camera transform  
  7.     FVector CameraLoc;  
  8.     FRotator CameraRot;  
  9.     GetActorEyesViewPoint(CameraLoc, CameraRot);  
  10.       
  11.     // MuzzleOffset is in camera space, so transform it to world space before offsetting from the camera to find the final muzzle position  
  12.     FVector const MuzzleLocation = CameraLoc + FTransform(CameraRot).TransformVector(MuzzleOffset);  
  13.     FRotator MuzzleRotation = CameraRot;  
  14.     MuzzleRotation.Pitch += 10.0f;// skew the aim upwards a bit  
  15.   
  16.     UWorld*const World = GetWorld();   
  17.     if (World)  
  18.     {  
  19.         FActorSpawnParameters SpawnParams;  
  20.         SpawnParams.Owner = this;  
  21.         SpawnParams.Instigator = Instigator;// spawn the projectile at the muzzle  
  22.         AFPSProjectile*const Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);  
  23.   
  24.         if (Projectile)  
  25.         {  
  26.             // find launch direction  
  27.             FVector const LaunchDir = MuzzleRotation.Vector();  
  28.             Projectile->InitVelocity(LaunchDir);  
  29.         }  
  30.     }  
  31. }  
  32. }  

  這個函數有點複雜,得須要好好理解一下。最後注意在角色cpp裏面添加頭文件

 

 

[cpp]  view plain copy print ?
 
  1. #include "FPSProjectile.h"  

  8.編譯代碼,回到編輯器,最後一步爲子彈建立藍圖添加網格,建立新的藍圖,父類FPSProjectile,藍圖命名BP_FPSProjectile,在藍圖裏面手動添加靜態網格組件,命名ProjectileMesh,在右面的Detail面板中指定Mesh值,並調整其大小爲:0.09(x、y、z都是)。另外把子彈網格組件ProjectileMesh的碰撞關閉,由於咱們使用球形碰撞體來檢測碰撞而不是網格物體。

 

  

  9.接下來,咱們就要爲剛剛C++裏面添加的兩個共有變量賦值了!打開角色藍圖,點擊頭上的ClassDefault,而後在右面,你就能夠看到這兩個變量了。

  分別按照上面賦值!以後,進入遊戲測試吧!應該但是打出子彈了!

(九)設置子彈的碰撞和生命週期

 

  如今,咱們須要考慮兩件事情

  • 咱們打出的子彈如今是不會消失的, Scene Outliner面板告訴了咱們
  • 彈不會產生碰撞。
  第一件事,幸運的是,UE4全部的Actors都有一個生命的限制,被一個默認生命期限限制,因此在子彈的構造函數中添加以下代碼控制生命,也能夠在藍圖的default中手動添加。  
[cpp]  view plain copy print ?
 
  1. // 子彈生命週期(全部的Actor都有的參數)  
  2.     InitialLifeSpan = 3.0f;  

     接下來,就要解決子彈的碰撞問題了。這部分問題,我看到的材料有點複雜,理解的不是很透徹,得多操做幾遍才能熟悉,我會把英文原文附上,因此熟能生巧...

   1.UE4有幾個有用的碰撞渠道,並且還提供了一些咱們能夠定製的碰撞渠道,如今咱們來自定義爲彈丸碰撞渠道,這樣場景中的一切均可以選擇如何在咱們的遊戲中和一個子彈的相互做用。

   在引擎配置.ini( DefaultEngine.ini,位於FPSProject > Config)的下方添加下面的代碼:

 

[cpp]  view plain copy print ?
 
  1. [/Script/Engine.CollisionProfile]  
  2.   +DefaultChannelResponses=(Channel=ECC_GameTraceChannel1, Name=Projectile)  

  這其實是重命名了通用的渠道爲「Projectile」,咱們要在本地代碼中重命名它,來保證事情的持續性和可讀性。這其實是重命名了通用的渠道爲「Projectile」,咱們要在本地代碼中重命名它,來保證事情的持續性和可讀性。

 

  2.UE4也有一個功能叫作碰撞分佈,實質上是引擎總體使用的預先捆綁的碰撞的設置。日後想一下,或許咱們能夠在咱們的遊戲有不少類型的彈丸,若是保持一致的碰撞設置其中多是容易出錯的,因此咱們定義爲彈一個新的文件。

      繼續在DefaultEngine.ini,添加代碼:

 

[cpp]  view plain copy print ?
 
  1. +Profiles=(Name="Projectile", CollisionEnabled=QueryOnly,ObjectTypeName=Projectile, CustomResponses=( \  
  2.  (Channel=Static,            Response=ECR_Block), \  
  3.  (Channel=PawnMovement,      Response=ECR_Block), \  
  4.  (Channel=Dynamic,           Response=ECR_Block), \  
  5.  (Channel=PhysicsBody,       Response=ECR_Block), \  
  6.  (Channel=VehicleMovement,   Response=ECR_Block), \  
  7.  (Channel=Destructible,      Response=ECR_Block) \  
  8.  ))  

  This profile means that the projectile will be blocked by Static Actors, Pawns, Dynamic Actors, Actors simulating Physics, Vehicles, and Destructible Actors. 這個分佈意味着,子彈能夠被靜態物體,Pawns, Dynamic Actors, Actors simulating Physics, Vehicles, and Destructible Actors等等這些物體阻擋。

 

 3.如今咱們來設置咱們的子彈使用這個分佈,在子彈構造函數中,添加下面代碼:

 

[cpp]  view plain copy print ?
 
  1. //使用自定義的碰撞分佈  
  2. CollisionComp->BodyInstance.SetCollisionProfileName("Projectile");  

  ok,編譯一下代碼吧。

 

(十)子彈和世界的物體交互

 既然咱們已經能夠發現咱們子彈的碰撞相互做用,咱們能夠決定怎麼對她作出反應,在咱們的子彈碰撞設置中,咱們已經設置了對塊的相互碰撞,接下來咱們要爲子彈類添加一個叫作OnHit的函數來處理這些事件。

  1.自定義一個函數OnHit。在子彈的.h文件下添加聲明

 

[cpp]  view plain copy print ?
 
  1. /** 當子彈碰到其餘物體時被調用*/  
  2.     UFUNCTION()  //這個必須有  
  3.     void OnHit(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);  

  2.cpp裏面實現它

 

 

[cpp]  view plain copy print ?
 
  1. void AFPSProjectile::OnHit(AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)  
  2. {  
  3.     if (OtherActor && (OtherActor != this) && OtherComp)  
  4.     {  
  5.         OtherComp->AddImpulseAtLocation(ProjectileMovement->Velocity * 100.0f, Hit.ImpactPoint);  
  6.     }  
  7. }  

 3.委託,如今咱們須要把這個函數交給子彈的球形組件的OnComponentBeginOverlap 事件委託,因此在子彈的構造函數中繼續添加下面代碼:

 

 

[cpp]  view plain copy print ?
 
  1. CollisionComp->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);  

 最後,你的子彈構造函數應該是這個樣子:

 

 

[cpp]  view plain copy print ?
 
  1. AFPSProjectile::AFPSProjectile(const FObjectInitializer& ObjectInitializer)  
  2.     : Super(ObjectInitializer)  
  3. {  
  4.     // 使用球體表明子彈  
  5.     CollisionComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));  
  6.     CollisionComp->InitSphereRadius(15.0f);  
  7.     RootComponent = CollisionComp;  
  8.     //使用自定義的碰撞分佈  
  9.     CollisionComp->BodyInstance.SetCollisionProfileName("Projectile");  
  10.     //碰撞事件委託  
  11.     CollisionComp->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);  
  12.   
  13.     // 使用子彈移動組件ProjectileMovementComponent控制子彈  
  14.     ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileComp"));  
  15.     ProjectileMovement->UpdatedComponent = CollisionComp;  
  16.     ProjectileMovement->InitialSpeed = 3000.f;  
  17.     ProjectileMovement->MaxSpeed = 3000.f;  
  18.     ProjectileMovement->bRotationFollowsVelocity = true;  
  19.     ProjectileMovement->bShouldBounce = true;  
  20.     ProjectileMovement->Bounciness = 0.3f;  
  21.   
  22.     // 子彈生命週期(全部的Actor都有的參數)  
  23.     InitialLifeSpan = 3.0f;  
  24. }  

    好咧,編譯代碼!

 

    4.回到編輯器,爲了有一個對比的東西,咱們選擇地面物體SM_Template_Map_Floor這個網格,而後複製一份,改變他的大小爲{0.2, 0.2, 3.0},位置爲:{-230, 0, 160}.接下來,在Detail面板中,找到Physical選項,勾選計算物理Simulate Physics.而後就開始測試吧!

 5.咱們測試的時候回遇到不少問題,退出遊戲,提示

  

  不要懼怕,咱們更具這裏的信息記錄更改就行,通常就是說,某一個網格沒有勾選物理選項。這裏好像有個問題,就是最後提示地面要模擬物理個警告咱們不用管它。不然,地面就直接掉下去了。測試到最後的結果是,咱們能夠用子彈把這個正方體打下去!

 

(十一)增添十字錨點

    UE4有一個基類HUD咱們能夠擴展。

  1.新建一個類,父類HUD,命名FPSHUD

  2.咱們想要在屏幕的中央畫一個十字的瞄準圖片,首先咱們添加對貼圖資源的引用,其實引用方法和藍圖同樣!

   (1)新建一個變量來引用它,在HUD的.h文件裏面添加變量私有的就行:

 

[cpp]  view plain copy print ?
 
  1. private:  
  2.     /**瞄準十字貼圖 */  
  3.     UTexture2D* CrosshairTex;  

   (2)咱們在構造函數中引用資源,提醒一下,能夠在內容瀏覽器中經過右鍵複製引用的方式找到資源的位置.

[cpp]  view plain copy print ?
 
  1. AFPSHUD::AFPSHUD(const FObjectInitializer& ObjectInitializer)  
  2.     : Super(ObjectInitializer)  
  3. {  
  4.     // 引用賦值  
  5.     static ConstructorHelpers::FObjectFinder<UTexture2D> CrosshairTexObj(TEXT("Texture2D'/Game/Texture/crosshair.crosshair'"));  
  6.     CrosshairTex = CrosshairTexObj.Object;  
  7. }  

 

3.HUD基類有一個咱們能夠重寫的虛函數叫作DrawHUD,來增長咱們自定義的須要往屏幕上添加的內容,咱們如今來重寫它!

 HUD.H裏面添加

 

[cpp]  view plain copy print ?
 
  1. virtual void DrawHUD() OVERRIDE;  

 cpp裏面實現

 

 

[cpp]  view plain copy print ?
 
  1. void AFPSHUD::DrawHUD()  
  2. {  
  3.     Super::DrawHUD();  
  4.       
  5.     // Draw very simple crosshair  
  6.       
  7.     // 找到屏幕可繪區域的中心  
  8.     FVector2D Center(Canvas->ClipX *0.5f, Canvas->ClipY *0.5f);  
  9.       
  10.     //計算一下偏移  
  11.     FVector2D CrosshairDrawPosition((Center.X-(CrosshairTex->GetSurfaceWidth()*0.5)),(Center.Y - (CrosshairTex->GetSurfaceHeight()*0.5f)));  
  12.       
  13.     // 畫出來  
  14.     FCanvasTileItem TileItem(CrosshairDrawPosition, CrosshairTex->Resource, FLinearColor::White);  
  15.     TileItem.BlendMode = SE_BLEND_Translucent;  
  16.     Canvas->DrawItem(TileItem);  
  17. }  

 

 3.在遊戲模式的構造函數裏面添加

 

[cpp]  view plain copy print ?
 
  1. HUDClass = AFPSHUD::StaticClass();  
[cpp]  view plain copy print ?
 
  1. AMyNewFPSGameMode::AMyNewFPSGameMode(const FObjectInitializer& ObjectInitializer)  
  2.     : Super(ObjectInitializer)  
  3. {  
  4.     //DefaultPawnClass = AMyNewFPSCharacter::StaticClass();  
  5.   
  6.     static ConstructorHelpers::FObjectFinder<UBlueprint> PlayerPawnObject(TEXT("Blueprint'/Game/Blueprints/BP_FPSCharacter.BP_FPSCharacter'"));  
  7.     if (PlayerPawnObject.Object != NULL)  
  8.     {  
  9.         DefaultPawnClass = (UClass*)PlayerPawnObject.Object->GeneratedClass;  
  10.     }  
  11.   
  12.     HUDClass = AFPSHUD::StaticClass();  
  13. }  

  注意要寫上頭文件。

 

  好了,到了這一步,就能夠測試了!應該沒問題!

(十二) 給角色添加動畫

   1.建立動畫藍圖,命名Arms_AnimBP ,選擇父類:AniInstance  目標骨架爲HeroFPP_Skeleton 

   

  2.咱們須要建立一個狀態機來設置這些動畫的狀態和轉化,可是要想清楚怎麼樣來驅動這個狀態機。這裏咱們須要處理兩個事情:1.人物是否是在走路,人物是否是在空中。咱們給動畫藍圖添加兩個變量來存儲這些信息。更多其餘建立的方法,能夠參考藍圖文檔。

   接下來添加兩個布爾變量:bIsRunning,bIsFalling   

 3.接下來,咱們看動畫藍圖裏面的第一張圖表:事件圖表

  (1)圖表中添加第一個事件節點: Event Blueprint Update Animation 

     這個節點容許咱們當動畫更新是,更新咱們設置的變量,他們是和整個遊戲狀態是同步的。

     
  (2)咱們能夠經過詢問角色的CharacterMovementComponent角色移動組件來爲咱們的變量賦正確的數值。爲了實現這一點,咱們須要添加一個動畫控制角色的引用。添加節點:Try Get Pawn Owner,連線:Cast to Character  (中文爲:類型轉換爲Character)

    

 (3)以下圖,在拖出一個節點:中文叫作得到CharacterMovement,繼續添加節點Get Movement Mode.   

       (4)如今咱們能夠查詢CharacterMovementComponent's 組件的MovementMode 來設置bIsFalling 變量是否是真,當咱們在降落的狀態的時候。連出新節點: Equal (Enum).(中文:等於枚舉值),設置其屬性爲Falling,得到變量bIsFalling,最後鏈接,以下圖   

 (5)接着,爲了決定咱們是在行走或是在站立,咱們須要得到角色的速度而且設置bIsRunning 爲真,固然是在值大於零的時候。反之,先得到速度,若是角色不是站立,那麼速度數組的長度會大於零。因此以下圖,繼續添加節點Vector Length

    最後的最後,藍圖張這個樣子:

    4.接下來,來看動畫藍圖的第二個圖表:動畫圖表

  (1)右鍵,新建狀態機,重命名爲Arms State Machine.如上圖連線。

    

   (2)雙擊ArmStateMachine狀態機節點來編輯它的圖表。

      

    在這個圖表中咱們須要五個狀態,接下來處理第一個狀態

      (1)圖表右鍵,添加狀態,命名爲Idle,雙擊進入狀態的圖表.

      (2)圖表中右鍵,查找Idle,添加Play FPP_Idle節點,以下圖鏈接:

     

  (3)OK,第一個狀態添加成功,接下來,按照一樣的方法添加另外的四個狀態    

 

  • Run
  • JumpStart
  • JumpEnd
  • JumpLoop
  最後效果:

 

   (4)接下來,咱們來添加狀態之間的轉換,其實這部分,說白了,就是連線,加轉換條件。

      <1>Entry->Idle

      <2>Idle->Run,雙擊這個線,在裏面添加轉換的條件

        

        雙擊連線,進入轉換規則的圖表,從變量欄中拉出bIsRunning,並得到,而後按照下圖連接

        

      <3>Run->Idle

        轉換條件:

       

     <4>Idle->JumpStart

      

    <5>Run->JumpStart

      

   <6>JumpStart->JumpLoop

     

     這裏有一點要注意:從JumpStart到JumpLoop發生在JumpStart立刻就要完成的時候,因此在轉換的規則中,咱們添加一個節點:FPP_JumpStart的剩餘時間<=0.1的節點,而後如上圖鏈接。

   <7>JumpLoop->JumpEnd

     

  <8>JumpEnd->Idle

    

  (5)最終的動畫圖表以下:    

 5.聯繫動畫藍圖和角色藍圖。(兩個藍圖的鏈接!)

  (1)編譯、保存動畫藍圖

  (2)打開角色藍圖BP_FPSCharacter 

  (3)看下圖:

 

 最後最後最後啦!!!!測試你的遊戲吧!沒問題!

  好了,到目前爲止,咱們的這個第一人稱射擊類遊戲就算完成了!

(十三) 總結一下今天

    今天作了以下一些事情:

    1.給角色添加網格,添加自定義攝像機,調整視角,添加第二套網格

  2.添加彈藥,實現射擊
   (1)添加彈藥類,實例化彈藥
   (2) 子彈碰撞和生命週期
 3.添加HUD 十字瞄準點
 4.給角色添加動畫。
 
  作了不少事,其實最重要的是,應該解決了一些童鞋心中的疑惑,好比:
(一)C++和藍圖應該怎麼互相有聯繫呢?
  答案:能夠用藍圖來繼承C++的類,這只是他們聯繫的一張方式,之後,咱們甚至能夠本身添加節點,完善藍圖。
(二)C++如何訪問藍圖
  答案:經過引用資源這種方式
(三)C++也能夠直接添加組件,方式是:
    1.添加變量
    2.構造函數中建立組件
 
第二部分:全篇總結
 
(一)整個流程:
 
第一步:建立工程,並自定義GameMode
    1.建立空白工程
    2.設置啓動關卡.
    3.自定義GameMode
    4.修改啓動遊戲模式爲GameMode
 
1.建立關卡
2.建立遊戲模式
3.製做一個角色
4.控制輸入:映射軸(鍵盤WASD--前進,鼠標X,Y--向上看,向右看),映射事件(Space--Jump)

仍是那一套,進入遊戲測試。

這裏總結一下:前面這麼多處理輸入控制的函數,其實每一類控制要想完成都須要三部:

1.處理映射:在項目設置中處理映射,包括軸映射(鍵盤鼠標均可以),事件映射。

1.添加函數:這裏要在角色的.h和.cpp文件裏都要添加,一個是聲明一個是實現

2.函數和輸入綁定!這一步,在角色重寫的那個設置玩家輸入組件的函數裏面實現。

 

5.給角色添加網格,添加自定義攝像機,調整視角,添加第二套網格

6.添加彈藥,實現射擊
  (1)添加彈藥類,實例化彈藥
  (2) 子彈碰撞和生命週期
7.添加HUD 十字瞄準點
8.給角色添加動畫。
 
 
(二)我已近把涉及到的這幾個類傳上去了,僅供參考:
    1.http://pan.baidu.com/s/1dDpOhsH
    2.你們也能夠直接新建一個C++的第一人稱模板類的項目,啥也別作,打開看看,其實內容是差很少的。
相關文章
相關標籤/搜索