【UE4】将蓝图转为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"));
	//不执行这个绑定的话就无法获取输入
}
  • 要添加的代码,也就很轻松得把上面的计算逻辑实现了。然后调用前面准备好的函数就完成了。
    在这里插入图片描述
  • 如此一来,飞船就完全改完了。

提示

  1. 这只是一个个人向的练习,所以对于写法的选择并不一定很科学规范,仅提供一种思路
  2. 如果想不到蓝图节点怎么对应C++可以创建一个这个模板的C++版本参考学习(但是官方的C++实现也不见得最好)
  3. 多做多积累~

猜你喜欢

转载自blog.csdn.net/Alexander_420/article/details/124989859