UE4编程之C++创建一个FPS工程(二)角色网格、动画、HUD、子弹类
转自:http://blog.csdn.net/u011707076/article/details/44243103
紧接上回,本篇文章将和大家一同整理总结UE4关于角色网格、动画、子弹类和HUD的简单实现。
(五)角色添加网格
Character类为我们默认创建了一个SkeletaMeshComponent组件,所以我们仅仅需要做的就是使用哪一个静态网格模型。接下来我们为我们的FPSCharacter类创建一个蓝图,这样我们可以简单的把资源指定给静态网格模型这里,并且方便以后操作添加的组件。作为开始,我们首先要导入一个第三人称的静态网格模型,最后我们设置成两个网格,一个是我们自己看的,另外一个是其他人看到的。
1.添加一个完整的全身的网格,现在点击这里载入资源吧。我把我们要用到的所有资源全部放进去了,GenericMale.fbx文件就是有全身网格的,HeroFPP.FBX是等下我们要用到的自己看自己只有胳膊和武器的网格,Sphere.FBX是个球体网格模拟子弹,crosshair.TGA是瞄准的十字准心,FPP_Animations文件夹存放的是胳膊和武器的那个网格的用的的动画。这是我的目录结构,仅供参考。
大家把他们都载入进来吧,注意一点就是,注意载入全部,比如载入网格,需要勾选载入材质,载入动作的时候,要选择网格,如下:
好了,关于资源载入的问题,就一次性说清,以后就不说了。
2.在蓝图文件夹内,新建蓝图
这里注意!在下拉框中选择FPSCharacter,使其作为蓝图的父类,给蓝图命名为“BP_FPSCharacter”,打开它。
3.选中Mesh,指定网格,并把Mesh的z轴值改为-88
完成以后就应该是这样了,为什么是-88?因为是已经调整好的数值,当使用自己的网格模型时,需要自己调整,最终目标是 使得网格被外面的胶囊组件包裹,并且方向为箭头所指向的方向。这将会使得模型在世界中正确行走,方向正常。可以直接在视图中调整,也可以直接设置数值。
4.编译保存蓝图,现在,我们要告诉我们的游戏模型使用最新我们创建的蓝图类来作为游戏角色的pawn物体,而不是我们之前创建的父类:FPSCharacter。返回VS,在模式的CPP文件构造函数中,删除
- DefaultPawnClass = AFPSCharacter::StaticClass();
并添加如下代码:
- AMyNewFPSGameMode::AMyNewFPSGameMode(const FObjectInitializer& ObjectInitializer)
- : Super(ObjectInitializer)
- {
- //DefaultPawnClass = AMyNewFPSCharacter::StaticClass();
- static ConstructorHelpers::FObjectFinder<UBlueprint> PlayerPawnObject(TEXT("Blueprint'/Game/Blueprints/BP_FPSCharacter.BP_FPSCharacter'"));
- if (PlayerPawnObject.Object != NULL)
- {
- DefaultPawnClass = (UClass*)PlayerPawnObject.Object->GeneratedClass;
- }
- }
(这里提前说一下,大家无视我的类名,按照自己的来即可)
这里有非常重要的一个知识点:在C++中引用蓝图(这样,就可以打通C++和蓝图了!),更准确的说应该是引用资源,这里的蓝图是以资源出现,等下我们也会发现在引用十字瞄准点的时候,使 用的是同样的方法,那就是:
- static ConstructorHelpers::FObjectFinder<UBlueprint> PlayerPawnObject(TEXT("Blueprint'/Game/Blueprints/BP_FPSCharacter.BP_FPSCharacter'"));
TEXT里面的是资源的路径,获取资源路径有个小技巧,就是在内容浏览器中右键资源,选中复制引用命令,就会把具体的位置复制到剪贴板中了。
5.回到编辑器,选择编译,等待编译成功进入游戏测试。如果你移动摄像机,你会发现角色的影子,按下F1+Shift键,来获得鼠标指针,点击上面的弹出,这 时候就可以在世界中随意的移动摄像机来看到角色的网格。
(六)更改摄像机试图(添加摄像机组件)
在上一节的结尾,默认的摄像机是在网格的脖子里,现在我们来创建一个正确的摄像机,并且可以设置摄像机的一些属性比如位置和视图。我们通过给我们的FPSCharacter类添加一个摄像机组件来完成(CameraComponent )。首先,我们为FPSCharacter添加一个属性来引用摄像机组件。
1.在角色.h文件里面添加下面的代码来添加一个公有变量FirstPersonCameraComponent。
- /** First person camera
- * 为摄像机新建变量,引用摄像机。在构造函数中做初始化------C++中添加组件!,蓝图中赋值。
- */
- UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
- TSubobjectPtr<UCameraComponent> FirstPersonCameraComponent;
2.我们在FPSCharacter的构造函数中创建真正的组件,在FPSCharacter的构造函数中添加如下代码,来创建一个摄像机组件并且把他依附到胶囊组件上。
- AMyNewFPSCharacter::AMyNewFPSCharacter(const FObjectInitializer& ObjectInitializer)
- : Super(ObjectInitializer)
- {
- /********************创建、初始化摄像机********************************/
- //创建摄像机组件
- FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
- FirstPersonCameraComponent->AttachParent = CapsuleComponent;
- // 摄像机的位置稍微比眼睛要高
- FirstPersonCameraComponent->RelativeLocation = FVector(0, 0, 50.0f + BaseEyeHeight);
- // 允许玩家控制旋转
- FirstPersonCameraComponent->bUsePawnControlRotation = true;
- }
3.我们来调整摄像机的位置到眼睛的位置,在蓝图中也可以作调整,这里只用调整他的位置信息,不用调整旋转,因为旋转会被鼠标改变。这部分代码就是上面的下两行。
4.好了,进游戏测试吧,摄像机应该在角色的头上,所以当你向下看的时候,将会看到角色的头部!
(七)添加第一人称网格
常规的FPS要准备和处理两套网格,一套是其他人看到的自己,但是对于自己来说它是隐藏的,这个就是全身的网格;还有一种是只有武器和手臂的网格,他们要依附在摄像机上,当玩家是第一人称的时候才被看到。为了实现这个,我们首先要保持现有的网格,给它命名为“Mesh”,并且为我们的第一人称网格准备一个新的静态网格模型。
1.角色的.h文件中新建变量来引用新的网格。
- /**
- * 出生的时候的网格,只有手臂和武器。 也是添加一个变量引用新的网格
- */
- UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
- TSubobjectPtr<USkeletalMeshComponent> FirstPersonMesh;
2.在构造函数中,添加如下代码来创建配置网格
- ///********************创建、初始化只有手和武器的网格********************************/
- //// 创建一个网格组件,他只有手臂和武器
- FirstPersonMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
- FirstPersonMesh->SetOnlyOwnerSee(true);// 仅仅只有这个Pawn才可以看到
- FirstPersonMesh->AttachParent = FirstPersonCameraComponent;
- FirstPersonMesh->bCastDynamicShadow = false;
- FirstPersonMesh->CastShadow = false;
- ///********************除了自己,其他人看到全身*******************************/
- //// 除了自己其他人都能看到
- 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文件里面定义变量:
- /** 胶囊碰撞组件 */
- UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
- TSubobjectPtr<USphereComponent> CollisionComp;
4.在CPP的构造函数里面添加组件,我们将把他作为根组件,因为我们要模拟它,并且在蓝图里面稍后为他添加可视化组件。
- AFPSProjectile::AFPSProjectile(const FObjectInitializer& ObjectInitializer)
- : Super(ObjectInitializer)
- {
- // 使用球体代表子弹
- CollisionComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));
- CollisionComp->InitSphereRadius(15.0f);
- RootComponent = CollisionComp;
- }
这里是第二次在C++里面直接添加组件了,总结一下过程就是:声明变量--添加组件!
5.UE4里面有一个自带的组件叫做子弹移动组件ProjectileMovementComponent ,他可以用于简单的弹道式的移动,我们把它添加到子弹类里面。
(1)在.h里面添加变量
- /**子弹移动组件 */
- UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Movement)
- TSubobjectPtr<class UProjectileMovementComponent> ProjectileMovement;
(2).cpp构造函数里面添加
- // 使用子弹移动组件ProjectileMovementComponent控制子弹
- ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileComp"));
- ProjectileMovement->UpdatedComponent = CollisionComp;
- ProjectileMovement->InitialSpeed = 3000.f;
- ProjectileMovement->MaxSpeed = 3000.f;
- ProjectileMovement->bRotationFollowsVelocity = true;
- ProjectileMovement->bShouldBounce = true;
- ProjectileMovement->Bounciness = 0.3f;
6.往后想一想,我们还需要一个功能通过初速度设置来“启动”我们的弹丸。我们自定义一个函数来处理他。首先.h里面声明一个公有函数:
- /** 子弹初速度*/
- void InitVelocity(const FVector& ShootDirection);
cpp里面添加实现函数
- void AFPSProjectile::InitVelocity(const FVector& ShootDirection)
- {
- if (ProjectileMovement)
- {
- // set the projectile's velocity to the desired direction 在所需方向上设置子弹的速度
- ProjectileMovement->Velocity = ShootDirection * ProjectileMovement->InitialSpeed;
- }
- }
7.既然速度已经制定了,接下来我们只需要设置一个启动的方向,首先在角色类中添加函数处理输入:
.h:
- void OnFire();
.cpp文件中添加实现函数,在实现这个函数的时候,有两点需要考虑:
(1)在哪里产生子弹
(2)产生什么样的子弹(即传入子弹的类)。
第一个问题,为了制定产生的位置,我们需要一个摄像机空间的偏移量作为可以编辑的参数,因此我们可以在角色蓝图BP_FPSCharacter 里面设置它,然后就可以根据这个数据来计算子弹的初始化速度。主要是第二个,这里也是一个重点,简单来说,它的实质是两个C++的类是什么样的关系,如何通信!我们解决的办法是,在C++里面定义一个共有变量,然后等下再蓝图里面指定它。所以在.h里面添加这个共有变量把~
角色.h
- /***************************这两项要在蓝图里指定******************************************/
- /** 需要实例化的子弹类 */
- UPROPERTY(EditDefaultsOnly, Category = Projectile)
- TSubclassOf<class AFPSProjectile> ProjectileClass;
- /** Gun muzzle's offset from the camera location
- 枪口到摄像机的位置偏移量*/
- UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
- FVector MuzzleOffset;
解释一下变量前面的两个宏,里我们使用只能在蓝图设置默认EditDefaultsOnly 的说明符,这就意味着你只能在蓝图类里面设置子弹类为默认,而在不是蓝图的每一个实例中.
OnFire将会包含以下几步:
- 因为我们的子弹产生位置是由相机空间决定,所以在我们真正计算子弹位置之前需要查询摄像机的位置信息.
- 尝试实例化子弹.
- 使用我们定义的函数给子弹一个初始化的速度.
OK,最后就是这个函数的实现了!
- void AMyNewFPSCharacter::OnFire()
- {
- // try and fire a projectile
- if(ProjectileClass !=NULL)
- {
- // Get the camera transform
- FVector CameraLoc;
- FRotator CameraRot;
- GetActorEyesViewPoint(CameraLoc, CameraRot);
- // MuzzleOffset is in camera space, so transform it to world space before offsetting from the camera to find the final muzzle position
- FVector const MuzzleLocation = CameraLoc + FTransform(CameraRot).TransformVector(MuzzleOffset);
- FRotator MuzzleRotation = CameraRot;
- MuzzleRotation.Pitch += 10.0f;// skew the aim upwards a bit
- UWorld*const World = GetWorld();
- if (World)
- {
- FActorSpawnParameters SpawnParams;
- SpawnParams.Owner = this;
- SpawnParams.Instigator = Instigator;// spawn the projectile at the muzzle
- AFPSProjectile*const Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
- if (Projectile)
- {
- // find launch direction
- FVector const LaunchDir = MuzzleRotation.Vector();
- Projectile->InitVelocity(LaunchDir);
- }
- }
- }
- }
这个函数有点复杂,得需要好好理解一下。最后注意在角色cpp里面添加头文件
- #include "FPSProjectile.h"
8.编译代码,回到编辑器,最后一步为子弹创建蓝图添加网格,创建新的蓝图,父类FPSProjectile,蓝图命名BP_FPSProjectile,在蓝图里面手动添加静态网格组件,命名ProjectileMesh,在右面的Detail面板中指定Mesh值,并调整其大小为:0.09(x、y、z都是)。另外把子弹网格组件ProjectileMesh的碰撞关闭,因为我们使用球形碰撞体来检测碰撞而不是网格物体。
9.接下来,我们就要为刚刚C++里面添加的两个共有变量赋值了!打开角色蓝图,点击头上的ClassDefault,然后在右面,你就可以看到这两个变量了。
分别按照上面赋值!之后,进入游戏测试吧!应该可是打出子弹了!
(九)设置子弹的碰撞和生命周期
现在,我们需要考虑两件事情
- 我们打出的子弹现在是不会消失的, Scene Outliner面板告诉了我们
- 弹不会产生碰撞。
- // 子弹生命周期(所有的Actor都有的参数)
- InitialLifeSpan = 3.0f;
接下来,就要解决子弹的碰撞问题了。这部分问题,我看到的材料有点复杂,理解的不是很透彻,得多操作几遍才能熟悉,我会把英文原文附上,所以熟能生巧...
1.UE4有几个有用的碰撞渠道,而且还提供了一些我们可以定制的碰撞渠道,现在我们来自定义为弹丸碰撞渠道,这样场景中的一切都可以选择如何在我们的游戏中和一个子弹的相互作用。
在引擎配置.ini( DefaultEngine.ini,位于FPSProject > Config)的下方添加下面的代码:
- [/Script/Engine.CollisionProfile]
- +DefaultChannelResponses=(Channel=ECC_GameTraceChannel1, Name=Projectile)
这实际上是重命名了通用的渠道为“Projectile”,我们要在本地代码中重命名它,来保证事情的持续性和可读性。这实际上是重命名了通用的渠道为“Projectile”,我们要在本地代码中重命名它,来保证事情的持续性和可读性。
2.UE4也有一个功能叫做碰撞分布,实质上是引擎整体使用的预先捆绑的碰撞的设置。往后想一下,或许我们可以在我们的游戏有很多类型的弹丸,如果保持一致的碰撞设置其中可能是容易出错的,所以我们定义为弹一个新的文件。
继续在DefaultEngine.ini
,添加代码:
- +Profiles=(Name="Projectile", CollisionEnabled=QueryOnly,ObjectTypeName=Projectile, CustomResponses=( \
- (Channel=Static, Response=ECR_Block), \
- (Channel=PawnMovement, Response=ECR_Block), \
- (Channel=Dynamic, Response=ECR_Block), \
- (Channel=PhysicsBody, Response=ECR_Block), \
- (Channel=VehicleMovement, Response=ECR_Block), \
- (Channel=Destructible, Response=ECR_Block) \
- ))
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.现在我们来设置我们的子弹使用这个分布,在子弹构造函数中,添加下面代码:
- //使用自定义的碰撞分布
- CollisionComp->BodyInstance.SetCollisionProfileName("Projectile");
ok,编译一下代码吧。
(十)子弹和世界的物体交互
既然我们已经可以发现我们子弹的碰撞相互作用,我们可以决定怎么对她做出反应,在我们的子弹碰撞设置中,我们已经设置了对块的相互碰撞,接下来我们要为子弹类添加一个叫做OnHit的函数来处理这些事件。
1.自定义一个函数OnHit。在子弹的.h文件下添加声明
- /** 当子弹碰到其他物体时被调用*/
- UFUNCTION() //这个必须有
- void OnHit(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
2.cpp里面实现它
- void AFPSProjectile::OnHit(AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
- {
- if (OtherActor && (OtherActor != this) && OtherComp)
- {
- OtherComp->AddImpulseAtLocation(ProjectileMovement->Velocity * 100.0f, Hit.ImpactPoint);
- }
- }
3.委托,现在我们需要把这个函数交给子弹的球形组件的OnComponentBeginOverlap
事件委托,所以在子弹的构造函数中继续添加下面代码:
- CollisionComp->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
最后,你的子弹构造函数应该是这个样子:
- AFPSProjectile::AFPSProjectile(const FObjectInitializer& ObjectInitializer)
- : Super(ObjectInitializer)
- {
- // 使用球体代表子弹
- CollisionComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));
- CollisionComp->InitSphereRadius(15.0f);
- RootComponent = CollisionComp;
- //使用自定义的碰撞分布
- CollisionComp->BodyInstance.SetCollisionProfileName("Projectile");
- //碰撞事件委托
- CollisionComp->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
- // 使用子弹移动组件ProjectileMovementComponent控制子弹
- ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileComp"));
- ProjectileMovement->UpdatedComponent = CollisionComp;
- ProjectileMovement->InitialSpeed = 3000.f;
- ProjectileMovement->MaxSpeed = 3000.f;
- ProjectileMovement->bRotationFollowsVelocity = true;
- ProjectileMovement->bShouldBounce = true;
- ProjectileMovement->Bounciness = 0.3f;
- // 子弹生命周期(所有的Actor都有的参数)
- InitialLifeSpan = 3.0f;
- }
好咧,编译代码!
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文件里面添加变量私有的就行:
- private:
- /**瞄准十字贴图 */
- UTexture2D* CrosshairTex;
(2)我们在构造函数中引用资源,提醒一下,可以在内容浏览器中通过右键复制引用的方式找到资源的位置.
- AFPSHUD::AFPSHUD(const FObjectInitializer& ObjectInitializer)
- : Super(ObjectInitializer)
- {
- // 引用赋值
- static ConstructorHelpers::FObjectFinder<UTexture2D> CrosshairTexObj(TEXT("Texture2D'/Game/Texture/crosshair.crosshair'"));
- CrosshairTex = CrosshairTexObj.Object;
- }
3.HUD基类有一个我们可以重写的虚函数叫做DrawHUD,来增加我们自定义的需要往屏幕上添加的内容,我们现在来重写它!
HUD.H里面添加
- virtual void DrawHUD() OVERRIDE;
cpp里面实现
- void AFPSHUD::DrawHUD()
- {
- Super::DrawHUD();
- // Draw very simple crosshair
- // 找到屏幕可绘区域的中心
- FVector2D Center(Canvas->ClipX *0.5f, Canvas->ClipY *0.5f);
- //计算一下偏移
- FVector2D CrosshairDrawPosition((Center.X-(CrosshairTex->GetSurfaceWidth()*0.5)),(Center.Y - (CrosshairTex->GetSurfaceHeight()*0.5f)));
- // 画出来
- FCanvasTileItem TileItem(CrosshairDrawPosition, CrosshairTex->Resource, FLinearColor::White);
- TileItem.BlendMode = SE_BLEND_Translucent;
- Canvas->DrawItem(TileItem);
- }
3.在游戏模式的构造函数里面添加
- HUDClass = AFPSHUD::StaticClass();
- AMyNewFPSGameMode::AMyNewFPSGameMode(const FObjectInitializer& ObjectInitializer)
- : Super(ObjectInitializer)
- {
- //DefaultPawnClass = AMyNewFPSCharacter::StaticClass();
- static ConstructorHelpers::FObjectFinder<UBlueprint> PlayerPawnObject(TEXT("Blueprint'/Game/Blueprints/BP_FPSCharacter.BP_FPSCharacter'"));
- if (PlayerPawnObject.Object != NULL)
- {
- DefaultPawnClass = (UClass*)PlayerPawnObject.Object->GeneratedClass;
- }
- HUDClass = AFPSHUD::StaticClass();
- }
注意要写上头文件。
好了,到了这一步,就可以测试了!应该没问题!
(十二) 给角色添加动画
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.给角色添加网格,添加自定义摄像机,调整视角,添加第二套网格
还是那一套,进入游戏测试。
这里总结一下:前面这么多处理输入控制的函数,其实每一类控制要想完成都需要三部:
1.处理映射:在项目设置中处理映射,包括轴映射(键盘鼠标都可以),事件映射。
1.添加函数:这里要在角色的.h和.cpp文件里都要添加,一个是声明一个是实现
2.函数和输入绑定!这一步,在角色重写的那个设置玩家输入组件的函数里面实现。
5.给角色添加网格,添加自定义摄像机,调整视角,添加第二套网格