这一章,我们要实现一个俯视视角的坦克小游戏,玩家可以操作坦克在地图中移动。敌人是固定的,但是具备一定的AI,可以瞄准玩家,并且在玩家进入攻击范围后,对玩家进行攻击。如果玩家被摧毁,则游戏结束。反之,玩家需要摧毁所有的敌人后,达成游戏胜利的条件。
一、前言介绍
在开始之前,我们先介绍这一章会涉及到的内容:
- 创建一个坦克,可以在地图中自由移动
- 解决输入问题(WASD移动,鼠标点击射击,鼠标转动明确攻击方向)
- 创建一个敌方炮台类
- 添加开火功能,玩家和炮台都可以开火攻击敌人
- 添加血条、伤害和破坏效果
- 添加HUD的胜利和失败界面
关于绑定输入,这里就不多赘述,如下图:

关于素材和地图,这里也不是我们的重点,暂时略过,感兴趣的可以自行寻找。我们直接进入编码步骤。
二、创建BasePawn
1.1 创建BasePawn
由于我们这里会有两个pawn(玩家和敌人),我们可以先创建一个BasePawn类。这将具有坦克和炮塔共享的基本功能。然后我们就可以创建我们的两个子类,炮台和坦克。
我们新建一个C++类:

但是哪一个才最适合我们想要做的事情?
- Actor类,可以被放置在世界中,有相应的视觉表现
- Pawn类,可由控制器拥有,可以处理运动输入
- Character类,有一些特定于角色的东西,适合双腿的运动模式或类似飞行和游泳运动。
所以,Pawn应该是我们想要的。我们创建对应的C++类即可。
1.2 Component
USceneComponent:
- has a transform(旋转或位置)
- supports attachment(这意味着我们可以将其他组件附加到场景组件)
- no visual representation
UCapsuleComponent:
- handle collision
UStaticMeshComponent:
- visual representation
我们讨论组件的目的,是要理清之后的操作思路。我们首先知道我们的Pawn有自己的root component,它的类型是USceneComponent。我们知道他是没有visual representation,是不可见的。
但是我们可以使用其他类型从SceneComponent(场景组件)派生的对象重新assign该根组件。我们知道UCapsuleComponent来自SceneComponent。如果我们创建一个Capsule,我们可以assign这个,作为Root,替换默认的SceneComponent:
RootComponent(UCapsuleComponent) = CapsuleComp(UCapsuleComponent)
具体的思路可以见下图:

在蓝图中,我们创建一个Actor蓝图,然后添加Capsule,接着添加静态网格体组件,并选择车身网格体,同理添加炮台。

结果:

在C++中,我们先在.h中定义
private:
UPROPERTY()
class UCapsuleComponent* CapsuleComp;
然后在.cpp中添加头文件,并将其设置为Root
CapsuleComp = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule Collider"));
RootComponent = CapsuleComp;
这样我们将BasePawn拖入场景中时,会有:

接下来我们要将BaseMesh和TurretMesh,attach到Root上(和之前的操作相同):
UPROPERTY()
UStaticMeshComponent* BaseMesh;
UPROPERTY()
UStaticMeshComponent* TurretMesh;
UPROPERTY()
USceneComponent* ProjectileSpawnPoint;
BaseMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Base Mesh"));
BaseMesh->SetupAttachment(CapsuleComp);
TurretMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Turret Mesh"));
TurretMesh->SetupAttachment(BaseMesh);
ProjectileSpawnPoint = CreateDefaultSubobject<USceneComponent>(TEXT("Spawn Point"));
ProjectileSpawnPoint->SetupAttachment(TurretMesh);
接下来我们可以为BasePawn创建一个基于BasePawn的蓝图类,这样我们进入后,可以看见这样的组件结构:

然后我们还要创建一个蓝图类作为敌人,命名为BP_PawnTurret。
1.3 UPROPERTY
但是有一点要注意的是,当我们选中左边的组件时,右边的细节面板内容不见了。这是因为 C++ 方面的蓝图没有任何内容。我们可以通过UPROPERTY来对齐进行操作。
UPROPERTY Specifiers:
| Defaults | Instance | Event Graph(l蓝图的事件图表) | |
| Read Only | VisibleAnyWhere | BluePrintReadOnly | |
| VisibleDefaultsOnly | VisibleInstanceOnly | ||
| Read/Write(set) | EditAnyWhere | BluePrintReadWrite | |
| EditDefaultOnly | EditInstanceOnly |
||
关于事件图表,我们在C++中可以使用:
UPROPERTY(VisibleAnyWhere,BluePrintReadWrite)
int32 visibleAnywhere = 12;
这样我们在事件图表中搜索visibleAnywhere,就会有两个节点Set和Get。
其次还有一个需要注意的点是,对于private,会有报错(同样对于BluePrintReadOnly):
BluePrintReadWrite should not be used on private members
对于这种情况,我们仍然可以在事件图表中访问私有变量,我们需要添加:
UPROPERTY(VisibleAnyWhere,BluePrintReadWrite,meta = (AllowPrivateAccess = "true"))
当然我们可以为其添加Category,这个在之前有提到过。
在这一步之后,别忘了给坦克和炮台添加网格体,移动project point位置。
二 Moving Tank
2.1 Component
要实现坦克的移动和玩家输入,炮台的站立,我们要创建新的派生类:

其次,为了实现坦克的移动,我们首先要保证视角跟随,即坦克的身后有一个摄像机——Camera Component(UCameraComponent)和一个固定距离的Spring arm component(USpingArmComponent)。
在蓝图中:
所以我们回到蓝图中,添加弹簧臂组件到Capsule下:

然后我们选中弹簧臂组件,再添加一个摄像机组件:

但是我们的主要目标是C++。
在C++中:
我们在.h中创建:
public:
ATank();
private:
UPROPERTY(VisibleAnyWhere,Category = "Component")
class UCameraComponent* Camera;
UPROPERTY(VisibleAnyWhere,Category = "Component")
class USpringArmComponent* SpringArm;
在.cpp中:
ATank::ATank(){
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("Spring Arm"));
SpringArm->SetupAttachment(RootComponent);
Camera = CreateDefaultSubobject<U
2091




被折叠的 条评论
为什么被折叠?



