【UE4】将蓝图转为C++的方法实操示范
动机这里我也按照我的理解,简单的将官方模板双摇杆射击游戏从蓝图转为C++供大家参考。这种转换的方法并不一定很好,只是希望能够用上教程中的转换方法,巩固知识。准备工作1.创建工程我们最后是做一个C++的项目,但是一开始选模板的时候是选蓝图。2. 添加C++类新建了工程,很容易就会发现和C++工程相比,没有编译这个选项。工程文件里面也没有source文件夹所以首先就需要新建C++类,把我们的这个工程转
·
动机
前面总结了官方教程
【UE4】蓝图转为C++官方教程部分笔记
这里我也按照我的理解,简单的将官方模板双摇杆射击游戏从蓝图转为C++供大家参考。
这种转换的方法并不一定很好,只是希望能够用上教程中的转换方法,巩固知识。
准备工作
1.创建工程
- 我们最后是做一个C++的项目,但是一开始选模板的时候是选蓝图。
2. 添加C++类
- 新建了工程,很容易就会发现和C++工程相比,没有编译这个选项。工程文件里面也没有source文件夹
- 所以首先就需要新建C++类,把我们的这个工程转为C++项目。
- 我们第一步先改写操作的这个飞船,飞船原本的父类是pawn类,所以就先创建一个Pawn。
3. 重设蓝图父项
- 在我们的飞船找到重设蓝图父项这个选项。
- 找到新建的pawn,选择之后,新建的C++类就变成我们当前蓝图的父项了。
- 接下来的也就是将蓝图里面的逻辑迁移到类里面。
根据蓝图写C++
观察蓝图
- 在事件图表中,很容易就可以看到事件tick后有很长一串的操作,同时有两个函数。我们可以先从替换函数入手。
方法一 :一一对应改写函数
- 首先先看FireShot函数,他有一个输入,向量Direction,没有输出。
UFUNCTION(BlueprintCallable)
void FireShot(FVector Direction) ;
- 那我们声明也尽量还原他的函数定义
- 首先是第一部分,他注释成Check if the user can fire的代码块。
- 首先我们要用到这个Can Fire的变量。但是他是在蓝图中定义的,在C++ 文件中没有。所以我们要在C++ 文件中创建。
UPROPERTY(BlueprintReadWrite)
bool CanFire;
- 使用BlueprintReadWrite,蓝图就可以读写这个变量
- 第一个挑战来自于向量长度这个API,通过FVector类的联想可以找到。
如果没法联想或者找不到。就在蓝图里面查看他属于什么库。
- 进入C++ 源代码查找
- 通过搜索,找到库里面的节点函数实现,这下就破案了。
void ANewPawn::FireShot(FVector Direction)
{
if(!CanFire) return ;
if(Direction.Size() <= 0) return;
}
- 那就把这个块的蓝图逻辑实现。(目前代码↑)
方法二 : 使用蓝图本地事件
- 第二个块,其实逻辑也非常轻松,但是难办的点在于。生成Actor这个函数需要我们提供类。但是C++里面寻找类资产还是比较麻烦的。
if(!CanFire) return ;
if(Direction.Size() <= 0) return;
FRotator ShipRotator = Direction.ToOrientationRotator() ;
FVector SpawnPoint = GetActorLocation()+Direction.ToOrientationRotator().RotateVector(GunOffset);
FTransform SpawnTransform = FTransform(ShipRotator, SpawnPoint, FVector(1.0f,1.0f,1.0f));
- 将生成Actor之前的代码都做好了。但是生成Actor实在不会写那怎么办呢。
- 这时候就需要搬出蓝图本地事件了。
UFUNCTION(BlueprintCallable,BlueprintNativeEvent)
void FireShot(FVector Direction) ;
- 在定义的时候加入BlueprintNativeEvent的宏
void ANewPawn::FireShot_Implementation(FVector Direction)
{
if(!CanFire) return ;
if(Direction.Size() <= 0) return;
FRotator ShipRotator = Direction.ToOrientationRotator() ;
FVector SpawnPoint = GetActorLocation()+Direction.ToOrientationRotator().RotateVector(GunOffset);
SpawnTransform = FTransform(ShipRotator, SpawnPoint, FVector(1.0f,1.0f,1.0f));
}
- 实现里面的函数要加上FireShot的后缀。
- 这里为了方便,我将生成的Transform创建了一个变量
- 编译完之后原本的CanFire等变量都变成_0的形式,防止冲突。我们需要将他们一个个替换掉。
- 不要忘了设置好C++变量的初始值
- 添加重载函数,选择在C++里面实现的FireShot
- 前半部分是C++实现的,所以也要调用父类函数,先执行完再执行剩下的步骤。
- 也就是说,我们将这个函数改成了一半是C++实现的,一半是蓝图实现的。
- 但是如果这样就会出现问题,问题在于:原本不是CanShot的时候,运行时直接停止的。但是在我的写法中,他会继续运行,并且访问生成Actor这个节点。
bool ANewPawn::FireShot_Implementation(FVector Direction)
{
if(!CanFire) return false;
if(Direction.Size() <= 0) return false;
FRotator ShipRotator = Direction.ToOrientationRotator() ;
FVector SpawnPoint = GetActorLocation()+Direction.ToOrientationRotator().RotateVector(GunOffset);
SpawnTransform = FTransform(ShipRotator, SpawnPoint, FVector(1.0f,1.0f,1.0f));
return true ;
}
- 修改一下CPP代码,改为有bool判断的函数。
- 重载之后其他的就用原来的蓝图节点。
- 测试:可以正常射出
方法三 :蓝图可实现事件
- 如果你觉得,类似于图上设置场景旋转等操作需要访问骨骼网络体,在C++中不好实现,同样可以将他们留在蓝图中。
UFUNCTION(BlueprintCallable,BlueprintImplementableEvent)
void HandleRotation(FRotator Rotation,FVector NewLocation) ;
- 在这里我们将后面的代码都放在一个蓝图可实现事件里面(宏:BlueprintImplementableEvent)
- 声明好了之后直接编译(这和纯虚函数类似,并不需要在父类实现)
- 回到子类蓝图,创建重载
- 就可以把原来的函数分割开了。(后来修改时发现还得要加上一个Vector输入,图片没改)
改写Tick
- 看了前面那些可能就会有疑惑,这样费劲心思的改写函数,又是改C++的,又是用蓝图本地事件,蓝图可实现事件的意义是什么呢。
- 答案:在我们这个飞船中,事件Tick就类似于平时的main函数。利用上面的方法改写后的函数可以在C++中调用,改写Tick就变得非常容易了。
- 现在的Tick逻辑就只有三步:1.计算旋转 2. 调用函数HandleRotaion 3.调用函数FireShot
void ANewPawn::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
FVector MoveVector = FVector(GetInputAxisValue(FName("MoveForward")),GetInputAxisValue(FName("MoveRight")),0.0f);
MoveVector = MoveVector.GetClampedToMaxSize(1.0f);
MoveVector = MovementSpeed * DeltaTime * MoveVector ;
if(MoveVector.Size() <= 0) return ;
FRotator XRotator = MoveVector.ToOrientationRotator();
//调用蓝图可实现事件HandleRotation
HandleRotation(XRotator,MoveVector);
//调用蓝图本地时间FireVector
FVector FireVector = FVector(GetInputAxisValue(FName("FireForward")),GetInputAxisValue(FName("FireRight")),0.0f);
FireShot(FireVector);
}
// Called to bind functionality to input
void ANewPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis(FName("MoveForward"));
PlayerInputComponent->BindAxis(FName("MoveRight"));
PlayerInputComponent->BindAxis(FName("FireForward"));
PlayerInputComponent->BindAxis(FName("FireRight"));
//不执行这个绑定的话就无法获取输入
}
- 要添加的代码,也就很轻松得把上面的计算逻辑实现了。然后调用前面准备好的函数就完成了。
- 如此一来,飞船就完全改完了。
提示
- 这只是一个个人向的练习,所以对于写法的选择并不一定很科学规范,仅提供一种思路
- 如果想不到蓝图节点怎么对应C++可以创建一个这个模板的C++版本参考学习(但是官方的C++实现也不见得最好)
- 多做多积累~
更多推荐
所有评论(0)