UE C++基础
- 虚幻C++各个头文件的含义
- 虚幻 C++各个宏的作用
- 虚幻C++游戏架构,创建C++默认类
- 虚幻C++重写Beginplay、Tick、Endplay函数
- 虚幻C++UE_LOG和AddOnScreenDebugMessage
- 虚幻C++的基础变量类型
- 虚幻C++FString、FName、FText三者的区别和相互转化
- 虚幻C++容器
- 虚幻C++基础数据类型输出打印
- 虚幻C++的宏及其属性说明符和元数据说明符
- 虚幻C++Ubject的实例化
- 虚幻C++ UGameInstance的实例化
- 虚幻C++创建Actor添加初始化组件
- 虚幻C++创建摄像机摇臂和相机,并且设置旋转位移缩放
- 虚幻C++按键映射绑定以及使用鼠标滑轮控制镜头缩放远近
- 虚幻C++AddActorLocalOffset和AddActorWorldOffset
- 虚幻C++的代理绑定
- 虚幻C++碰撞设置
- 虚幻C++粒子特效的激活和失效
- 虚幻C++创建UserWidget并且绑定Button点击事件
- 虚幻C++进度条Progress的实现
- 虚幻C++的代理
- 虚幻C++子弹类型设置
- 虚幻C++创建Character添加增强输入
- 虚幻C++创建Interface接口
- 虚幻C++创建TimeHandle定时器
- 虚幻C++创建3DWidget并渲染到屏幕上
- 虚幻C++创建Apply Damage并且接受伤害TakeDamage
- 虚幻C++创建Timeline时间轴
- 虚幻C++射线检测LinetranceByChannel和LineTanceByObject
- 虚幻C++软引用
- 虚幻C++同步和异步加载资源
- 虚幻C++智能指针
- UE查找Actor的方式
虚幻C++各个头文件的含义
#pragma once
// 预处理程序指令 作用:保护同一个文件不会多次包含,
#include "CoreMinimal.h"
// 包含了一套来自UE4的核心变成环境的普遍存在类型
#include "GameFramework/GameModeBase.h"
// 包含了默认GameModeBase的头文件
#include "UEStudyGameModeBase.generated.h"
// 存储反射信息数据
虚幻 C++各个宏的作用
GENERATED_BODY()
//表示我们不使用父类的构造函数,如果我们要在我们自定义的类中做一些初始化操作,需要我们自己在.h头文件中
// 声明构造函数,然后在.cpp文件中实现该构造函数,它之后的成员的是private
GENERATED_UCLASS_BODY()//表示我们使用父类的构造,如果我们在自定义类中做一些初始化操作,可以直接在.cpp文件
// 中实现构造函数,而不需要在.h文件中去声明,这个宏会自动生成带有特定参数的构造函数,它之后的成员是public
UCLASS()//告知虚幻引擎生成类的反射数据,类必须派生自UObject
UPROPERTY()//叫做属性声明宏,虚幻C++在标准C++基础之上实现了一套反射系统(Reflection System)
// 反射系统负责垃圾回收、引用更新、编辑器集成等一系列高级且有用的功能,而UPROPERTY的作用就是
// 声明该属性在反射系统的行为
UFUNCTION()//函数声明宏,反射系统可识别的C++函数
USTRUCT()//结构体声明宏,反射系统可识别的C++结构体
UENUM()//枚举声明宏,反射系统可识别的C++枚举
虚幻C++游戏架构,创建C++默认类
世界场景设置
虚幻游戏架构组成:
- 游戏模式(GameMode):即游戏的技术规则,可包含出现的玩家和观众数量,以及允许的玩家和观众的最大数量,以及游戏是否暂停以及如何处理游戏暂停,玩家进入关卡的方式,关卡之间的过渡等
- 默认pawn类(Default Pawn Class):默认玩家角色类,可以是带有运动属性的character,也可以是不带有运动属性的pawn,相当于人体躯干,可以被controller控制,这个controller可以是玩家,也可以是aiController
- HUD类(HUD Class):用户界面类,绘制到屏幕上的UI,用来进行一些UI交互界面的展示
- 玩家控制器类(Player Controller Class):控制器是一个非物理的actor,可以拥有一个pawn或者是pawn的派生类,比如说charactor来控制其运动,可以是aiController,也可以是playerController,可以控制角色的移动,相当于给角色赋予了灵魂
- 游戏状态类(Game State Class):主要是追踪记录游戏层面的属性,比如说已经链接玩家的链表,团队的得分,开放世界中完成的任务等等
- 玩家状态类(Player State Class):追踪游戏玩家的状态属性,比如当前玩家的姓名、得分、在线状态等等
- 旁观者类(Spectator Class):第三方视角
创建C++默认类:
-
在UE中添加以上各个类
在VS中选择全部重新加载,会发现VS中多了private和public文件夹以及我们创建的类对应的.h和.cpp文件 -
在MyGameMode.h文件中添加加载我们创建的头文件,并创建一个默认构造函数
-
在MyGameMode.cpp文件中实现默认构造函数,并创建对应的C++默认类
-
关闭UE,生成并运行对应项目
-
在UE的世界场景设置中,将GameMode设置为我们创建的GameMode,即可完成游戏模式默认类创建
虚幻C++重写Beginplay、Tick、Endplay函数
-
在GameMode.h添加对应的构造函数,因为是重写的函数,所以需要关键字override,且Tick和EndPlay函数具有自带的参数
-
在GameMode.cpp中实现对应的构造函数,因为是继承自父类的函数,所以需要通过super关键字添加父类的对应函数
虚幻C++UE_LOG和AddOnScreenDebugMessage
输出日志UE_LOG
UE中应用此GameMode后运行的输出结果,在输出日志中
打印在屏幕上AddOnScreenDebugMessage
UE中应用此GameMode后运行的输出结果,在屏幕中
此打印相当于蓝图中的print string:
虚幻C++的基础变量类型
//布尔类型的变量声明
bool varBool;
//整形32位的变量声明
int32 varInt32;
//整形64位的变量声明
int64 varInt64;
// 字节类型的变量声明
BYTE varByte;
// 字符串类型的变量声明
FString varString;
// 名称类型的变量声明
FName varName;
// 文本类型的变量声明
FText varText;
// 向量类型的变量声明
FVector varVector;// 包含x轴、y轴、z轴的左边
//旋转向量类型的变量声明
FRotator varRotator;// 包含X轴的旋转Roll,y轴的旋转Pit,Z轴的旋转Yaw
// FTransform类型的变量声明
FTransform varTransform;// 包含FVector和FRotator以及缩放Scale三者的集合
虚幻C++FString、FName、FText三者的区别和相互转化
FString:提供了大量的字符串操作接口,三者之中唯一可以修改的字符串类型,消耗更高,性能更低
FName:更着重于表示名称,不区分大小写,不可以更改。引擎中的资源名称都是此类型。通过一个轻型的、系统重复使用的字符串,在创建时会根据内容创建一个哈希值,并且同样的内容指挥存储一次,通过哈希值进行FName的查找和访问的时候,速度比较快,也不需要比较字符串的内容,直接比较哈希值来区分不同FName的字符串
FText:着重于显示和本地化,显示可理解为玩家能直接看到的信息,本地化就是多种语言的处理,不可以更改,相较于另外,FText会更加臃肿,但是提供了非常优秀的本地化功能
//创建一个FString
FString myString = TEXT("I am String");
// FString转化为FName
FName myName = FName(*myString);
// FString转化为FText
FText myText = FText::FromString(myString);
// FName转化为FString
myString = myName.ToString();
// FName转化为FText
FText text1 = FText::FromName(myName);
// FText转化为FString
FString strFromText = text1.ToString();
// FText不能直接转化为FName,需要通过FString间接转化
虚幻C++容器
TArray
TArray:是虚幻C++中的动态数组TArray,索引值从0开始
特点:速度快,内存消耗小,安全性高,并且TArray所有元素均完全为相同类型,不能进行不同元素类型的混合
- 声明TArray变量以及打印函数
//声明一个整型32为的TArray
TArray<int32>myArray;
void PrintArray();
- 打印函数的实现:
void AMyGameMode::PrintArray()
{
// 用迭代器遍历、打印数组
for (auto It = myArray.CreateConstIterator(); It; It++)
{
UE_LOG(LogTemp, Warning, TEXT("%d"), *It);
GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Blue, FString::Printf(TEXT("%d"), *It));
}
}
- 对TArray进行操作:增及其输出
//数组TArray中的操作
//增,将元素添加到数组当中
myArray.Add(10);
myArray.Add(20);
myArray.AddUnique(20);//如果数组中没有相同的元素,则将此数加入数组之中
myArray.Add(30);
myArray.AddUnique(40);
PrintArray();
- 对TArray进行操作:删改查
// 删,将元素从数组中删除
myArray.Remove(20);// 移除数组中所有等值的元素
myArray.RemoveAt(0);// 移除数组中索引值为0的元素
myArray.RemoveSingle(10);// 移除首个匹配到的元素,即移除碰到的第一个10
myArray.Empty();// 清空数组,数组内容数number为0
myArray.Reset();// 重置数组,将数组中的所有元素变为0,数量number不变
// 改
myArray.Insert(60, 0);// 在索引值为0的位置插入值60
int32& temp = myArray[0];
temp = 50;// 修改索引值为0的位置的数值为50
// 查
myArray.Contains(10);// 查找数组中是否包含某个元素,返回值为bool类型
myArray.Find(10);// 正向查找我们的第一个匹配的元素,找到则返回下标,否则返回-1
myArray.FindLast(10);// 反向查找我们匹配的第一个元素,找到则返回下标,否则返回-1
TMap
TMap:一种键值对容器,里面的数据都是成对出现的(key,value),value通过key值来获取,且key值不能重复,key值唯一
- 声明TMap变量及打印函数:
// 创建一个int32位的TMap类型变量
TMap<int32, int32>myMap;
void PrintMap();
- 打印函数的实现:
void AMyGameMode::PrintMap()
{
for (auto& TestMap : myMap)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Blue, FString::Printf(TEXT("Key:%d Value:%d"), TestMap.Key, TestMap.Value));
UE_LOG(LogTemp, Warning, TEXT("Key:%d Value:%d"), TestMap.Key, TestMap.Value);
}
}
- TMap的相关操作:
// 增
myMap.Emplace(0, 1);// 用法同Array的add,往容器中添加数值
myMap.Emplace(1, 2);
myMap.Emplace(2, 3);
PrintMap();
// 删
myMap.Remove(0);// 移除key值为0的元素
myMap.Empty();// 清空Map中的数据
// 查
myMap.Contains(1);// 查找匹配的key值是否存在,找到返回真,否则为假,进行两次查找
int32* isFind = myMap.Find(6);// 查找匹配的key值是否存在,找到为真,否则为假,只进行一次查找,返回的是指针
const int32* isFind2 = myMap.FindKey(2);//反向查找,通过查找value的值来查找它对应的key,返回类型为指针
// 分别获取所有的keys和values
TArray<int32> testKeys;
TArray<int32> testValues;
myMap.GenerateKeyArray(testKeys);
myMap.GenerateValueArray(testValues);
TSet
TSet简介:
- 是一种快速容器,(通常)用于在排序不重要的情况下存储唯一元素
- TSet类似于TMap和TMultiMap,但有一个重要区别,TSet是通过对元素求值的可覆盖函数,使用数据本身作为键,而不是将数据值与独立的键相关联
- TSet可以非常快速地添加、查找和删除元素(恒定时间),-TSet也是值类型,支持常规复制、赋值和析构函数的操作,以及其元素较强的所有权
- 声明TSet变量及打印函数:
// 声明一个TSet类型的变量
TSet<FString>mySet;
void PrintSet();
- 打印函数的实现:
void AMyGameMode::PrintSet()
{
for (auto& testSet : mySet)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Blue, FString::Printf(TEXT("%s"), *testSet));
UE_LOG(LogTemp, Warning, TEXT("%s"), *testSet);
}
}
- TMap的相关操作:
// TSet相关操作
// 增
mySet.Add(TEXT("Banana"));
mySet.Add(TEXT("Apple"));
mySet.Add(TEXT("Pineapple"));
mySet.Emplace(TEXT("orange"));//add和emplace都是添加元素到容器中,但是emplace可以避免在插入集合时创建的临时文件
PrintSet();
// 合并元素
TSet<FString>mySet2;
mySet2.Add(TEXT("zhangsan"));
mySet2.Add(TEXT("lisi"));
mySet2.Add(TEXT("wangwu"));
mySet.Append(mySet2);//将mySet2的值合并到mySet中
PrintSet();
// 移除元素
mySet.Remove(TEXT("Banana"));// 移除匹配的值,会返回已删除元素的数量,如果给定的键未包含在集合中,则返回0
mySet.Empty();// 清空容器,释放内存
mySet.Reset();// 清空集合元素,保留内存
// 查找元素
int32 Count = mySet.Num();// 查询集合中保存的元素数量,返回值为整型
bool isFind = mySet.Contains(TEXT("Banana"));// 查询是否包含特点元素,有则返回真,否则为假
FString* isFind2 = mySet.Find(TEXT("Banana"));// 查找是否包含特定元素,返回指向元素数组的指针,如果映射不包含该键,则返回为Null
// array函数
TArray<FString> fruitArray = mySet.Array();//函数会返回一个TArray,其中填充了TSet中每个元素的副本
// 排序
TSet<FString>testSet = { TEXT("a"),TEXT("aa"),TEXT("aaa"),TEXT("aaaa") };
testSet.Sort([](FString A, FString B) {return A.Len() > B.Len(); });// 按长度从长到短排列
// 赋值运算符 =
TSet<FString>newSet;
newSet = mySet;// 把mySet的值赋值给newSet
newSet.Add(TEXT("OneOne"));
// []运算符
FSetElementId index = newSet.Add(TEXT("TwoTwo"));// 根据FSetElementId访问集合对应元素的引用
testSet[index] = TEXT("One");
// Reserve 预先分配内存
TSet<FString>newSet1;
newSet1.Reserve(10);// 预先分配内存,若输入的Number大于元素个数,则会产生闲置内存(Slack)
// Shrink 从容器末端移除所有的Slack(闲置内存)
for (int32 i = 0; i < 10; i++)
{
newSet1.Add(FString::Printf(TEXT("newSet%d"), i));// 添加元素
}
for (int32 i = 0; i < 10; i += 2)
{
newSet1.Remove(FSetElementId::FromInteger(i));// 删除当前索引值下面的元素 删除元素产生限制内存
}
newSet1.Shrink();// 删除末端的空元素
// Compact 将容器中的所有空白元素集合到一起放到末尾,方便一起删除
newSet1.Compact();// 注意,Compact可能会改变元素之间的顺序,如果不想改变的话,可以使用CompactStable
newSet1.Shrink();
虚幻C++基础数据类型输出打印
// 基础数据类型的输出打印
int32 myInt = 10;
float myFloat = 5.0f;
bool myBool = true;
char myChar = 'a';
FString myString1 = TEXT("myString1");
FVector myVector = FVector(0, 0, 0);
UE_LOG(LogTemp, Warning, TEXT("%d"), myInt);
UE_LOG(LogTemp, Warning, TEXT("%f"), myFloat);
UE_LOG(LogTemp, Warning, TEXT("%d"), myBool);
UE_LOG(LogTemp, Warning, TEXT("%c"), myChar);
UE_LOG(LogTemp, Warning, TEXT("%s"), *myString1);
UE_LOG(LogTemp, Warning, TEXT("%s"), *myVector.ToString());
虚幻C++的宏及其属性说明符和元数据说明符
UPROPERTY宏,属性说明符和元数据说明符
UPROPERTY:通过反射把属性暴露在蓝图或者实例的细节面板中,从而实现C++和蓝图之间的通信或交互。虚幻会生成PROPERTY的反射数据,利用这些反射数据,我们在蓝图中可以找到这些变量或者方法
定义宏:(定义在MyPawn.h中)
// 仅在类默认设置中可见
UPROPERTY(VisibleDefaultsOnly)
int32 visibleDefaultsOnlyInt;
// 仅在实例化细节面板可见
UPROPERTY(VisibleInstanceOnly)
FString visibleInstanceOnlyString;
// 类默认设置和实例化细节面板中都可以见到
UPROPERTY(VisibleAnywhere)
FVector visibleAnywhereVector;
// 仅在类默认设置中可以编辑
UPROPERTY(EditDefaultsOnly)
int32 editDefaultsOnlyInt;
// 仅在实例化细节面板可以编辑
UPROPERTY(EditInstanceOnly)
FString editInstanceOnlyString;
// 类默认设置和实例化细节面板中都可以编辑
UPROPERTY(EditAnywhere)
FVector editAnywhereVector;
// 仅在蓝图中可读
UPROPERTY(EditAnywhere, BlueprintReadOnly)
int32 blueprintReadOnlyInt;
// 在蓝图中可读可写即可以获取和设置变量
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 blueprintReadWriteInt;
// category目录
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "myIntValue")
int32 value1;
// category子目录
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "myIntValue|mySubIntValue")
int32 value2;
// meta元数据说明符
// DisPlayName别名
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisPlayName = "myValue3DisPlayName"))
int32 myValue3;
// EditCondition 条件控制编辑 用一个变量控制另一个变量受否可以被编辑
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (DisPlayName = "Controller"))
bool isController;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "isController"))
float value3;
//Tooltip 解释说明我们的变量
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Tooltip = "isControllerTrue"))
bool isTrue;
// 蓝图生成时暴露
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "myExposeOnSpawn", meta = (ExposeOnSpawn = "ExposeOnSpawnValue"))
float myHealth;
定义完成后编译运行,在UE中创建基于MyPawn的蓝图类
命名为BP_MyPawn,位置存放在content中
蓝图的类默认设置面板:可以看到visibleDefaultsOnlyInt和visibleAnywhereVector,可以编辑EditDefaultsOnlyInt和editAnywhereVector
将类拖拽到场景中,即将类实例化,实例化细节面板:可以看到visibleInstanceOnlyString和visibleAnywhereVector,可以编辑editInstanceOnlyString和editAnywhereVector
在蓝图中搜索blueprintRead:其中:blueprintReadOnlyInt只能get,而blueprintReadWriteInt既可以get也可以set
在蓝图中搜索value:myIntValue目录下有一个mySubIntValue目录
在蓝图中搜索myValue3,发现没有,它被别名myValue3DisPlayName所替代了
蓝图的类默认值面板中,只有勾选Controller,才可以编辑Value3
蓝图中将鼠标放置在isTrue上即可查看对应的解释
打开关卡蓝图,搜索SpawnActor from class,class选择BP_MyPawn,即可看到效果
打开BP_MyPawn蓝图,新建变量,勾选生成时公开,即可使用蓝图达到同样的效果
UFUNCTION宏,属性说明符和元数据说明符
UFUNCTION宏:生成函数的一些反射数据,能够将一些函数暴露给蓝图,实现函数和蓝图之间的通信
定义宏:(定义在MyPawn.h中)
// 暴露PrintF1函数在蓝图中,能够在蓝图中进行调用
UFUNCTION(BlueprintCallable, Category = "myFunction")
void PrintF1();
// 纯虚函数的定义BlueprintPure
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "myFunction")
bool PrintF2();
// BlueprintImplementableEvent在C++中声明,不能定义,蓝图可重载
// 无返回值的是事件,有返回值的是函数
UFUNCTION(BlueprintImplementableEvent)
void Test1();
UFUNCTION(BlueprintImplementableEvent)
int Test2();
UFUNCTION(BlueprintImplementableEvent)
void Test11(const FString &myString);
UFUNCTION(BlueprintImplementableEvent)
int Test22(const FString &myString);
// BlueprintNativeEvent在C++中声明和实现,蓝图可选择性重载
UFUNCTION(BlueprintNativeEvent)
void TestA();
UFUNCTION(BlueprintNativeEvent)
int TestB();
UFUNCTION(BlueprintNativeEvent)
void TestC(const FString &myString);
UFUNCTION(BlueprintNativeEvent)
int TestD(const FString& myString);
// meta 元数据说明符
UFUNCTION(BlueprintCallable,Category="myFunction",meta=(DisPlayName="myPrintTest"))
void PrintTest();
纯虚函数:也可以叫 抽象函数 ,一般来说它只有函数名、参数和返回值类型 ,不需要函数体 。 这意味着它没有函数的实现,需要让派生类去实现。 C++中的纯虚函数,一般在函数签名后使用=0作为此类函数的标志。
在MyPawn.cpp文件中实现两个printf函数的定义后,运行程序,在蓝图中搜索PrintF,发现显示的两个函数的图标颜色不同,绿色为纯虚函数
在蓝图中重载4个函数,蓝图中显示为:无返回值的是事件,有返回值的是函数
在MyPawn.cpp文件中实现四个TestABCD函数的定义(注意函数名称后要加上_Implementation,否则编译会报错)后,在蓝图中重载,蓝图中显示为:无返回值的是事件,有返回值的是函数
函数的使用:MyPawn.cpp文件
void AMyPawn::BeginPlay()
{
Super::BeginPlay();
TestA();
}
...
void AMyPawn::TestA_Implementation()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("TestA"));
}
编译运行后,在蓝图中重载TestA函数,并添加输出为TestAHello
将蓝图拖拽到场景中,运行,发现TestAHello和TestA都会打印在屏幕中,即在C++实现的(本地实现)和在蓝图中重载的函数都会运行
在MyPawn.cpp中实现PrintTest函数后,运行项目,在蓝图中输入PrintTest,会发现没有,被myPrintTest替代了
UENUM(枚举)宏
UENUM(枚举)宏:生成枚举的反射数据,通过反射,将枚举暴露给蓝图,进行C++和蓝图之间的通信
定义声明方法一:(MyPawn.h文件中)
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "MyPawn.generated.h"
// BlueprintType:将枚举类型可以用于蓝图中的变量 蓝图中可以选择生成的变量类型为MyNumType
UENUM(BlueprintType)
namespace MyNumType
{
enum MyCustomEnum
{
Type1,
Type2,
Type3,
};
}
UCLASS()
class UESTUDY_API AMyPawn : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
AMyPawn();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明一个枚举变量
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyEnum")
TEnumAsByte<MyNumType::MyCustomEnum> MyCustomEnum;
};
编译运行后,在UE蓝图的细节面板中可以看到
定义声明方法二:(MyPawn.h文件中)
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "MyPawn.generated.h"
// 枚举声明方法二
UENUM(BlueprintType)
enum class EMyTestEnum :uint8
{
OneType UMETA(DisPlayName="OneType"),
TwoType UMETA(DisPlayName = "TwoType"),
ThreeType UMETA(DisPlayName = "ThreeType")
};
UCLASS()
class UESTUDY_API AMyPawn : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
AMyPawn();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明一个枚举变量
// 方法二对应
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyEnum")
EMyTestEnum MyCustonEnum2;
};
USTRUCT结构体宏
## USTRUCT结构体宏:生成结构体的反射数据,通过反射,将结构体里的变量或者方法暴露给蓝图
定义声明:(MyPawn.h文件中):
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "MyPawn.generated.h"
// BlueprintType让结构体可以在蓝图中创建变量,并且进入到变量中,可以将变量设置为结构体
// BlueprintType让蓝图中可以选择生成的变量类型为FMyTestStruct
USTRUCT(BlueprintType)
struct FMyTestStruct
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "myTestStruct")
int32 Healthl;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "myTestStruct")
FString MyName;
};
UCLASS()
class UESTUDY_API AMyPawn : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
AMyPawn();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明一个结构体变量
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "myCustomStruct")
FMyTestStruct MyCustomStruct;
};
编译运行后,在蓝图中可以看到
将结构体用于table中:
- 添加C++类Object
- 在VS中选择全部重新加载,并在MyObject.h中声明结构体
#pragma once
#include "CoreMinimal.h"
// 数据表格类对应的头文件
#include "Engine/Classes/Engine/DataTable.h"
#include "UObject/NoExportTypes.h"
#include "MyObject.generated.h"
USTRUCT(BlueprintType)
// :public FTableRowBase:让结构体可以使用数据表格,所以需要继承此类
struct FMyDateTableStruct: public FTableRowBase
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyTestDataTableStruct")
float Health;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyTestDataTableStruct")
FString Name;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyTestDataTableStruct")
int32 Level;
};
UCLASS()
class UESTUDY_API UMyObject : public UObject
{
GENERATED_BODY()
};
编译成功后创建数据表格
将数据表格另存为csv格式,并导入ue项目:,选择MyDateTableStruct
虚幻C++Ubject的实例化
- 前面步骤中已经新建了MyIObject,所以直接在MyPawn.h中添加头文件和声明
#pragma once
#include "CoreMinimal.h"
#include "MyObject.h"
#include "GameFramework/Pawn.h"
#include "MyPawn.generated.h"
UCLASS()
class UESTUDY_API AMyPawn : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
AMyPawn();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明Object变量
UPROPERTY()
UMyObject* myTestObject;
};
- 在MyPawn.cpp中实现
#include "MyPawn.h"
// Sets default values
AMyPawn::AMyPawn()
{
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMyPawn::BeginPlay()
{
Super::BeginPlay();
//TestA();
// Object实例化的实现
TSubclassOf<UMyObject> MySubClassObject = UMyObject::StaticClass();
myTestObject = NewObject<UMyObject>(GetWorld(), MySubClassObject);
if (myTestObject)
{
UE_LOG(LogTemp, Warning, TEXT("myTestObject is %s"), *myTestObject->GetName());
}
}
- 编译运行后,打开UE,运行,有输出object的名称,即Object被实例化成功
获得Object的参数
- 为Object添加一个参数,并添加一个构造函数用于初始化:(MyObject.h中)
USTRUCT(BlueprintType)
// :public FTableRowBase:让结构体可以使用数据表格,所以需要继承此类
struct FMyDataTableStruct: public FTableRowBase
{
GENERATED_USTRUCT_BODY()
// 构造函数
FMyDataTableStruct();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyTestDataTableStruct")
float Health;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyTestDataTableStruct")
FString Name;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyTestDataTableStruct")
};
UCLASS()
class UESTUDY_API UMyObject : public UObject
{
GENERATED_BODY()
public:
// 声明一个结构体变量
UPROPERTY()
FMyDataTableStruct myDataTableStruct;
};
MyObject.cpp,初始化:
#include "MyObject.h"
FMyDataTableStruct::FMyDataTableStruct()
{
// 初始化
Health = 10;
Name = TEXT("zhangsan");
Level = 1;
}
- 在MyPawn.cpp中添加打印Object的参数:
void AMyPawn::BeginPlay()
{
Super::BeginPlay();
//TestA();
// Object实例化的实现
TSubclassOf<UMyObject> MySubClassObject = UMyObject::StaticClass();
myTestObject = NewObject<UMyObject>(GetWorld(), MySubClassObject);
if (myTestObject)
{
UE_LOG(LogTemp, Warning, TEXT("myTestObject is %s"), *myTestObject->GetName());
UE_LOG(LogTemp, Warning, TEXT("myHeal is %f"), myTestObject->myDataTableStruct.Health);
UE_LOG(LogTemp, Warning, TEXT("myName is %s"), *myTestObject->myDataTableStruct.Name);
UE_LOG(LogTemp, Warning, TEXT("myLevel is %d"), myTestObject->myDataTableStruct.Level);
}
}
- 编译运行,打开UE,选择MyGameMode,运行:
虚幻C++ UGameInstance的实例化
GameInstance:全局唯一单例,这个在引擎初始化的时候就已经生成,一直存在到引擎关闭
作用:
- 引擎初始化与关闭时执行的逻辑
- 为游戏保存全局数据:比如上一个关卡的信息需要在下一个关卡使用时,我们用GameInstance保存数据但是只是临时数据,游戏结束则小时,如果想要本地持久保存数据需要用SavaGame
在UE中创建GameInstance:
在VS中,在MyGameInstance.h文件中为实例添加参数和构造函数:
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.h"
UCLASS()
class UESTUDY_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
// 创建构造函数
UMyGameInstance();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyInstance")
FString myAppID;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyInstance")
FString myUserID;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyInstance")
FString myName;
};
在MyGameInstance.cpp文件的构造函数实现中,为实例的参数进行初始化:
#include "MyGameInstance.h"
UMyGameInstance::UMyGameInstance()
{
// 初始化数据
myAppID = TEXT("123");
myUserID = TEXT("456");
myName = TEXT("wangwu");
}
在MyPawn.h中添加MyGameInstance.h头文件,并声明实例:
// 游戏实例的头文件
#include "MyGameInstance.h"
UCLASS()
class UESTUDY_API AMyPawn : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
AMyPawn();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明游戏实例
UPROPERTY()
UMyGameInstance* myInstance;
};
在MyPawn.cpp中添加MyGameInstance的实例化与输出:
void AMyPawn::BeginPlay()
{
Super::BeginPlay();
// 单例的使用
myInstance = Cast<UMyGameInstance>(GetWorld()->GetFirstPlayerController()->GetGameInstance());
if (myInstance)
{
UE_LOG(LogTemp, Warning, TEXT("myInstance is %s"), *myInstance->GetName());
UE_LOG(LogTemp, Warning, TEXT("myAppID is %s"), *myInstance->myAppID);
UE_LOG(LogTemp, Warning, TEXT("myUserID is %s"), *myInstance->myUserID);
UE_LOG(LogTemp, Warning, TEXT("myName is %s"), *myInstance->myName);
}
}
编译运行,打开UE,在编辑-项目设置-地图和模式中,将游戏实例切换为MyGameInstance:
点击运行,查看输出日志:
虚幻C++创建Actor添加初始化组件
Actor:从程序实现角度说,Actor是构成游戏世界的所有元素以及在此之上,运行游戏的规则以及逻辑的抽象实例
在UE中添加C++类Actor:
在MyActor.h中添加各组件的头文件,并添加参数:
#pragma once
#include "CoreMinimal.h"
// 场景组件头文件
#include "Components/SceneComponent.h"
// StaticMesh组件头文件
#include "Components/StaticMeshComponent.h"
// 碰撞组件头文件
#include "Components/BoxComponent.h"
// 粒子特效组件
#include "Particles/ParticleSystemComponent.h"
// 声音组件
#include "Components/AudioComponent.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class UESTUDY_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 声明变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class USceneComponent* MyScene;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UStaticMeshComponent* MyMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UParticleSystemComponent* MyParticle;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UBoxComponent* MyBox;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UAudioComponent* MyAudio;
};
在MyActor.cpp文件中的构造函数添加各组件的默认值
#include "MyActor.h"
// Sets default values
// 构造函数
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 组件默认值
MyScene = CreateDefaultSubobject<USceneComponent>(TEXT("MyCustomScene"));
MyMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyCustomStaticMesh"));
MyParticle = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MyCustomParticle"));
MyBox = CreateDefaultSubobject<UBoxComponent>(TEXT("MyCustomBox"));
MyAudio = CreateDefaultSubobject<UAudioComponent>(TEXT("MyCustomAudio"));
// 设置父子级
RootComponent = MyScene;// 将MyMesh设置为根组件
MyMesh->SetupAttachment(MyScene);// 将MyMesh放到根组件下面
MyParticle->SetupAttachment(MyScene);
MyBox->SetupAttachment(MyScene);
MyAudio->SetupAttachment(MyBox);
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
编译运行,在UE中创建基于MyActor的蓝图类,命名为BP_MyActor:
打开蓝图类,即可查看设置效果:
虚幻C++静态加载类和资源
静态加载:分为静态加载资源和静态加载类两种,在代码编译时加载资源,静态加载必须写在构造函数中
静态加载资源:
-
在UE中添加初学者内容包:
-
在MyActor.cpp中添加静态加载资源代码:
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 组件默认值
MyScene = CreateDefaultSubobject<USceneComponent>(TEXT("MyCustomScene"));
MyMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyCustomStaticMesh"));
MyParticle = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MyCustomParticle"));
MyBox = CreateDefaultSubobject<UBoxComponent>(TEXT("MyCustomBox"));
MyAudio = CreateDefaultSubobject<UAudioComponent>(TEXT("MyCustomAudio"));
// 设置父子级
RootComponent = MyScene;// 将MyMesh设置为根组件
MyMesh->SetupAttachment(MyScene);// 将MyMesh放到根组件下面
MyParticle->SetupAttachment(MyScene);
MyBox->SetupAttachment(MyScene);
MyAudio->SetupAttachment(MyBox);
// 静态加载资源
static ConstructorHelpers::FObjectFinder<UStaticMesh>TempStaticMesh(TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Shapes/Shape_Cone.Shape_Cone'"));
// 将临时静态模型设置为自己的模型 TempStaticMesh.Object获取对象资源的引用
MyMesh->SetStaticMesh(TempStaticMesh.Object);
static ConstructorHelpers::FObjectFinder<UParticleSystem>TempParticSystem(TEXT("/Script/Engine.ParticleSystem'/Game/StarterContent/Particles/P_Explosion.P_Explosion'"));
MyParticle->SetTemplate(TempParticSystem.Object);
static ConstructorHelpers::FObjectFinder<USoundWave>TempSound(TEXT("/Script/Engine.SoundWave'/Game/StarterContent/Audio/Collapse01.Collapse01'"));
MyAudio->SetSound(TempSound.Object);
}
- 编译运行,打开BP_MyActor查看效果:
静态加载类: - 在MyActor.h中声明一个MyActor
#pragma once
#include "CoreMinimal.h"
// 场景组件头文件
#include "Components/SceneComponent.h"
// StaticMesh组件头文件
#include "Components/StaticMeshComponent.h"
// 碰撞组件头文件
#include "Components/BoxComponent.h"
// 粒子特效组件
#include "Particles/ParticleSystemComponent.h"
// 声音组件
#include "Components/AudioComponent.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class UESTUDY_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 声明变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class USceneComponent* MyScene;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UStaticMeshComponent* MyMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UParticleSystemComponent* MyParticle;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UBoxComponent* MyBox;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UAudioComponent* MyAudio;
// 声明MyActor
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "MyClass")
TSubclassOf<AActor> MyActor;
};
- 在MyActor.cpp文件的构造函数中添加静态加载类的代码,并在BeginPlay函数中添加打印,静态加载类时,要在引用最后面加上_C,否则编译会报错
#include "MyActor.h"
// Sets default values
// 构造函数
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 组件默认值
MyScene = CreateDefaultSubobject<USceneComponent>(TEXT("MyCustomScene"));
MyMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyCustomStaticMesh"));
MyParticle = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MyCustomParticle"));
MyBox = CreateDefaultSubobject<UBoxComponent>(TEXT("MyCustomBox"));
MyAudio = CreateDefaultSubobject<UAudioComponent>(TEXT("MyCustomAudio"));
// 设置父子级
RootComponent = MyScene;// 将MyMesh设置为根组件
MyMesh->SetupAttachment(MyScene);// 将MyMesh放到根组件下面
MyParticle->SetupAttachment(MyScene);
MyBox->SetupAttachment(MyScene);
MyAudio->SetupAttachment(MyBox);
// 静态加载资源
static ConstructorHelpers::FObjectFinder<UStaticMesh>TempStaticMesh(TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Shapes/Shape_Cone.Shape_Cone'"));
// 将临时静态模型设置为自己的模型 TempStaticMesh.Object获取对象资源的引用
MyMesh->SetStaticMesh(TempStaticMesh.Object);
static ConstructorHelpers::FObjectFinder<UParticleSystem>TempParticSystem(TEXT("/Script/Engine.ParticleSystem'/Game/StarterContent/Particles/P_Explosion.P_Explosion'"));
MyParticle->SetTemplate(TempParticSystem.Object);
static ConstructorHelpers::FObjectFinder<USoundWave>TempSound(TEXT("/Script/Engine.SoundWave'/Game/StarterContent/Audio/Collapse01.Collapse01'"));
MyAudio->SetSound(TempSound.Object);
// 静态加载类
static ConstructorHelpers::FClassFinder<AActor> TempMyActor(TEXT("/Script/Engine.Blueprint'/Game/StarterContent/Blueprints/Blueprint_CeilingLight.Blueprint_CeilingLight_C'"));
// 设置自己的类
MyActor = TempMyActor.Class;
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 打印查看Actor是否设置成功
if (MyActor)
{
UE_LOG(LogTemp, Warning, TEXT("MyActor is %s"), *MyActor->GetName());
}
}
编译运行,在UE中,将MyActor拖拽到场景中,并运行
虚幻C++动态加载类和资源
动态加载:在游戏运行时加载资源
在MyActor.cpp的begin函数中添加动态加载的代码:注意加载类的时候引用后面要加_C
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 打印查看Actor是否设置成功
if (MyActor)
{
UE_LOG(LogTemp, Warning, TEXT("MyActor is %s"), *MyActor->GetName());
}
// 动态加载资源
UStaticMesh* MyTempStaticMesh = LoadObject<UStaticMesh>(nullptr, TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube'"));
if (MyTempStaticMesh)
{
MyMesh->SetStaticMesh(MyTempStaticMesh);
}
// 动态加载类 LoadClass<AActor>:加载AActor模板类 this:在哪个类里
UClass* MyTempClass = LoadClass<AActor>(this, TEXT("/Script/Engine.Blueprint'/Game/StarterContent/Blueprints/Blueprint_WallSconce.Blueprint_WallSconce_C'"));
if (MyTempClass)
{
// SpawnActor出生,SpawnActor<AActor>:产生一个Actor类,FVector:类产生的位置 ZeroVector:(0,0,0)
// FRotator::ZeroRotator 类出生时的旋转 ZeroRotator:旋转为0 缩放默认值为(1,1,1)
AActor* SpawnActor = GetWorld()->SpawnActor<AActor>(MyTempClass, FVector::ZeroVector,FRotator::ZeroRotator);
}
}
运行之后Actor从锥型变为长方体
虚幻C++创建摄像机摇臂和相机,并且设置旋转位移缩放
在MyPawn.h中添加摄像机摇臂和相机的头文件,并声明对应的变量
#pragma once
#include "CoreMinimal.h"
// Object的头文件
#include "MyObject.h"
// 游戏实例的头文件
#include "MyGameInstance.h"
// 摄像机摇臂头文件
#include "GameFramework/SpringArmComponent.h"
// 相机头文件
#include "Camera/CameraComponent.h"
#include "GameFramework/Pawn.h"
#include "MyPawn.generated.h"
UCLASS()
class UESTUDY_API AMyPawn : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
AMyPawn();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明摇臂和相机变量
public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
USceneComponent* MyRoot;
// 摄像机摇臂组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
USpringArmComponent* MySpringArm;
// 相机组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UCameraComponent* MyCamera;
};
在MyPawn.cpp文件的构造函数中初始化摄像头摇臂和相机,并设置其父子集关系及碰撞,在BeginPlay函数中设置旋转的位移和缩放
#include "MyPawn.h"
// Sets default values
AMyPawn::AMyPawn()
{
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 摄像头摇臂和相机初始化
MyRoot = CreateDefaultSubobject<USceneComponent>(TEXT("MyRootComponent"));
MySpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("MySpringArmComponent"));
MyCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("MyCameraComponent"));
// 设置父子级
RootComponent = MyRoot;
MySpringArm->SetupAttachment(MyRoot);
MyCamera->SetupAttachment(MySpringArm);
// 设置MySpringArm的碰撞
MySpringArm->bDoCollisionTest = false;// 不作为碰撞体
}
// Called when the game starts or when spawned
void AMyPawn::BeginPlay()
{
Super::BeginPlay();
// 设置旋转的位移和缩放
FVector MyLocation = FVector(0, 0, 0);
FRotator MyRotation = FRotator(-50, 0, 0);
FVector MyScale = FVector(1, 1, 1);
SetActorLocation(MyLocation);
SetActorRotation(MyRotation);
SetActorScale3D(MyScale);
}
虚幻C++按键映射绑定以及使用鼠标滑轮控制镜头缩放远近
在UE的编辑—项目设置—输入—绑定—操作映射中加入两个按键映射
MyPlayerController.cpp文件中:
#include "MyPawn.h"
#include "MyPlayerController.h"
void AMyPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();// 继承自父类
// 重写按键输入绑定函数的实现
// InputComponent:输入组件,也是输入的变量 BindAction(按键映射名称,按下的方式,对象在哪个类中,绑定的函数):绑定按键映射
InputComponent->BindAction("WheelUp",IE_Pressed,this,&AMyPlayerController::WheelUpFunction);
InputComponent->BindAction("WheelDown", IE_Pressed, this, &AMyPlayerController::WheelDownFunction);
}
void AMyPlayerController::WheelUpFunction()
{
// 如果获得了Pawn
if (GetPawn())
{
// 用getPawn转化为Pawn
AMyPawn* myCameraPawn = Cast<AMyPawn>(GetPawn());
if (myCameraPawn)
{
myCameraPawn->Zoom(1, 10);
}
}
}
void AMyPlayerController::WheelDownFunction()
{
// 用getPawn转化为Pawn
AMyPawn* myCameraPawn = Cast<AMyPawn>(GetPawn());
if (myCameraPawn)
{
myCameraPawn->Zoom(0, 10);
}
}
MyPlayerController.h文件中:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "MyPlayerController.generated.h"
/**
*
*/
UCLASS()
class UESTUDY_API AMyPlayerController : public APlayerController
{
GENERATED_BODY()
public:
// 重写按键输入绑定函数
virtual void SetupInputComponent();
// 滑轮向上时绑定的函数
void WheelUpFunction();
// 滑轮向下时绑定的函数
void WheelDownFunction();
};
MyPawn.h文件中:声明Zoom函数
UCLASS()
class UESTUDY_API AMyPawn : public APawn
{
GENERATED_BODY()
// 声明摇臂和相机变量
public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
USceneComponent* MyRoot;
// 摄像机摇臂组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
USpringArmComponent* MySpringArm;
// 相机组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UCameraComponent* MyCamera;
// 控制相机摇臂的缩放 鼠标滑轮移动镜头缩放
void Zoom(bool Direction, float ZoomSpeed);
};
MyPawn.cpp文件中:实现Zoom函数
// 鼠标滑轮移动镜头缩放函数的实现
void AMyPawn::Zoom(bool Direction, float ZoomSpeed)
{
if (Direction)// 滚轮向上滑动
{
if (MySpringArm->TargetArmLength >= 300 && MySpringArm->TargetArmLength <= 5000)
{
MySpringArm->TargetArmLength += (ZoomSpeed * 2);
// 打印摄像机摇臂的长度
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("SpringArmLength is %f"), MySpringArm->TargetArmLength));
}
}
else
{
if (MySpringArm->TargetArmLength > 300 && MySpringArm->TargetArmLength <= 5000)
{
MySpringArm->TargetArmLength -= (ZoomSpeed * 2);
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("SpringArmLength is %f"), MySpringArm->TargetArmLength));
}
}
}
虚幻C++AddActorLocalOffset和AddActorWorldOffset
AddActorLocalOffset:添加Actor的本地坐标
AddActorWorldOffset:添加Actor的世界坐标
在MyActor.cpp中添加对应代码:
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 偏移量
FVector MyOffSet = FVector(1, 0, 0);
// 返回的碰撞参数:如碰撞位置,碰撞的Actor和坐标之类的信息
FHitResult HitResult;
// 改变本地坐标,X轴每帧增加1
// 参数:偏移量,是否产生碰撞,返回的碰撞参数
AddActorLocalOffset(MyOffSet,false,&HitResult);
// 改变世界坐标,X轴每帧增加1
// AddActorWorldOffset(MyOffSet, false, &HitResult);
}
分别注释代码,编译运行,在UE中添加MyActor并旋转一定角度后运行,发现改变本地坐标时,物体沿自己的x轴移动,改变世界坐标时,物体沿正上方移动
虚幻C++的代理绑定
BeginOverlap和EndOverlap的代理绑定
在MyActor.cpp文件的BeginPlay中添加代码:
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 利用碰撞box绑定BeginOverlap 参数:对象,绑定的函数
MyBox->OnComponentBeginOverlap.AddDynamic(this, &AMyActor::BeginOverlapFunction);
// 利用碰撞box绑定EndOverlap 参数:对象,绑定的函数
MyBox->OnComponentEndOverlap.AddDynamic(this, &AMyActor::EndOverlapFunction);
}
// 利用碰撞box绑定BeginOverlap 绑定的函数的实现
void AMyActor::BeginOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("BeginOverLap is seccess"));
}
// 利用碰撞box绑定EndOverlap 绑定的函数的实现
void AMyActor::EndOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("EndOverLap is seccess"));
}
在MyActor.h文件中添加代码:
UCLASS()
class UESTUDY_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// 利用碰撞box绑定BeginOverlap 绑定的函数的声明 两个物体开始重叠时
// DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SixParams( FComponentBeginOverlapSignature, UPrimitiveComponent, OnComponentBeginOverlap,
// UPrimitiveComponent*, OverlappedComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp,
// int32, OtherBodyIndex, bool, bFromSweep, const FHitResult &, SweepResult);
UFUNCTION()
// 复制原函数的后6个参数,并删掉类型和参数名中间的逗号
void BeginOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
// 利用碰撞box绑定EndOverlap 绑定的函数的声明 两个物体结束重叠时
//DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams( FComponentEndOverlapSignature, UPrimitiveComponent,OnComponentEndOverlap,
// UPrimitiveComponent*, OverlappedComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, int32, OtherBodyIndex);
UFUNCTION()
// 复制原函数的4个参数,并删掉类型和参数名中间的逗号
void EndOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
};
编译运行,打开UE,在UE中添加第三人称模板
将GameMode改为None,将第三人称人物拖动到场景中:
修改键盘控制玩家为玩家0
在BP_MyActor的蓝图类中修改box的缩放大小为(5,5,5),点击运行,控制玩家靠近Actor,观察输出:在玩家刚开始与物体重叠时,输出BeginOcerlap is seccess,当玩家与物体结束重叠时,输出EndOverLap is seccess
Hit事件的代理绑定
在MyActor.cpp文件的BeginPlay中添加代码:
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 利用碰撞box绑定Hit 参数:对象,绑定的代理函数
MyBox->OnComponentHit.AddDynamic(this, &AMyActor::HitFunction);
}
// 利用碰撞box绑定Hit 绑定的函数的实现
void AMyActor::HitFunction(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Hit is seccess"));
}
在MyActor.h文件中添加代码:
UCLASS()
class UESTUDY_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// 利用碰撞box绑定Hit 绑定的函数的声明
// DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams(FComponentHitSignature, UPrimitiveComponent, OnComponentHit,
// UPrimitiveComponent*, HitComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, FVector, NormalImpulse, const FHitResult&, Hit);
UFUNCTION()
// 复制原函数的5个参数,并删掉类型和参数名中间的逗号
void HitFunction(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
};
编译运行,打开UE,将GameMode改为None,将第三人称人物拖动到场景中,修改键盘控制玩家为玩家0,打开BP_MyActor蓝图类,修改以下位置的参数:将碰撞预设设置为Custom(自定义),将被Pawn碰到的碰撞改为Block(阻挡),勾选模拟生成命中事件(这样才会产生Hit事件)
把BP_MyActor拖动到场景中,将box大小改为(5,5,5),移动人物靠近Actor观察输出:两个物体表面靠近,即发生Hit事件
虚幻C++碰撞设置
在MyActor.cpp文件的构造函数中设置碰撞:
// 构造函数
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 碰撞设置 SetCollisionEnabled:设置碰撞输入
MyBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);// 此组件没有碰撞效果
MyBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);// 此组件只有查询碰撞 只有事件检测和重叠
MyBox->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);// 此组件只有物理碰撞 刚体和约束等碰撞
MyBox->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);// 此组件既有查询碰撞,也有物理碰撞
MyBox->SetCollisionEnabled(ECollisionEnabled::ProbeOnly);//此组件只有探测碰撞
MyBox->SetCollisionEnabled(ECollisionEnabled::QueryAndProbe); // 此组件既有查询碰撞,也有探测碰撞
// 碰撞设置 SetCollisionObjectType:设置碰撞对象类型
MyBox->SetCollisionObjectType(ECC_WorldStatic);// 世界的静态对象
MyBox->SetCollisionObjectType(ECC_WorldDynamic);// 世界的动态对象
MyBox->SetCollisionObjectType(ECC_Pawn);
MyBox->SetCollisionObjectType(ECC_PhysicsBody);
MyBox->SetCollisionObjectType(ECC_Vehicle);// 载具
MyBox->SetCollisionObjectType(ECC_Destructible);// 可破碎物体
// 碰撞设置 碰撞响应
// SetCollisionResponseToAllChannels:对所有通道进行设置
// SetCollisionResponseToChannel:对单个通道进行设置
MyBox->SetCollisionResponseToAllChannels(ECR_Block);// 将所有的通道碰撞响应设置为Block(阻挡)
MyBox->SetCollisionResponseToAllChannels(ECR_Overlap);// 将所有的通道碰撞响应设置为Overlap(重叠)
MyBox->SetCollisionResponseToAllChannels(ECR_Ignore);// 将所有的通道碰撞响应设置为Ignore(忽略)
MyBox->SetCollisionResponseToChannel(ECC_Pawn,ECR_Overlap);// 将Pawn的碰撞响应设置为Overlap
MyBox->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);// 将世界静态的碰撞响应设置为Block
MyBox->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Ignore);// 将世界动态的碰撞响应设置为Ignore
}
编译运行,打开UE,重新生成BP_Actor蓝图类,查看碰撞设置:collision Enabled(碰撞已启用)设置为了最后设置的QueryAndProbe,Object Type设置成了最后设置的Destructible(可破碎物体)…
虚幻C++粒子特效的激活和失效
在MyActor.cpp中:
#include "MyActor.h"
// Sets default values
// 构造函数
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 组件默认值
MyScene = CreateDefaultSubobject<USceneComponent>(TEXT("MyCustomScene"));
MyMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyCustomStaticMesh"));
MyParticle = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MyCustomParticle"));
MyBox = CreateDefaultSubobject<UBoxComponent>(TEXT("MyCustomBox"));
MyAudio = CreateDefaultSubobject<UAudioComponent>(TEXT("MyCustomAudio"));
// 设置父子级
RootComponent = MyScene;// 将MyMesh设置为根组件
MyMesh->SetupAttachment(MyScene);// 将MyMesh放到根组件下面
MyParticle->SetupAttachment(MyScene);
MyBox->SetupAttachment(MyScene);
MyAudio->SetupAttachment(MyBox);
// 静态加载资源
static ConstructorHelpers::FObjectFinder<UStaticMesh>TempStaticMesh(TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Shapes/Shape_Cone.Shape_Cone'"));
// 将临时静态模型设置为自己的模型 TempStaticMesh.Object获取对象资源的引用
MyMesh->SetStaticMesh(TempStaticMesh.Object);
static ConstructorHelpers::FObjectFinder<UParticleSystem>TempParticSystem(TEXT("/Script/Engine.ParticleSystem'/Game/StarterContent/Particles/P_Explosion.P_Explosion'"));
MyParticle->SetTemplate(TempParticSystem.Object);
static ConstructorHelpers::FObjectFinder<USoundWave>TempSound(TEXT("/Script/Engine.SoundWave'/Game/StarterContent/Audio/Collapse01.Collapse01'"));
MyAudio->SetSound(TempSound.Object);
// 静态加载类
static ConstructorHelpers::FClassFinder<AActor> TempMyActor(TEXT("/Script/Engine.Blueprint'/Game/StarterContent/Blueprints/Blueprint_CeilingLight.Blueprint_CeilingLight_C'"));
// 设置自己的类
MyActor = TempMyActor.Class;
// 碰撞设置 SetCollisionEnabled:设置碰撞输入
//MyBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);// 此组件没有碰撞效果
MyBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);// 此组件只有查询碰撞 只有事件检测和重叠
//MyBox->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);// 此组件只有物理碰撞 刚体和约束等碰撞
//MyBox->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);// 此组件既有查询碰撞,也有物理碰撞
//MyBox->SetCollisionEnabled(ECollisionEnabled::ProbeOnly);//此组件只有探测碰撞
//MyBox->SetCollisionEnabled(ECollisionEnabled::QueryAndProbe); // 此组件既有查询碰撞,也有探测碰撞
// 碰撞设置 SetCollisionObjectType:设置碰撞对象类型
//MyBox->SetCollisionObjectType(ECC_WorldStatic);// 世界的静态对象
MyBox->SetCollisionObjectType(ECC_WorldDynamic);// 世界的动态对象
//MyBox->SetCollisionObjectType(ECC_Pawn);
//MyBox->SetCollisionObjectType(ECC_PhysicsBody);
//MyBox->SetCollisionObjectType(ECC_Vehicle);// 载具
//MyBox->SetCollisionObjectType(ECC_Destructible);// 可破碎物体
// 碰撞设置 碰撞响应
// SetCollisionResponseToAllChannels:对所有通道进行设置
// SetCollisionResponseToChannel:对单个通道进行设置
//MyBox->SetCollisionResponseToAllChannels(ECR_Block);// 将所有的通道碰撞响应设置为Block(阻挡)
MyBox->SetCollisionResponseToAllChannels(ECR_Overlap);// 将所有的通道碰撞响应设置为Overlap(重叠)
//MyBox->SetCollisionResponseToAllChannels(ECR_Ignore);// 将所有的通道碰撞响应设置为Ignore(忽略)
//MyBox->SetCollisionResponseToChannel(ECC_Pawn,ECR_Overlap);// 将Pawn的碰撞响应设置为Overlap
//MyBox->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);// 将世界静态的碰撞响应设置为Block
//MyBox->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Ignore);// 将世界动态的碰撞响应设置为Ignore
// 设置Box大小
MyBox->SetBoxExtent(FVector(64, 64, 64));
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 游戏开始时,粒子系统默认失效
if(MyParticle)
{
MyParticle->Deactivate();
}
// 利用碰撞box绑定BeginOverlap 参数:对象,绑定的函数
MyBox->OnComponentBeginOverlap.AddDynamic(this, &AMyActor::BeginOverlapFunction);
// 利用碰撞box绑定EndOverlap 参数:对象,绑定的函数
MyBox->OnComponentEndOverlap.AddDynamic(this, &AMyActor::EndOverlapFunction);
// 利用碰撞box绑定Hit 参数:对象,绑定的代理函数
MyBox->OnComponentHit.AddDynamic(this, &AMyActor::HitFunction);
}
// Called every frame
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 利用碰撞box绑定BeginOverlap 绑定的函数的实现
void AMyActor::BeginOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
// 碰撞时粒子生效
MyParticle->Activate();
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("BeginOverLap is seccess"));
}
// 利用碰撞box绑定EndOverlap 绑定的函数的实现
void AMyActor::EndOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
// 离开时粒子失效
MyParticle->Deactivate();
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("EndOverLap is seccess"));
}
编译运行,在UE中右键MyActor创建蓝图类BP_MyActor,查看设置,Box范围设置结果
碰撞设置结果:
将Actor和第三方人物都拖拽到世界中,将GameMode改为None,修改键盘控制玩家为玩家0,运行项目,控制玩家碰撞Actor,玩家碰撞Actor时即显示一次粒子特效
虚幻C++创建UserWidget并且绑定Button点击事件
- 在UE中创建C++类UserWidget:
- 在VS的MyUserWidget.cpp中添加:
#include "MyUserWidget.h"
bool UMyUserWidget::Initialize()
{
if (!Super::Initialize())// 是否继承了父类组件,继承了返回为真,否则为假
{
return false;
}
// 创建代理绑定
// 按钮点击事件 参数:对象,绑定函数,
ButtonStart->OnClicked.AddDynamic(this, &UMyUserWidget::Start);
ButtonQuit->OnClicked.AddDynamic(this, &UMyUserWidget::Quit);
return true;
}
void UMyUserWidget::Start()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Start"));
}
void UMyUserWidget::Quit()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Quit"));
}
- 在VS的MyUserWidget.h中添加:
#pragma once
#include "CoreMinimal.h"
// 按钮Button的头文件
#include "Components/Button.h"
#include "Blueprint/UserWidget.h"
#include "MyUserWidget.generated.h"
UCLASS()
class UESTUDY_API UMyUserWidget : public UUserWidget
{
GENERATED_BODY()
public:// 声明变量
UPROPERTY(meta = (BindWidget))
UButton* ButtonStart;
UPROPERTY(meta = (BindWidget))
UButton* ButtonQuit;
// 初始化函数 编译时调用
virtual bool Initialize() override;
// 按钮点击事件绑定的函数的声明
UFUNCTION()
void Start();
UFUNCTION()
void Quit();
};
- 编译运行,打开UE,在Content(内容)文件夹中创建Widget Blueprint,命名为UMG_Widget:
- 在控件蓝图中添加Canvas Panel(画布面板)和两个Button(按钮),按钮的名字要与C++中创建的名字一致,否则编译会报错
- 编译后,切换至图表面板,将类设置中的类选项的父类设置为MyUserWidget,编译保存
- 复制UMG_Widget的引用:/Script/UMGEditor.WidgetBlueprint’/Game/UMG_Widget.UMG_Widget’
创建Widget并且将其添加到屏幕中:
- 在MyPlayerController.h中声明重写BeginPlay函数
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "MyPlayerController.generated.h"
/**
*
*/
UCLASS()
class UESTUDY_API AMyPlayerController : public APlayerController
{
GENERATED_BODY()
public:
// 重写BeginPlay函数
virtual void BeginPlay()override;
};
- 在MyPlayerController.cpp中,添加对应的头文件,及相应代码,记得要在UserWidget的引用的末尾加上_C,否则编译运行时会报错
#include "MyPlayerController.h"
#include "MyPawn.h"
// 用户面板的头文件
#include "Blueprint/UserWidget.h"
void AMyPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();// 继承自父类
}
void AMyPlayerController::BeginPlay()
{
// 创建Widget并且将其添加到屏幕中
Super::BeginPlay();// 继承自父类
UClass* widgetClass = LoadClass<UUserWidget>(NULL, TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/UMG_Widget.UMG_Widget_C'"));
// 声明一个UserWdget类型的变量
UUserWidget* MyWidgetClass = nullptr;
MyWidgetClass = CreateWidget<UUserWidget>(GetWorld(), widgetClass);
// 将MyWidgetClass添加到屏幕上
MyWidgetClass->AddToViewport();
}
- 编译运行,将GameMode改为MyGameMode,运行即可看到Widget,依次点击按钮,会出现相应的输出:
虚幻C++进度条Progress的实现
- 在MyUserWidget.h文件中添加进度条的实现代码:
#pragma once
#include "CoreMinimal.h"
// 按钮Button的头文件
#include "Components/Button.h"
#include "Blueprint/UserWidget.h"
#include "MyUserWidget.generated.h"
UCLASS()
class UESTUDY_API UMyUserWidget : public UUserWidget
{
GENERATED_BODY()
public:// 声明变量
UPROPERTY(meta = (BindWidget))
UButton* ButtonStart;
UPROPERTY(meta = (BindWidget))
UButton* ButtonQuit;
// 进度条的实现
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "myHealth")
float CurrentHealth = 100.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "myHealth")
float MaxHealth = 100.0f;
// 更新血量值的函数的声明
void UpdateHealth();
// 初始化函数 编译时调用
virtual bool Initialize() override;
// 按钮点击事件绑定的函数的声明
UFUNCTION()
void Start();
UFUNCTION()
void Quit();
};
- 在MyUserWidget.cpp文件中添加对应的实现:
#include "MyUserWidget.h"
// 更新血量值的函数的实现
void UMyUserWidget::UpdateHealth()
{
// 判断当前血量值
if(CurrentHealth <=0)// 血量值小于0,游戏结束
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Game Over"));
}
else
{
CurrentHealth -= 10;
}
}
bool UMyUserWidget::Initialize()
{
if (!Super::Initialize())// 是否继承了父类组件,继承了返回为真,否则为假
{
return false;
}
// 创建代理绑定
// 按钮点击事件 参数:对象,绑定函数,
ButtonStart->OnClicked.AddDynamic(this, &UMyUserWidget::Start);
ButtonQuit->OnClicked.AddDynamic(this, &UMyUserWidget::Quit);
return true;
}
void UMyUserWidget::Start()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Start"));
// 调用掉血函数
UpdateHealth();
}
void UMyUserWidget::Quit()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Quit"));
}
- 编译运行,在UE的UMG_Widget蓝图中,添加Progress Bar(进度条):
- 创建绑定
- 用当前血量值除以最大血量值以获得进度条的百分比,编译保存
- 将GameMode设置为MyGameMode,运行,点击Start按钮,观察输出和进度条变化:
虚幻C++的代理
代理机制:代理也叫做委托,其作用就是提供一种消息机制,我们知道消息的传递需要发送方和接收方,而代理的过程也可分为这两大部分,我们可以换个名字分别叫做:出发点和执行点,这就是代理的主要部分,记住这两个点就能记住代理的原理。其实代理的方法和软件模式中的观察者模式是统一原理
代理:分为单播代理、多播代理和动态多播代理
单播代理的实现
- 在UE中创建一个C++类Actor用来绑定代理,命名为MyDelegateActor
- MyDelegateActor.cpp文件
#include "MyDelegateActor.h"
// Sets default values
AMyDelegateActor::AMyDelegateActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 绑定 参数:对象,绑定的函数
NoParamDelegate.BindUObject(this, &AMyDelegateActor::NoParamDelegateFunction);
OneParamDelegate.BindUObject(this, &AMyDelegateActor::OneParamDelegateFunction);
TwoParamDelegate.BindUObject(this, &AMyDelegateActor::TwoParamDelegateFunction);
ThreeParamDelegate.BindUObject(this, &AMyDelegateActor::ThreeParamDelegateFunction);
RetvalDelegate.BindUObject(this, &AMyDelegateActor::RetvalDelegateFunction);
}
// Called when the game starts or when spawned
void AMyDelegateActor::BeginPlay()
{
Super::BeginPlay();
// 进行代理的执行
NoParamDelegate.ExecuteIfBound();// 判断是否绑定,绑定了就执行
OneParamDelegate.ExecuteIfBound("OneParamDelegate");
TwoParamDelegate.ExecuteIfBound("TwoParamDelegate", 10);
ThreeParamDelegate.ExecuteIfBound("ThreeParamDelegate", 10, 5.0);
FString strValue = RetvalDelegate.Execute();
}
// Called every frame
void AMyDelegateActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMyDelegateActor::NoParamDelegateFunction()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, TEXT("NoParamDelegate"));
}
void AMyDelegateActor::OneParamDelegateFunction(FString str)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, PFString::Printf(TEXT("%s"), *str));
}
void AMyDelegateActor::TwoParamDelegateFunction(FString str, int32 value)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("%s %d"), *str, value));
}
void AMyDelegateActor::ThreeParamDelegateFunction(FString str, int32 value, float value1)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("%s %d %f"), *str, value, value1));
}
FString AMyDelegateActor::RetvalDelegateFunction()
{
FString str = FString::Printf(TEXT("RetvalDelegate"));
return str;
}
- MyDelegateActor.h文件
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyDelegateActor.generated.h"
// 声明单播代理
// 无参单播代理 NoParamDelegate:代理名称
DECLARE_DELEGATE(NoParamDelegate);
// 有一个参数的单播代理 FString:参数类型Clare
DECLARE_DELEGATE_OneParam(OneParamDelegate, FString);
// 有两个参数的单播代理 FString, int32:参数类型
DECLARE_DELEGATE_TwoParams(TwoParamDelegate, FString, int32);
// 有三个参数的单播代理 FString, int32, float:参数类型
DECLARE_DELEGATE_ThreeParams(ThreeParamDelegate, FString, int32, float);
// 带有返回参数的单播代理 FString:返回参数 RetvalDelegate:代理名称
DECLARE_DELEGATE_RetVal(FString, RetvalDelegate);
UCLASS()
class UESTUDY_API AMyDelegateActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyDelegateActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 声明代理名称变量
NoParamDelegate NoParamDelegate;
OneParamDelegate OneParamDelegate;
TwoParamDelegate TwoParamDelegate;
ThreeParamDelegate ThreeParamDelegate;
RetvalDelegate RetvalDelegate;
// 声明绑定函数
void NoParamDelegateFunction();
void OneParamDelegateFunction(FString str);
void TwoParamDelegateFunction(FString str, int32 value);
void ThreeParamDelegateFunction(FString str, int32 value, float value1);
FString RetvalDelegateFunction();
};
- 编译运行,打开UE,把MyDelegateActor拖入场景中,点击运行
多播代理的实现
- MyDelegateActor.cpp文件:
#include "MyDelegateActor.h"
// Sets default values
AMyDelegateActor::AMyDelegateActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 多播代理的绑定 多播代理可以绑定多个函数
OneParamMultiDelegate.AddUObject(this, &AMyDelegateActor::MultiDelegateFunction1);
OneParamMultiDelegate.AddUObject(this, &AMyDelegateActor::MultiDelegateFunction2);
OneParamMultiDelegate.AddUObject(this, &AMyDelegateActor::MultiDelegateFunction3);
}
// Called when the game starts or when spawned
void AMyDelegateActor::BeginPlay()
{
Super::BeginPlay();
// 进行多播代理的执行
OneParamMultiDelegate.Broadcast("OneParamMultiDelegate");
}
// Called every frame
void AMyDelegateActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 多播代理绑定函数的实现
void AMyDelegateActor::MultiDelegateFunction1(FString str)
{
FString TempStr = str.Append("1");
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("%s"), *TempStr));
}
void AMyDelegateActor::MultiDelegateFunction2(FString str)
{
FString TempStr = str.Append("2");
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("%s"), *TempStr));
}
void AMyDelegateActor::MultiDelegateFunction3(FString str)
{
FString TempStr = str.Append("3");
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("%s"), *TempStr));
}
- MyDelegateActor.h文件
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyDelegateActor.generated.h"
// 声明多播代理
// 有一个参数的多播代理 OneParamMultiDelegate:代理名称 FString:参数类型
DECLARE_MULTICAST_DELEGATE_OneParam(OneParamMultiDelegate, FString);
UCLASS()
class UESTUDY_API AMyDelegateActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyDelegateActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 声明多播代理变量名称
OneParamMultiDelegate OneParamMultiDelegate;
// 声明多播代理绑定函数
UFUNCTION()
void MultiDelegateFunction1(FString str);
UFUNCTION()
void MultiDelegateFunction2(FString str);
UFUNCTION()
void MultiDelegateFunction3(FString str);
};
- 编译运行,打开UE,把MyDelegateActor拖入场景中,点击运行:
动态多播代理的实现
- MyDelegateActor.cpp文件:
#include "MyDelegateActor.h"
// Sets default values
AMyDelegateActor::AMyDelegateActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMyDelegateActor::BeginPlay()
{
Super::BeginPlay();
// 执行动态多播代理 绑定在蓝图中实现
DynamicMultiDelegate.Broadcast("DynamicMultiDelegate");
}
// Called every frame
void AMyDelegateActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
- MyDelegateActor.h文件
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyDelegateActor.generated.h"
// 声明动态多播代理 和动态多播代理的区别在于动态多播代理可以暴露给蓝图,在蓝图中进行事件的绑定 param:参数名称
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDynamicMultiDelegate, FString, param);
UCLASS()
class UESTUDY_API AMyDelegateActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyDelegateActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 声明动态多播代理变量
UPROPERTY(BlueprintAssignable) // 将动态多播代理可以暴露给蓝图,在蓝图中进行事件的绑定
FDynamicMultiDelegate DynamicMultiDelegate;
};
- 编译运行,打开UE,创建基于MyDelegateActord的蓝图类,命名为BP_MyDelegateActor,在蓝图中进行事件的绑定:
- 编译保存,将BP_MyDelegateActor拖动到场景中,运行:
虚幻C++子弹类型设置
- 打开UE,创建C++类Actor,命名为MyBullet
- MyBullet.h文件:
#pragma once
#include "CoreMinimal.h"
// staticMesh头文件
#include "Components/StaticMeshComponent.h"
// 球型碰撞头文件
#include "Components/SphereComponent.h"
// 运动组件头文件
#include "GameFramework/ProjectileMovementComponent.h"
#include "GameFramework/Actor.h"
#include "MyBullet.generated.h"
UCLASS()
class UESTUDY_API AMyBullet : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyBullet();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "myComponent")
UStaticMeshComponent* MyBullet;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "myComponent")
USphereComponent* MySphere;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "myComponent")
UProjectileMovementComponent* MyProjectile;
};
- MyBullet.cpp文件:
#include "MyBullet.h"
// Sets default values
AMyBullet::AMyBullet()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 初始化
MyBullet = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyBulletComponent"));
MySphere = CreateDefaultSubobject<USphereComponent>(TEXT("MySphereComponent"));
MyProjectile= CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("MyPronjectileComponent"));
// 静态加载,设置模型
static ConstructorHelpers::FObjectFinder<UStaticMesh>TempStaticMesh(TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Shapes/Shape_Cone.Shape_Cone'"));
MyBullet->SetStaticMesh(TempStaticMesh.Object);
MyBullet->SetRelativeScale3D(FVector(0.4, 0.4, 0.4));
// 设置父子级
RootComponent = MyBullet;
MySphere->SetupAttachment(MyBullet);
MyProjectile->SetUpdatedComponent(MyBullet);
MyProjectile->InitialSpeed = 1200.0f;
MyProjectile->MaxSpeed = 2400.0f;
MyProjectile->bRotationFollowsVelocity = true;
MyProjectile->bIsHomingProjectile = true;
MyProjectile->ProjectileGravityScale = 1.5f;
}
// Called when the game starts or when spawned
void AMyBullet::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMyBullet::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
- 编译运行,打开UE,创建基于MyBullet的蓝图类,命名为BP_MyBullet,在蓝图中查看设置:
虚幻C++创建Character添加增强输入
-
在UE中新建C++类Character,命名为MyCharacter
-
在VS的UEStudy.build.cs文件中,添加增强输入模块:
-
在MyCharacter.h中:
#pragma once
#include "CoreMinimal.h"
// 输入映射 value值
#include "InputActionValue.h"
//增强输入头文件
#include "EnhancedInputComponent.h"
// 增强输入子系统
#include "EnhancedInputSubsystems.h"
// 控制器头文件
#include "GameFramework/Controller.h"
// 摄像机摇臂头文件
#include "GameFramework/SpringArmComponent.h"
// 相机头文件
#include "Camera/CameraComponent.h"
// 运动组件头文件
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
UCLASS()
class UESTUDY_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明摄像头摇臂变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
USpringArmComponent* MySpringArm;
// 声明相机变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UCameraComponent* MyCamera;
// 声明输入映射相关变量
// 输入映射上下文
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputMappingContext* DefaultMappingContext;
// 移动映射输入变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* MoveAction;
// 旋转映射变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* LookAction;
// 鼠标按下移动时的代理绑定函数
// FInputActionValue:鼠标按下和键盘按下时会有值输入
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
};
- 在MyCharacter.cpp中:
#include "MyCharacter.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 初始化
MySpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("MySpringArmComponent"));
MyCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("MyCameraComponent"));
// 设置摄像机摇臂长度
MySpringArm->TargetArmLength = 400.0f;
// 设置根组件 将相机设置为摄像机摇臂的组件
MyCamera->SetupAttachment(MySpringArm);
// 将摄像机摇臂设置为默认根组件的组件
MySpringArm->SetupAttachment(RootComponent);
// 让控制器的旋转不影响角色的转动,只影响摄像机的转动
bUseControllerRotationPitch = false;// 设置Pitch旋转
bUseControllerRotationRoll = false;// 设置Roll旋转
bUseControllerRotationYaw = false;// 设置Yaw旋转
// 让角色面朝加速度的方向
GetCharacterMovement()->bOrientRotationToMovement = true;
// 使用Pawn控制器的旋转
MySpringArm->bUsePawnControlRotation = true;
}
// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayController = Cast<APlayerController>(Controller))// 判断控制器是否合法
{
// 判断玩家子系统 获取增强输入的本地玩家子系统 将本地控制器转化为增强输入本地玩家子系统
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayController->GetLocalPlayer()))
{
// 转化成功 添加映射输入上下文 0为优先级,数值越大,优先级越高
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
}
// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
// 输入映射
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// 将玩家输入映射,转化为增强输入玩家映射 判断是否有输入映射
// CastChecked<UEnhancedInputComponent>(PlayerInputComponent):将PlayerInputComponent转化为UEnhancedInputComponent
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
// 绑定映射 MoveAction:映射变量,ETriggerEvent::Trigger:方式,this:对象,&AMyCharacter::Move绑定的函数
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::Look);
}
}
// 移动
void AMyCharacter::Move(const FInputActionValue& Value)
{
// 用2D变量来控制移动 Value.Get<FVector2D>():获得输入的2D向量坐标
FVector2D MovementVector = Value.Get<FVector2D>();
// 判断控制器是否合法
if (Controller != nullptr)
{
// 通过变量的旋转获得他的前向向量和右向向量,来控制前后左右移动
// Controller->GetControlRotation():获得控制器的旋转
const FRotator Rotation = Controller->GetControlRotation();
// 获取Yaw方向上的旋转
const FRotator YawRotation(0, Rotation.Yaw, 0);
// 获得前向向量(X轴方向)
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// 获得右向向量(Y轴方向)
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// 在键盘按下WASD时,添加输入
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
// 旋转
void AMyCharacter::Look(const FInputActionValue& Value)
{
// 获得鼠标X和Y轴的向量
FVector2D LookAxisVector = Value.Get<FVector2D>();
// 判断控制器是否合法,即是否有输入
if (Controller != nullptr)
{
// 获得X轴方向的向量值
AddControllerYawInput(LookAxisVector.X);
// 获得Y轴方向的向量值
AddControllerPitchInput(LookAxisVector.Y);
}
}
- 编译运行,打开UE,创建基于MyCharacter的蓝图类,命名为BP_MyCharacter,打开蓝图,为网格体添加变换、网格体和动画类,编译保存
- 在类设置中,为几个映射添加默认值:
这几个是第三人称里自带的,也可以自己新建:
- 将BP_MyCharacter拖入场景当中,在细节面板中将自动控制玩家改为玩家0,将世界设置中的GameMode设置为默认,运行:
虚幻C++创建Interface接口
- 在UE中添加Unreal Interface,命名为MyInterface:
- 在MyInterface.h文件中添加函数:
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "MyInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UMyInterface : public UInterface
{
GENERATED_BODY()
};
class UESTUDY_API IMyInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
// 必须声明为虚函数,这样在继承时,才可以将其进行重写、覆盖
virtual void Attack() {};
virtual void CalculateHealth() {};
};
- 在MyCharacter中使用接口:MyCharacter.cpp
#include "MyCharacter.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
// 调用接口函数
Attack();
CalculateHealth();
}
// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMyCharacter::Attack()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Attack"));
}
void AMyCharacter::CalculateHealth()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("CalculateHealth"));
}
- 在MyCharacter.h中
#pragma once
// 接口
#include "MyInterface.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
UCLASS()// ,public IMyInterface:继承自接口
class UESTUDY_API AMyCharacter : public ACharacter,public IMyInterface
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 重写两个接口函数
virtual void Attack()override;
virtual void CalculateHealth()override;
};
- 编译运行,打开UE,将BP_MyCharacter拖入场景中,运行
虚幻C++创建TimeHandle定时器
- 在MyCharacter.h文件中:
#pragma once
#include "CoreMinimal.h"
// 定时器头文件
#include "TimerManager.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
UCLASS()// ,public IMyInterface:继承自接口
class UESTUDY_API AMyCharacter : public ACharacter,public IMyInterface
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明一个定时器变量
FTimerHandle Time;
// 声明一个函数用来方便定时器打印内容
void PrintF();
};
- 在MyCharacter.cpp中:
#include "MyCharacter.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
// 设置定时器 参数:时间句柄,对象,绑定的函数,计时间隔,是否循环
GetWorld()->GetTimerManager().SetTimer(Time, this, &AMyCharacter::PrintF, 1.0, true);
// 清除定时器 加上此段以后,就没有输出了
if (Time.IsValid()) // 时间合法的话
{
GetWorld()->GetTimerManager().ClearTimer(Time);
}
}
// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 定时器相关
void AMyCharacter::PrintF()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Time"));
}
- 编译运行,打开UE,将BP_MyCharacter拖入场景中,运行
虚幻C++创建3DWidget并渲染到屏幕上
- 在UE中创建C++类UserWidget,命名为MyHealthWidget
- 在Content中, 创建一个控件蓝图,命名为UMG_Health
- 在UMG_Health中添加,canvas Pannel,并将填充屏幕改为自定义,将长款设为400和20
4. 再添加一个进度条,将锚点改为平铺,且偏移都设置为0:
5. 将类默认设置改为MyHealthWidget
6. 给进度条创建绑定,编译保存,复制其引用:/Script/UMGEditor.WidgetBlueprint’/Game/UMG_Health.UMG_Health’
7. 在MyCharacter.h文件中:
#pragma once
#include "CoreMinimal.h"
// WidgetCompontent头文件
#include "Components/WIdgetComponent.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
UCLASS()// ,public IMyInterface:继承自接口
class UESTUDY_API AMyCharacter : public ACharacter, public IMyInterface
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明一个Widget类型变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UWidgetComponent* MyWidgetHealth;
};
- 在MyCharacter.cpp文件中:
#include "MyCharacter.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 初始化控件
MyWidgetHealth = CreateDefaultSubobject<UWidgetComponent>(TEXT("MyWidgetComponent"));
// 将控件设置为默认根组件的组件
MyWidgetHealth->SetupAttachment(RootComponent);
static ConstructorHelpers::FClassFinder<UUserWidget>WidgetClass(TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/UMG_Health.UMG_Health_C'"));
MyWidgetHealth->SetWidgetClass(WidgetClass.Class);
MyWidgetHealth->SetRelativeLocation(FVector(0, 0, 100));
// 设置其渲染方式,渲染到屏幕上或渲染到世界当中
MyWidgetHealth->SetWidgetSpace(EWidgetSpace::Screen);
// 设置位置大小
MyWidgetHealth->SetDrawSize(FVector2D(400, 20));
}
- 编译运行,打开UE,查看BP_Character,
虚幻C++创建Apply Damage并且接受伤害TakeDamage
- 在MyActor.h中
#pragma once
#include "CoreMinimal.h"
// 场景组件头文件
#include "Components/SceneComponent.h"
// StaticMesh组件头文件
#include "Components/StaticMeshComponent.h"
// 碰撞组件头文件
#include "Components/BoxComponent.h"
// 粒子特效组件
#include "Particles/ParticleSystemComponent.h"
// 声音组件
#include "Components/AudioComponent.h"
// ApplyDamage所在的函数库头文件
#include "Kismet/GameplayStatics.h"
#include "MyCharacter.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class UESTUDY_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 声明变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class USceneComponent* MyScene;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UStaticMeshComponent* MyMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UParticleSystemComponent* MyParticle;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UBoxComponent* MyBox;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
class UAudioComponent* MyAudio;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "MyClass")
TSubclassOf<AActor> MyActor;
// 利用碰撞box绑定BeginOverlap 绑定的函数的声明 两个物体开始重叠时执行
UFUNCTION()
// 复制原函数的后6个参数,并删掉类型和参数名中间的逗号
void BeginOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
- 在MyActor.cpp中
#include "MyActor.h"
// Sets default values
// 构造函数
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 组件默认值
MyScene = CreateDefaultSubobject<USceneComponent>(TEXT("MyCustomScene"));
MyMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyCustomStaticMesh"));
MyParticle = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MyCustomParticle"));
MyBox = CreateDefaultSubobject<UBoxComponent>(TEXT("MyCustomBox"));
MyAudio = CreateDefaultSubobject<UAudioComponent>(TEXT("MyCustomAudio"));
// 设置父子级
RootComponent = MyScene;// 将MyMesh设置为根组件
MyMesh->SetupAttachment(MyScene);// 将MyMesh放到根组件下面
MyParticle->SetupAttachment(MyScene);
MyBox->SetupAttachment(MyScene);
MyAudio->SetupAttachment(MyBox);
// 静态加载资源
static ConstructorHelpers::FObjectFinder<UStaticMesh>TempStaticMesh(TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Shapes/Shape_Cone.Shape_Cone'"));
// 将临时静态模型设置为自己的模型 TempStaticMesh.Object获取对象资源的引用
MyMesh->SetStaticMesh(TempStaticMesh.Object);
static ConstructorHelpers::FObjectFinder<UParticleSystem>TempParticSystem(TEXT("/Script/Engine.ParticleSystem'/Game/StarterContent/Particles/P_Explosion.P_Explosion'"));
MyParticle->SetTemplate(TempParticSystem.Object);
static ConstructorHelpers::FObjectFinder<USoundWave>TempSound(TEXT("/Script/Engine.SoundWave'/Game/StarterContent/Audio/Collapse01.Collapse01'"));
MyAudio->SetSound(TempSound.Object);
// 静态加载类
static ConstructorHelpers::FClassFinder<AActor> TempMyActor(TEXT("/Script/Engine.Blueprint'/Game/StarterContent/Blueprints/Blueprint_CeilingLight.Blueprint_CeilingLight_C'"));
// 设置自己的类
MyActor = TempMyActor.Class;
// 碰撞设置 SetCollisionEnabled:设置碰撞输入
MyBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);// 此组件只有查询碰撞 只有事件检测和重叠
// 碰撞设置 SetCollisionObjectType:设置碰撞对象类型
MyBox->SetCollisionObjectType(ECC_WorldDynamic);// 世界的动态对象
// 碰撞设置 碰撞响应
MyBox->SetCollisionResponseToAllChannels(ECR_Overlap);// 将所有的通道碰撞响应设置为Overlap(重叠)
// 设置Box大小
MyBox->SetBoxExtent(FVector(64, 64, 64));
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 游戏开始时,粒子系统默认失效
if(MyParticle)
{
MyParticle->Deactivate();
}
// 动态加载资源
UStaticMesh* MyTempStaticMesh = LoadObject<UStaticMesh>(nullptr, TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube'"));
if (MyTempStaticMesh)
{
MyMesh->SetStaticMesh(MyTempStaticMesh);
}
// 动态加载类 LoadClass<AActor>:加载AActor模板类 this:在哪个类里
UClass* MyTempClass = LoadClass<AActor>(this, TEXT("/Script/Engine.Blueprint'/Game/StarterContent/Blueprints/Blueprint_WallSconce.Blueprint_WallSconce_C'"));
if (MyTempClass)
{
// SpawnActor出生,SpawnActor<AActor>:产生一个Actor类,FVector:类产生的位置 ZeroVector:(0,0,0)
// FRotator::ZeroRotator 类出生时的旋转 ZeroRotator:旋转为0 缩放默认值为(1,1,1)
AActor* SpawnActor = GetWorld()->SpawnActor<AActor>(MyTempClass, FVector::ZeroVector,FRotator::ZeroRotator);
}
// 利用碰撞box绑定BeginOverlap 参数:对象,绑定的函数
MyBox->OnComponentBeginOverlap.AddDynamic(this, &AMyActor::BeginOverlapFunction);
}
// Called every frame
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 利用碰撞box绑定BeginOverlap 绑定的函数的实现
void AMyActor::BeginOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
// 当other actor碰撞到角色时,角色受到伤害
AMyCharacter* MyCharacter = Cast<AMyCharacter>(OtherActor);
if (MyCharacter)// 判断MyCharacter是否合法
{
// 碰到的时第三人称的时候,才受到伤害
// ApplyDamage( 受伤害的对象,伤害值,控制器,this指针,伤害类型)
UGameplayStatics::ApplyDamage(MyCharacter, 5.0f, nullptr, this, UDamageType::StaticClass());
}
// 碰撞时粒子生效
MyParticle->Activate();
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("BeginOverLap is seccess"));
}
- 在MyCharacter.h中
#pragma once
#include "CoreMinimal.h"
// 输入映射 value值
#include "InputActionValue.h"
//增强输入头文件
#include "EnhancedInputComponent.h"
// 增强输入子系统
#include "EnhancedInputSubsystems.h"
// 控制器头文件
#include "GameFramework/Controller.h"
// 摄像机摇臂头文件
#include "GameFramework/SpringArmComponent.h"
// 相机头文件
#include "Camera/CameraComponent.h"
// 运动组件头文件
#include "GameFramework/CharacterMovementComponent.h"
// 接口
#include "MyInterface.h"
// 定时器头文件
#include "TimerManager.h"
// WidgetCompontent头文件
#include "Components/WidgetComponent.h"
#include "MyHealthWidget.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
UCLASS()// ,public IMyInterface:继承自接口
class UESTUDY_API AMyCharacter : public ACharacter, public IMyInterface
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明摄像头摇臂变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
USpringArmComponent* MySpringArm;
// 声明相机变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UCameraComponent* MyCamera;
// 声明输入映射相关变量
// 输入映射上下文
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputMappingContext* DefaultMappingContext;
// 移动映射输入变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* MoveAction;
// 旋转映射变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* LookAction;
// 鼠标按下移动时的代理绑定函数
// FInputActionValue:鼠标按下和键盘按下时会有值输入
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
// 重写两个接口函数
virtual void Attack()override;
virtual void CalculateHealth()override;
// 声明一个定时器变量
FTimerHandle Time;
// 声明一个函数用来方便定时器打印内容
void PrintF();
// 声明一个Widget类型变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UWidgetComponent* MyWidgetHealth;
// 重写受到伤害的函数
// 参数:接收到的伤害值,
virtual float TakeDamage(float DamageAmount,struct FDamageEvent const &DamageEvent,class AController* EnvenInstigator,AActor* DamageCauser)override;
};
- 在MyCharacter.cpp中
#include "MyCharacter.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 初始化
MySpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("MySpringArmComponent"));
MyCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("MyCameraComponent"));
// 初始化控件
MyWidgetHealth = CreateDefaultSubobject<UWidgetComponent>(TEXT("MyWidgetComponent"));
// 将控件设置为默认根组件的组件
MyWidgetHealth->SetupAttachment(RootComponent);
static ConstructorHelpers::FClassFinder<UUserWidget>WidgetClass(TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/UMG_Health.UMG_Health_C'"));
MyWidgetHealth->SetWidgetClass(WidgetClass.Class);
MyWidgetHealth->SetRelativeLocation(FVector(0, 0, 100));
// 设置其渲染方式,渲染到屏幕上或渲染到世界当中
MyWidgetHealth->SetWidgetSpace(EWidgetSpace::Screen);
// 设置位置大小
MyWidgetHealth->SetDrawSize(FVector2D(400, 20));
// 设置摄像机摇臂长度
MySpringArm->TargetArmLength = 400.0f;
// 设置根组件 将相机设置为摄像机摇臂的组件
MyCamera->SetupAttachment(MySpringArm);
// 将摄像机摇臂设置为默认根组件的组件
MySpringArm->SetupAttachment(RootComponent);
// 让控制器的旋转不影响角色的转动,只影响摄像机的转动
bUseControllerRotationPitch = false;// 设置Pitch旋转
bUseControllerRotationRoll = false;// 设置Roll旋转
bUseControllerRotationYaw = false;// 设置Yaw旋转
// 让角色面朝加速度的方向
GetCharacterMovement()->bOrientRotationToMovement = true;
// 使用Pawn控制器的旋转
MySpringArm->bUsePawnControlRotation = true;
}
// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayController = Cast<APlayerController>(Controller))// 判断控制器是否合法
{
// 判断玩家子系统 获取增强输入的本地玩家子系统 将本地控制器转化为增强输入本地玩家子系统
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayController->GetLocalPlayer()))
{
// 转化成功 添加映射输入上下文 0为优先级,数值越大,优先级越高
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
// 调用接口函数
Attack();
CalculateHealth();
// 设置定时器 参数:时间句柄,对象,绑定的函数,计时间隔,是否循环
GetWorld()->GetTimerManager().SetTimer(Time, this, &AMyCharacter::PrintF, 1.0, true);
// 清除定时器 加上此段以后,就没有输出了
if (Time.IsValid()) // 时间合法的话
{
GetWorld()->GetTimerManager().ClearTimer(Time);
}
}
// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
// 输入映射
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// 将玩家输入映射,转化为增强输入玩家映射 判断是否有输入映射
// CastChecked<UEnhancedInputComponent>(PlayerInputComponent):将PlayerInputComponent转化为UEnhancedInputComponent
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
// 绑定映射 MoveAction:映射变量,ETriggerEvent::Trigger:方式,this:对象,&AMyCharacter::Move绑定的函数
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::Look);
}
}
// 移动
void AMyCharacter::Move(const FInputActionValue& Value)
{
// 用2D变量来控制移动 Value.Get<FVector2D>():获得输入的2D向量坐标
FVector2D MovementVector = Value.Get<FVector2D>();
// 判断控制器是否合法
if (Controller != nullptr)
{
// 通过变量的旋转获得他的前向向量和右向向量,来控制前后左右移动
// Controller->GetControlRotation():获得控制器的旋转
const FRotator Rotation = Controller->GetControlRotation();
// 获取Yaw方向上的旋转
const FRotator YawRotation(0, Rotation.Yaw, 0);
// 获得前向向量(X轴方向)
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// 获得右向向量(Y轴方向)
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// 在键盘按下WASD时,添加输入
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
// 旋转
void AMyCharacter::Look(const FInputActionValue& Value)
{
// 获得鼠标X和Y轴的向量
FVector2D LookAxisVector = Value.Get<FVector2D>();
// 判断控制器是否合法,即是否有输入
if (Controller != nullptr)
{
// 获得X轴方向的向量值
AddControllerYawInput(LookAxisVector.X);
// 获得Y轴方向的向量值
AddControllerPitchInput(LookAxisVector.Y);
}
}
// 接口相关
void AMyCharacter::Attack()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Attack"));
}
void AMyCharacter::CalculateHealth()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("CalculateHealth"));
}
// 定时器相关
void AMyCharacter::PrintF()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Time"));
}
// 受伤减血
float AMyCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EnvenInstigator, AActor* DamageCauser)
{
UMyHealthWidget* MyWidget = Cast<UMyHealthWidget>(MyWidgetHealth->GetUserWidgetObject());
// 判断MyWidget是否合法
if (MyWidget)
{
// 判断血量值
if (MyWidget->CurrentHealth <= 0)
{
// 游戏结束
return 0;
}
MyWidget->CurrentHealth -= 5.0f;
}
return 0.0f;
}
- 编译运行,打开UE,将Pawn的自动控制玩家设置为玩家0,运行,移动玩家碰撞Actor,每碰撞一次,血条减少5:
虚幻C++创建Timeline时间轴
- 打开UE,创建C++类Actor,命名为MyTimelineActor
- 在MyTimelineActor.h中:
#pragma once
#include "CoreMinimal.h"
// Timeline头文件
#include "Components/TimelineComponent.h"
#include "GameFramework/Actor.h"
#include "MyTimelineActor.generated.h"
UCLASS()
class UESTUDY_API AMyTimelineActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyTimelineActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 用于设置浮点曲线的变量
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "myCurve")
UCurveFloat* MyCurveFloat;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "mySceneComponent")
UTimelineComponent* MyTimeline;
// 声明两个代理,用于timeline时绑定
// 时间轴一开始时进行的绑定
FOnTimelineFloat TimelineDelegate;
// 时间轴完成时进行的绑定
FOnTimelineEvent TimelineFinishedDelegate;
// 用于timeline代理绑定的函数
// 时间轴开始时绑定
UFUNCTION()
void TimelineStart(float value);
// 时间轴结束时绑定
UFUNCTION()
void TimelinFinished();
};
- 在MyTimelineActor.cpp中
#include "MyTimelineActor.h"
// Sets default values
AMyTimelineActor::AMyTimelineActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 初始化
MyTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("MyTimeline"));
}
// Called when the game starts or when spawned
void AMyTimelineActor::BeginPlay()
{
Super::BeginPlay();
// 代理绑定逻辑
TimelineDelegate.BindUFunction(this, TEXT("TimelineStart"));
TimelineFinishedDelegate.BindUFunction(this, TEXT("TimelinFinished"));
// 设置浮点曲线
MyTimeline->AddInterpFloat(MyCurveFloat, TimelineDelegate);
// 设置是否循环
MyTimeline->SetLooping(false);
// 设置播放
MyTimeline->PlayFromStart();// 从一开始就播放
MyTimeline->Play();// 从当前位置播放
// 设置完成时绑定的代理
MyTimeline->SetTimelineFinishedFunc(TimelineFinishedDelegate);
}
// Called every frame
void AMyTimelineActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 时间轴开始时绑定
void AMyTimelineActor::TimelineStart(float value)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("TimelinePlay"));
}
// 时间轴结束时绑定
void AMyTimelineActor::TimelinFinished()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("TimelineFinished"));
}
- 编译运行,打开UE,在UE中创建浮点曲线,命名为MyFloatCurve,双击打开,添加两个关键帧,一个数值为(0,0),一个数值为(1,1)
- 创建基于MyTimelineActor的蓝图类,命名为BP_MyTimelineActor,双击打开,指定其浮点曲线为刚创建的MyFloatCurve,编译保存
- 把BP_MyTimelineActor拖动到场景中,点击运行
用TimeLine实现开关门
- 在MyTimelineActor.h中:
#pragma once
#include "CoreMinimal.h"
// Timeline头文件
#include "Components/TimelineComponent.h"
// 碰撞头文件
#include "Components/BoxComponent.h"
#include "MyCharacter.h"
#include "GameFramework/Actor.h"
#include "MyTimelineActor.generated.h"
UCLASS()
class UESTUDY_API AMyTimelineActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyTimelineActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 用于设置浮点曲线的变量
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "myCurve")
UCurveFloat* MyCurveFloat;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "mySceneComponent")
UTimelineComponent* MyTimeline;
// 声明两个代理,用于timeline时绑定
// 时间轴一开始时进行的绑定
FOnTimelineFloat TimelineDelegate;
// 时间轴完成时进行的绑定
FOnTimelineEvent TimelineFinishedDelegate;
// 用于timeline代理绑定的函数
// 时间轴开始时绑定
UFUNCTION()
void TimelineStart(float value);
// 时间轴结束时绑定
UFUNCTION()
void TimelinFinished();
// 用Timeline实现开关门
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "mySceneComponent")
USceneComponent* MyScene;
// 声明一个模型变量
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "mySceneComponent")
UStaticMeshComponent* MyStaticMesh;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MySceneComponent")
UBoxComponent* MyBox;
// 利用碰撞box绑定BeginOverlap 绑定的函数的声明 两个物体开始重叠时执行
UFUNCTION()
// 复制原函数的后6个参数,并删掉类型和参数名中间的逗号
void BeginOverlapFunction(UPrimitiveComponent * OverlappedComponent, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);
// 利用碰撞box绑定EndOverlap 绑定的函数的声明 两个物体结束重叠时执行
UFUNCTION()
// 复制原函数的4个参数,并删掉类型和参数名中间的逗号
void EndOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
};
- 在MyTimelineActor.cpp中
#include "MyTimelineActor.h"
// Sets default values
AMyTimelineActor::AMyTimelineActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 初始化
MyTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("MyTimeline"));
MyScene = CreateDefaultSubobject<USceneComponent>(TEXT("MySceneComponent"));
MyStaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyStaticMeshComponent"));
MyBox = CreateDefaultSubobject<UBoxComponent>(TEXT("MyBoxComponent"));
// 加载模型
static ConstructorHelpers::FObjectFinder<UStaticMesh>TempStaticMesh(TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Architecture/Wall_400x400.Wall_400x400'"));
if (TempStaticMesh.Succeeded())// 成功加载模型
{
MyStaticMesh->SetStaticMesh(TempStaticMesh.Object);
}
// 设置根组件
RootComponent = MyScene;
MyStaticMesh->SetupAttachment(MyScene);
MyBox->SetupAttachment(MyScene);
// 设置大小
MyBox->SetBoxExtent(FVector(200, 100, 100));
// 设置相对位置
MyBox->SetRelativeLocation(FVector(200, 0, 0));
}
// Called when the game starts or when spawned
void AMyTimelineActor::BeginPlay()
{
Super::BeginPlay();
// 代理绑定逻辑
TimelineDelegate.BindUFunction(this, TEXT("TimelineStart"));
TimelineFinishedDelegate.BindUFunction(this, TEXT("TimelinFinished"));
// 设置浮点曲线
MyTimeline->AddInterpFloat(MyCurveFloat, TimelineDelegate);
// 设置是否循环
//MyTimeline->SetLooping(false);
设置播放
//MyTimeline->PlayFromStart();// 从一开始就播放
//MyTimeline->Play();// 从当前位置播放
// 设置完成时绑定的代理
MyTimeline->SetTimelineFinishedFunc(TimelineFinishedDelegate);
// 利用碰撞box绑定BeginOverlap 参数:对象,绑定的函数
MyBox->OnComponentBeginOverlap.AddDynamic(this, &AMyTimelineActor::BeginOverlapFunction);
// 利用碰撞box绑定EndOverlap 参数:对象,绑定的函数
MyBox->OnComponentEndOverlap.AddDynamic(this, &AMyTimelineActor::EndOverlapFunction);
}
// Called every frame
void AMyTimelineActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 时间轴开始时绑定
void AMyTimelineActor::TimelineStart(float value)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("TimelinePlay"));
// 在Z轴上缓慢旋转90°
float YawRotation = FMath::Lerp(0, 90, value);
MyStaticMesh->SetRelativeRotation(FRotator(0, YawRotation, 0));
}
// 时间轴结束时绑定
void AMyTimelineActor::TimelinFinished()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("TimelineFinished"));
}
void AMyTimelineActor::BeginOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
// 判断是否是这个角色碰到了碰撞体
AMyCharacter* TempCharacter = Cast<AMyCharacter>(OtherActor);
if (TempCharacter)
{
MyTimeline->PlayFromStart();
}
}
void AMyTimelineActor::EndOverlapFunction(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
// 判断是否是这个角色碰到了碰撞体
AMyCharacter* TempCharacter = Cast<AMyCharacter>(OtherActor);
if (TempCharacter)
{
MyTimeline->ReverseFromEnd();// 从后往前播放
}
}
- 编译运行,将BP_MyTimeLineActor和人物拖入场景中,将Pawn的自动控制玩家设置为玩家0,运行,移动玩家靠近门,玩家靠近门打开,离开门则关闭
虚幻C++射线检测LinetranceByChannel和LineTanceByObject
根据通道进行检测
- 在MyCharacter.cpp中:
#include "MyCharacter.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 初始化
MySpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("MySpringArmComponent"));
MyCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("MyCameraComponent"));
// 初始化控件
MyWidgetHealth = CreateDefaultSubobject<UWidgetComponent>(TEXT("MyWidgetComponent"));
// 将控件设置为默认根组件的组件
MyWidgetHealth->SetupAttachment(RootComponent);
static ConstructorHelpers::FClassFinder<UUserWidget>WidgetClass(TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/UMG_Health.UMG_Health_C'"));
MyWidgetHealth->SetWidgetClass(WidgetClass.Class);
MyWidgetHealth->SetRelativeLocation(FVector(0, 0, 100));
// 设置其渲染方式,渲染到屏幕上或渲染到世界当中
MyWidgetHealth->SetWidgetSpace(EWidgetSpace::Screen);
// 设置位置大小
MyWidgetHealth->SetDrawSize(FVector2D(400, 20));
// 设置摄像机摇臂长度
MySpringArm->TargetArmLength = 400.0f;
// 设置根组件 将相机设置为摄像机摇臂的组件
MyCamera->SetupAttachment(MySpringArm);
// 将摄像机摇臂设置为默认根组件的组件
MySpringArm->SetupAttachment(RootComponent);
// 让控制器的旋转不影响角色的转动,只影响摄像机的转动
bUseControllerRotationPitch = false;// 设置Pitch旋转
bUseControllerRotationRoll = false;// 设置Roll旋转
bUseControllerRotationYaw = false;// 设置Yaw旋转
// 让角色面朝加速度的方向
GetCharacterMovement()->bOrientRotationToMovement = true;
// 使用Pawn控制器的旋转
MySpringArm->bUsePawnControlRotation = true;
}
// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayController = Cast<APlayerController>(Controller))// 判断控制器是否合法
{
// 判断玩家子系统 获取增强输入的本地玩家子系统 将本地控制器转化为增强输入本地玩家子系统
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayController->GetLocalPlayer()))
{
// 转化成功 添加映射输入上下文 0为优先级,数值越大,优先级越高
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
// 调用接口函数
Attack();
CalculateHealth();
// 设置定时器 参数:时间句柄,对象,绑定的函数,计时间隔,是否循环
GetWorld()->GetTimerManager().SetTimer(Time, this, &AMyCharacter::PrintF, 1.0, true);
// 清除定时器 加上此段以后,就没有输出了
if (Time.IsValid()) // 时间合法的话
{
GetWorld()->GetTimerManager().ClearTimer(Time);
}
}
// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 执行射线检测
StartLocation = MyCamera->GetComponentLocation();
ForwardVector = MyCamera->GetForwardVector();
EndLocation = StartLocation + ForwardVector * 9999;
// 用bool值接收射线检测的返回值 ECC_Visibility:碰撞类型
// 根据通道查询检测
bool bHit = GetWorld()->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECC_Visibility);
if (bHit)//判断是否检测到了东西
{
AActor* HitActor = HitResult.GetActor();
// 击中这个点的位置
FVector ImpactPoint = HitResult.ImpactPoint;
// 击中的位置
FVector HitLocation = HitResult.Location;
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("%s"),*HitActor->GetName()));
}
}
// Called to bind functionality to input
// 输入映射
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// 将玩家输入映射,转化为增强输入玩家映射 判断是否有输入映射
// CastChecked<UEnhancedInputComponent>(PlayerInputComponent):将PlayerInputComponent转化为UEnhancedInputComponent
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
// 绑定映射 MoveAction:映射变量,ETriggerEvent::Trigger:方式,this:对象,&AMyCharacter::Move绑定的函数
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::Look);
}
}
// 移动
void AMyCharacter::Move(const FInputActionValue& Value)
{
// 用2D变量来控制移动 Value.Get<FVector2D>():获得输入的2D向量坐标
FVector2D MovementVector = Value.Get<FVector2D>();
// 判断控制器是否合法
if (Controller != nullptr)
{
// 通过变量的旋转获得他的前向向量和右向向量,来控制前后左右移动
// Controller->GetControlRotation():获得控制器的旋转
const FRotator Rotation = Controller->GetControlRotation();
// 获取Yaw方向上的旋转
const FRotator YawRotation(0, Rotation.Yaw, 0);
// 获得前向向量(X轴方向)
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// 获得右向向量(Y轴方向)
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// 在键盘按下WASD时,添加输入
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
// 旋转
void AMyCharacter::Look(const FInputActionValue& Value)
{
// 获得鼠标X和Y轴的向量
FVector2D LookAxisVector = Value.Get<FVector2D>();
// 判断控制器是否合法,即是否有输入
if (Controller != nullptr)
{
// 获得X轴方向的向量值
AddControllerYawInput(LookAxisVector.X);
// 获得Y轴方向的向量值
AddControllerPitchInput(LookAxisVector.Y);
}
}
// 接口相关
void AMyCharacter::Attack()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Attack"));
}
void AMyCharacter::CalculateHealth()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("CalculateHealth"));
}
// 定时器相关
void AMyCharacter::PrintF()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Time"));
}
float AMyCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EnvenInstigator, AActor* DamageCauser)
{
UMyHealthWidget* MyWidget = Cast<UMyHealthWidget>(MyWidgetHealth->GetUserWidgetObject());
// 判断MyWidget是否合法
if (MyWidget)
{
// 判断血量值
if (MyWidget->CurrentHealth <= 0)
{
// 游戏结束
return 0;
}
MyWidget->CurrentHealth -= 5.0f;
}
return 0.0f;
}
- 在MyCharacter.h中:
#pragma once
#include "CoreMinimal.h"
// 输入映射 value值
#include "InputActionValue.h"
//增强输入头文件
#include "EnhancedInputComponent.h"
// 增强输入子系统
#include "EnhancedInputSubsystems.h"
// 控制器头文件
#include "GameFramework/Controller.h"
// 摄像机摇臂头文件
#include "GameFramework/SpringArmComponent.h"
// 相机头文件
#include "Camera/CameraComponent.h"
// 运动组件头文件
#include "GameFramework/CharacterMovementComponent.h"
// 接口
#include "MyInterface.h"
// 定时器头文件
#include "TimerManager.h"
// WidgetCompontent头文件
#include "Components/WidgetComponent.h"
#include "MyHealthWidget.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
UCLASS()// ,public IMyInterface:继承自接口
class UESTUDY_API AMyCharacter : public ACharacter, public IMyInterface
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明摄像头摇臂变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
USpringArmComponent* MySpringArm;
// 声明相机变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UCameraComponent* MyCamera;
// 声明输入映射相关变量
// 输入映射上下文
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputMappingContext* DefaultMappingContext;
// 移动映射输入变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* MoveAction;
// 旋转映射变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* LookAction;
// 鼠标按下移动时的代理绑定函数
// FInputActionValue:鼠标按下和键盘按下时会有值输入
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
// 重写两个接口函数
virtual void Attack()override;
virtual void CalculateHealth()override;
// 声明一个定时器变量
FTimerHandle Time;
// 声明一个函数用来方便定时器打印内容
void PrintF();
// 声明一个Widget类型变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UWidgetComponent* MyWidgetHealth;
// 重写受到伤害的函数
// 参数:接收到的伤害值,
virtual float TakeDamage(float DamageAmount,struct FDamageEvent const &DamageEvent,class AController* EnvenInstigator,AActor* DamageCauser)override;
// 射线检测起始位置
FVector StartLocation;
// 射线检测向前向量
FVector ForwardVector;
// 射线检测的最终位置
FVector EndLocation;
// 结构体,射线检测击中物体后返回的一些数据
FHitResult HitResult;
};
- 编译运行,打开UE,在Content里新建一个蓝图类Actor,命名为BP_HitActor,将Pawn的自动控制玩家设置为玩家0,将BP_HitActor和人物都拖拽到场景中,运行,移动人物,靠近P_HitActor,发现P_HitActor和地面都能检测出来
根据对象查询检测
- 在MyCharacter.cpp中:
#include "MyCharacter.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 初始化
MySpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("MySpringArmComponent"));
MyCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("MyCameraComponent"));
// 初始化控件
MyWidgetHealth = CreateDefaultSubobject<UWidgetComponent>(TEXT("MyWidgetComponent"));
// 将控件设置为默认根组件的组件
MyWidgetHealth->SetupAttachment(RootComponent);
static ConstructorHelpers::FClassFinder<UUserWidget>WidgetClass(TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/UMG_Health.UMG_Health_C'"));
MyWidgetHealth->SetWidgetClass(WidgetClass.Class);
MyWidgetHealth->SetRelativeLocation(FVector(0, 0, 100));
// 设置其渲染方式,渲染到屏幕上或渲染到世界当中
MyWidgetHealth->SetWidgetSpace(EWidgetSpace::Screen);
// 设置位置大小
MyWidgetHealth->SetDrawSize(FVector2D(400, 20));
// 设置摄像机摇臂长度
MySpringArm->TargetArmLength = 400.0f;
// 设置根组件 将相机设置为摄像机摇臂的组件
MyCamera->SetupAttachment(MySpringArm);
// 将摄像机摇臂设置为默认根组件的组件
MySpringArm->SetupAttachment(RootComponent);
// 让控制器的旋转不影响角色的转动,只影响摄像机的转动
bUseControllerRotationPitch = false;// 设置Pitch旋转
bUseControllerRotationRoll = false;// 设置Roll旋转
bUseControllerRotationYaw = false;// 设置Yaw旋转
// 让角色面朝加速度的方向
GetCharacterMovement()->bOrientRotationToMovement = true;
// 使用Pawn控制器的旋转
MySpringArm->bUsePawnControlRotation = true;
}
// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayController = Cast<APlayerController>(Controller))// 判断控制器是否合法
{
// 判断玩家子系统 获取增强输入的本地玩家子系统 将本地控制器转化为增强输入本地玩家子系统
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayController->GetLocalPlayer()))
{
// 转化成功 添加映射输入上下文 0为优先级,数值越大,优先级越高
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
// 调用接口函数
Attack();
CalculateHealth();
// 设置定时器 参数:时间句柄,对象,绑定的函数,计时间隔,是否循环
GetWorld()->GetTimerManager().SetTimer(Time, this, &AMyCharacter::PrintF, 1.0, true);
// 清除定时器 加上此段以后,就没有输出了
if (Time.IsValid()) // 时间合法的话
{
GetWorld()->GetTimerManager().ClearTimer(Time);
}
}
// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 执行射线检测
StartLocation = MyCamera->GetComponentLocation();
ForwardVector = MyCamera->GetForwardVector();
EndLocation = StartLocation + ForwardVector * 9999;
// 根据对象查询检测 参数:检测到后返回的结果,射线检测的起始点,射线检测的终止点,通道名称
FCollisionObjectQueryParams objectType;// 用于添加通道
objectType.AddObjectTypesToQuery(ECC_WorldDynamic);// 添加通道ECC_WorldDynamic,可继续添加多个
bool bHit2 = GetWorld()->LineTraceSingleByObjectType(HitResult, StartLocation, EndLocation, objectType);
if (bHit2)//判断是否检测到了东西
{
AActor* HitActor2 = HitResult.GetActor();
// 击中这个点的位置
FVector ImpactPoint2 = HitResult.ImpactPoint;
// 击中的位置
FVector HitLocation2 = HitResult.Location;
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("%s"), *HitActor2->GetName()));
}
}
// Called to bind functionality to input
// 输入映射
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// 将玩家输入映射,转化为增强输入玩家映射 判断是否有输入映射
// CastChecked<UEnhancedInputComponent>(PlayerInputComponent):将PlayerInputComponent转化为UEnhancedInputComponent
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
// 绑定映射 MoveAction:映射变量,ETriggerEvent::Trigger:方式,this:对象,&AMyCharacter::Move绑定的函数
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::Look);
}
}
// 移动
void AMyCharacter::Move(const FInputActionValue& Value)
{
// 用2D变量来控制移动 Value.Get<FVector2D>():获得输入的2D向量坐标
FVector2D MovementVector = Value.Get<FVector2D>();
// 判断控制器是否合法
if (Controller != nullptr)
{
// 通过变量的旋转获得他的前向向量和右向向量,来控制前后左右移动
// Controller->GetControlRotation():获得控制器的旋转
const FRotator Rotation = Controller->GetControlRotation();
// 获取Yaw方向上的旋转
const FRotator YawRotation(0, Rotation.Yaw, 0);
// 获得前向向量(X轴方向)
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// 获得右向向量(Y轴方向)
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// 在键盘按下WASD时,添加输入
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
// 旋转
void AMyCharacter::Look(const FInputActionValue& Value)
{
// 获得鼠标X和Y轴的向量
FVector2D LookAxisVector = Value.Get<FVector2D>();
// 判断控制器是否合法,即是否有输入
if (Controller != nullptr)
{
// 获得X轴方向的向量值
AddControllerYawInput(LookAxisVector.X);
// 获得Y轴方向的向量值
AddControllerPitchInput(LookAxisVector.Y);
}
}
// 接口相关
void AMyCharacter::Attack()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Attack"));
}
void AMyCharacter::CalculateHealth()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("CalculateHealth"));
}
// 定时器相关
void AMyCharacter::PrintF()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Time"));
}
float AMyCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EnvenInstigator, AActor* DamageCauser)
{
UMyHealthWidget* MyWidget = Cast<UMyHealthWidget>(MyWidgetHealth->GetUserWidgetObject());
// 判断MyWidget是否合法
if (MyWidget)
{
// 判断血量值
if (MyWidget->CurrentHealth <= 0)
{
// 游戏结束
return 0;
}
MyWidget->CurrentHealth -= 5.0f;
}
return 0.0f;
}
- 在MyCharacter.h中:
#pragma once
#include "CoreMinimal.h"
// 输入映射 value值
#include "InputActionValue.h"
//增强输入头文件
#include "EnhancedInputComponent.h"
// 增强输入子系统
#include "EnhancedInputSubsystems.h"
// 控制器头文件
#include "GameFramework/Controller.h"
// 摄像机摇臂头文件
#include "GameFramework/SpringArmComponent.h"
// 相机头文件
#include "Camera/CameraComponent.h"
// 运动组件头文件
#include "GameFramework/CharacterMovementComponent.h"
// 接口
#include "MyInterface.h"
// 定时器头文件
#include "TimerManager.h"
// WidgetCompontent头文件
#include "Components/WidgetComponent.h"
#include "MyHealthWidget.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
UCLASS()// ,public IMyInterface:继承自接口
class UESTUDY_API AMyCharacter : public ACharacter, public IMyInterface
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明摄像头摇臂变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
USpringArmComponent* MySpringArm;
// 声明相机变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UCameraComponent* MyCamera;
// 声明输入映射相关变量
// 输入映射上下文
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputMappingContext* DefaultMappingContext;
// 移动映射输入变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* MoveAction;
// 旋转映射变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* LookAction;
// 鼠标按下移动时的代理绑定函数
// FInputActionValue:鼠标按下和键盘按下时会有值输入
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
// 重写两个接口函数
virtual void Attack()override;
virtual void CalculateHealth()override;
// 声明一个定时器变量
FTimerHandle Time;
// 声明一个函数用来方便定时器打印内容
void PrintF();
// 声明一个Widget类型变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UWidgetComponent* MyWidgetHealth;
// 重写受到伤害的函数
// 参数:接收到的伤害值,
virtual float TakeDamage(float DamageAmount,struct FDamageEvent const &DamageEvent,class AController* EnvenInstigator,AActor* DamageCauser)override;
// 射线检测起始位置
FVector StartLocation;
// 射线检测向前向量
FVector ForwardVector;
// 射线检测的最终位置
FVector EndLocation;
// 结构体,射线检测击中物体后返回的一些数据
FHitResult HitResult;
};
- 编译运行,打开UE,在Content里新建一个蓝图类Actor,命名为BP_HitActor2,在其中加一个Cube,类型为WorldDynamic,将Pawn的自动控制玩家设置为玩家0,将BP_HitActor2和人物都拖拽到场景中,运行,移动人物,靠近BP_HitActor2,只能检测到指定的对象类型
多射线通道检测
- 在MyCharacter.cpp中:
#include "MyCharacter.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 初始化
MySpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("MySpringArmComponent"));
MyCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("MyCameraComponent"));
// 初始化控件
MyWidgetHealth = CreateDefaultSubobject<UWidgetComponent>(TEXT("MyWidgetComponent"));
// 将控件设置为默认根组件的组件
MyWidgetHealth->SetupAttachment(RootComponent);
static ConstructorHelpers::FClassFinder<UUserWidget>WidgetClass(TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/UMG_Health.UMG_Health_C'"));
MyWidgetHealth->SetWidgetClass(WidgetClass.Class);
MyWidgetHealth->SetRelativeLocation(FVector(0, 0, 100));
// 设置其渲染方式,渲染到屏幕上或渲染到世界当中
MyWidgetHealth->SetWidgetSpace(EWidgetSpace::Screen);
// 设置位置大小
MyWidgetHealth->SetDrawSize(FVector2D(400, 20));
// 设置摄像机摇臂长度
MySpringArm->TargetArmLength = 400.0f;
// 设置根组件 将相机设置为摄像机摇臂的组件
MyCamera->SetupAttachment(MySpringArm);
// 将摄像机摇臂设置为默认根组件的组件
MySpringArm->SetupAttachment(RootComponent);
// 让控制器的旋转不影响角色的转动,只影响摄像机的转动
bUseControllerRotationPitch = false;// 设置Pitch旋转
bUseControllerRotationRoll = false;// 设置Roll旋转
bUseControllerRotationYaw = false;// 设置Yaw旋转
// 让角色面朝加速度的方向
GetCharacterMovement()->bOrientRotationToMovement = true;
// 使用Pawn控制器的旋转
MySpringArm->bUsePawnControlRotation = true;
}
// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayController = Cast<APlayerController>(Controller))// 判断控制器是否合法
{
// 判断玩家子系统 获取增强输入的本地玩家子系统 将本地控制器转化为增强输入本地玩家子系统
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayController->GetLocalPlayer()))
{
// 转化成功 添加映射输入上下文 0为优先级,数值越大,优先级越高
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
// 调用接口函数
Attack();
CalculateHealth();
// 设置定时器 参数:时间句柄,对象,绑定的函数,计时间隔,是否循环
GetWorld()->GetTimerManager().SetTimer(Time, this, &AMyCharacter::PrintF, 1.0, true);
// 清除定时器 加上此段以后,就没有输出了
if (Time.IsValid()) // 时间合法的话
{
GetWorld()->GetTimerManager().ClearTimer(Time);
}
}
// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 执行射线检测
StartLocation = MyCamera->GetComponentLocation();
ForwardVector = MyCamera->GetForwardVector();
EndLocation = StartLocation + ForwardVector * 9999;
// 多射线通道检测 参数:返回的结果,射线检测的起始点,射线检测的终止点,通道检测的对象
bool HitMulti = GetWorld()->LineTraceMultiByChannel(HitResults, StartLocation, EndLocation, ECC_Visibility);
if (HitMulti)//判断是否检测到了东西
{
for (int32 i = 0; i < HitResults.Num(); i++)
{
AActor* HitMultiActor = HitResults[i].GetActor();
FVector HitLocation = HitResults[i].Location;
FVector HitImpactPoint2 = HitResults[i].ImpactPoint;
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("%s"), *HitMultiActor->GetName()));
}
}
}
// Called to bind functionality to input
// 输入映射
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// 将玩家输入映射,转化为增强输入玩家映射 判断是否有输入映射
// CastChecked<UEnhancedInputComponent>(PlayerInputComponent):将PlayerInputComponent转化为UEnhancedInputComponent
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
// 绑定映射 MoveAction:映射变量,ETriggerEvent::Trigger:方式,this:对象,&AMyCharacter::Move绑定的函数
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::Look);
}
}
// 移动
void AMyCharacter::Move(const FInputActionValue& Value)
{
// 用2D变量来控制移动 Value.Get<FVector2D>():获得输入的2D向量坐标
FVector2D MovementVector = Value.Get<FVector2D>();
// 判断控制器是否合法
if (Controller != nullptr)
{
// 通过变量的旋转获得他的前向向量和右向向量,来控制前后左右移动
// Controller->GetControlRotation():获得控制器的旋转
const FRotator Rotation = Controller->GetControlRotation();
// 获取Yaw方向上的旋转
const FRotator YawRotation(0, Rotation.Yaw, 0);
// 获得前向向量(X轴方向)
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// 获得右向向量(Y轴方向)
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// 在键盘按下WASD时,添加输入
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
// 旋转
void AMyCharacter::Look(const FInputActionValue& Value)
{
// 获得鼠标X和Y轴的向量
FVector2D LookAxisVector = Value.Get<FVector2D>();
// 判断控制器是否合法,即是否有输入
if (Controller != nullptr)
{
// 获得X轴方向的向量值
AddControllerYawInput(LookAxisVector.X);
// 获得Y轴方向的向量值
AddControllerPitchInput(LookAxisVector.Y);
}
}
// 接口相关
void AMyCharacter::Attack()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Attack"));
}
void AMyCharacter::CalculateHealth()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("CalculateHealth"));
}
// 定时器相关
void AMyCharacter::PrintF()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Time"));
}
float AMyCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EnvenInstigator, AActor* DamageCauser)
{
UMyHealthWidget* MyWidget = Cast<UMyHealthWidget>(MyWidgetHealth->GetUserWidgetObject());
// 判断MyWidget是否合法
if (MyWidget)
{
// 判断血量值
if (MyWidget->CurrentHealth <= 0)
{
// 游戏结束
return 0;
}
MyWidget->CurrentHealth -= 5.0f;
}
return 0.0f;
}
- 在MyCharacter.h中:
#pragma once
#include "CoreMinimal.h"
// 输入映射 value值
#include "InputActionValue.h"
//增强输入头文件
#include "EnhancedInputComponent.h"
// 增强输入子系统
#include "EnhancedInputSubsystems.h"
// 控制器头文件
#include "GameFramework/Controller.h"
// 摄像机摇臂头文件
#include "GameFramework/SpringArmComponent.h"
// 相机头文件
#include "Camera/CameraComponent.h"
// 运动组件头文件
#include "GameFramework/CharacterMovementComponent.h"
// 接口
#include "MyInterface.h"
// 定时器头文件
#include "TimerManager.h"
// WidgetCompontent头文件
#include "Components/WidgetComponent.h"
#include "MyHealthWidget.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
UCLASS()// ,public IMyInterface:继承自接口
class UESTUDY_API AMyCharacter : public ACharacter, public IMyInterface
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明摄像头摇臂变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
USpringArmComponent* MySpringArm;
// 声明相机变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UCameraComponent* MyCamera;
// 声明输入映射相关变量
// 输入映射上下文
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputMappingContext* DefaultMappingContext;
// 移动映射输入变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* MoveAction;
// 旋转映射变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* LookAction;
// 鼠标按下移动时的代理绑定函数
// FInputActionValue:鼠标按下和键盘按下时会有值输入
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
// 重写两个接口函数
virtual void Attack()override;
virtual void CalculateHealth()override;
// 声明一个定时器变量
FTimerHandle Time;
// 声明一个函数用来方便定时器打印内容
void PrintF();
// 声明一个Widget类型变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UWidgetComponent* MyWidgetHealth;
// 重写受到伤害的函数
// 参数:接收到的伤害值,
virtual float TakeDamage(float DamageAmount,struct FDamageEvent const &DamageEvent,class AController* EnvenInstigator,AActor* DamageCauser)override;
// 射线检测起始位置
FVector StartLocation;
// 射线检测向前向量
FVector ForwardVector;
// 射线检测的最终位置
FVector EndLocation;
// 结构体,射线检测击中物体后返回的一些数据
FHitResult HitResult;
// 接收多射线反射返回的数组
TArray<FHitResult> HitResults;
};
- 编译运行,打开UE,在Content里新建一个蓝图类Actor,命名为BP_HitActor,在其中加一个Cube,类型为WorldDynamic,将Pawn的自动控制玩家设置为玩家0,将BP_HitActor和人物都拖拽到场景中,运行,移动人物,靠近BP_HitActor:
多射线对象检测
- 在MyCharacter.cpp中:
#include "MyCharacter.h"
// Sets default values
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 初始化
MySpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("MySpringArmComponent"));
MyCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("MyCameraComponent"));
// 初始化控件
MyWidgetHealth = CreateDefaultSubobject<UWidgetComponent>(TEXT("MyWidgetComponent"));
// 将控件设置为默认根组件的组件
MyWidgetHealth->SetupAttachment(RootComponent);
static ConstructorHelpers::FClassFinder<UUserWidget>WidgetClass(TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/UMG_Health.UMG_Health_C'"));
MyWidgetHealth->SetWidgetClass(WidgetClass.Class);
MyWidgetHealth->SetRelativeLocation(FVector(0, 0, 100));
// 设置其渲染方式,渲染到屏幕上或渲染到世界当中
MyWidgetHealth->SetWidgetSpace(EWidgetSpace::Screen);
// 设置位置大小
MyWidgetHealth->SetDrawSize(FVector2D(400, 20));
// 设置摄像机摇臂长度
MySpringArm->TargetArmLength = 400.0f;
// 设置根组件 将相机设置为摄像机摇臂的组件
MyCamera->SetupAttachment(MySpringArm);
// 将摄像机摇臂设置为默认根组件的组件
MySpringArm->SetupAttachment(RootComponent);
// 让控制器的旋转不影响角色的转动,只影响摄像机的转动
bUseControllerRotationPitch = false;// 设置Pitch旋转
bUseControllerRotationRoll = false;// 设置Roll旋转
bUseControllerRotationYaw = false;// 设置Yaw旋转
// 让角色面朝加速度的方向
GetCharacterMovement()->bOrientRotationToMovement = true;
// 使用Pawn控制器的旋转
MySpringArm->bUsePawnControlRotation = true;
}
// Called when the game starts or when spawned
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayController = Cast<APlayerController>(Controller))// 判断控制器是否合法
{
// 判断玩家子系统 获取增强输入的本地玩家子系统 将本地控制器转化为增强输入本地玩家子系统
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayController->GetLocalPlayer()))
{
// 转化成功 添加映射输入上下文 0为优先级,数值越大,优先级越高
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
// 调用接口函数
Attack();
CalculateHealth();
// 设置定时器 参数:时间句柄,对象,绑定的函数,计时间隔,是否循环
GetWorld()->GetTimerManager().SetTimer(Time, this, &AMyCharacter::PrintF, 1.0, true);
// 清除定时器 加上此段以后,就没有输出了
if (Time.IsValid()) // 时间合法的话
{
GetWorld()->GetTimerManager().ClearTimer(Time);
}
}
// Called every frame
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 执行射线检测
StartLocation = MyCamera->GetComponentLocation();
ForwardVector = MyCamera->GetForwardVector();
EndLocation = StartLocation + ForwardVector * 9999;
// 多射线对象查询检测
FCollisionObjectQueryParams objectType;// 用于添加通道
objectType.AddObjectTypesToQuery(ECC_WorldDynamic);// 添加通道ECC_WorldDynamic,可继续添加多个
objectType.AddObjectTypesToQuery(ECC_WorldStatic);
bool HitMulti2 = GetWorld()->LineTraceMultiByObjectType(HitResults, StartLocation, EndLocation, objectType);
if (HitMulti2)//判断是否检测到了东西
{
for (int32 i = 0; i < HitResults.Num(); i++)
{
AActor* HitMultiActor2 = HitResults[i].GetActor();
FVector HitLocation2 = HitResults[i].Location;
FVector HitImpactPoint2 = HitResults[i].ImpactPoint;
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("%s"), *HitMultiActor2->GetName()));
}
}
}
// Called to bind functionality to input
// 输入映射
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// 将玩家输入映射,转化为增强输入玩家映射 判断是否有输入映射
// CastChecked<UEnhancedInputComponent>(PlayerInputComponent):将PlayerInputComponent转化为UEnhancedInputComponent
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
// 绑定映射 MoveAction:映射变量,ETriggerEvent::Trigger:方式,this:对象,&AMyCharacter::Move绑定的函数
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::Look);
}
}
// 移动
void AMyCharacter::Move(const FInputActionValue& Value)
{
// 用2D变量来控制移动 Value.Get<FVector2D>():获得输入的2D向量坐标
FVector2D MovementVector = Value.Get<FVector2D>();
// 判断控制器是否合法
if (Controller != nullptr)
{
// 通过变量的旋转获得他的前向向量和右向向量,来控制前后左右移动
// Controller->GetControlRotation():获得控制器的旋转
const FRotator Rotation = Controller->GetControlRotation();
// 获取Yaw方向上的旋转
const FRotator YawRotation(0, Rotation.Yaw, 0);
// 获得前向向量(X轴方向)
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// 获得右向向量(Y轴方向)
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// 在键盘按下WASD时,添加输入
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
// 旋转
void AMyCharacter::Look(const FInputActionValue& Value)
{
// 获得鼠标X和Y轴的向量
FVector2D LookAxisVector = Value.Get<FVector2D>();
// 判断控制器是否合法,即是否有输入
if (Controller != nullptr)
{
// 获得X轴方向的向量值
AddControllerYawInput(LookAxisVector.X);
// 获得Y轴方向的向量值
AddControllerPitchInput(LookAxisVector.Y);
}
}
// 接口相关
void AMyCharacter::Attack()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Attack"));
}
void AMyCharacter::CalculateHealth()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("CalculateHealth"));
}
// 定时器相关
void AMyCharacter::PrintF()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Time"));
}
float AMyCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EnvenInstigator, AActor* DamageCauser)
{
UMyHealthWidget* MyWidget = Cast<UMyHealthWidget>(MyWidgetHealth->GetUserWidgetObject());
// 判断MyWidget是否合法
if (MyWidget)
{
// 判断血量值
if (MyWidget->CurrentHealth <= 0)
{
// 游戏结束
return 0;
}
MyWidget->CurrentHealth -= 5.0f;
}
return 0.0f;
}
- 在MyCharacter.h中:
#pragma once
#include "CoreMinimal.h"
// 输入映射 value值
#include "InputActionValue.h"
//增强输入头文件
#include "EnhancedInputComponent.h"
// 增强输入子系统
#include "EnhancedInputSubsystems.h"
// 控制器头文件
#include "GameFramework/Controller.h"
// 摄像机摇臂头文件
#include "GameFramework/SpringArmComponent.h"
// 相机头文件
#include "Camera/CameraComponent.h"
// 运动组件头文件
#include "GameFramework/CharacterMovementComponent.h"
// 接口
#include "MyInterface.h"
// 定时器头文件
#include "TimerManager.h"
// WidgetCompontent头文件
#include "Components/WidgetComponent.h"
#include "MyHealthWidget.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
UCLASS()// ,public IMyInterface:继承自接口
class UESTUDY_API AMyCharacter : public ACharacter, public IMyInterface
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 声明摄像头摇臂变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
USpringArmComponent* MySpringArm;
// 声明相机变量
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UCameraComponent* MyCamera;
// 声明输入映射相关变量
// 输入映射上下文
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputMappingContext* DefaultMappingContext;
// 移动映射输入变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* MoveAction;
// 旋转映射变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
class UInputAction* LookAction;
// 鼠标按下移动时的代理绑定函数
// FInputActionValue:鼠标按下和键盘按下时会有值输入
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
// 重写两个接口函数
virtual void Attack()override;
virtual void CalculateHealth()override;
// 声明一个定时器变量
FTimerHandle Time;
// 声明一个函数用来方便定时器打印内容
void PrintF();
// 声明一个Widget类型变量
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "mySceneComponent")
UWidgetComponent* MyWidgetHealth;
// 重写受到伤害的函数
// 参数:接收到的伤害值,
virtual float TakeDamage(float DamageAmount,struct FDamageEvent const &DamageEvent,class AController* EnvenInstigator,AActor* DamageCauser)override;
// 射线检测起始位置
FVector StartLocation;
// 射线检测向前向量
FVector ForwardVector;
// 射线检测的最终位置
FVector EndLocation;
// 结构体,射线检测击中物体后返回的一些数据
FHitResult HitResult;
// 接收多射线反射返回的数组
TArray<FHitResult> HitResults;
};
- 编译运行,打开UE,在Content里新建一个蓝图类Actor,命名为BP_HitActor,在其中加一个Cube,类型为WorldDynamic,将Pawn的自动控制玩家设置为玩家0,将BP_HitActor和人物都拖拽到场景中,运行,移动人物,靠近BP_HitActor,可以同时检测到世界动态和世界静态的物体
虚幻C++软引用
引用:分为软引用和硬引用
软引用:通常是仅储存资源对象的资源路径没有与资源产生耦合关系的引用(软引用加载到内存中,引用对象不会被加载到内存中,只有在需要的时候才会被加载进内存中)。软引用指向的资源未被加载,仅仅是提供了一个路径,通过路径找到对应的资源
硬引用:是拥有资源对象实际成员变量,直接与资源对象产生耦合(硬引用被加载到内存中,则被引用的对象资源也会被加载到内存中)
常用软引用:
- FSoftObjectPath
- FSoftClassPath
- TSoftObjectPtr
- TSoftClassPtr
软引用的使用:
- 在UE中创建一个C++类Actor,命名为:MySoftActor
- 在MySoftActor.h中
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MySoftActor.generated.h"
UCLASS()
class UESTUDY_API AMySoftActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMySoftActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 软引用 FSoftObjectPath
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Path")
FSoftObjectPath AssetObjectPath;
// 软引用 FSoftClassPath
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Path")
FSoftClassPath AssetClassPath;
// 软引用 TSoftObjectPtr 可引用Actor对象
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Path")
TSoftObjectPtr<AActor> AssetObjectPtr;
// 软引用
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Path")
TSoftClassPtr<AActor> AssetClassPtr;
};
- 编译运行,创建基于MySoftActor的蓝图类,命名为BP_MySoftActor,将其拖入场景中,查看右部细节面板是否设置成功
虚幻C++同步和异步加载资源
- 在以上基础上,在MySoftActor.cpp中添加代码:
#include "MySoftActor.h"
// 资源管理头文件
#include "Engine/AssetManager.h"
// Sets default values
AMySoftActor::AMySoftActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMySoftActor::BeginPlay()
{
Super::BeginPlay();
// 同步加载资源
// 用于加载资源路径的变量
FSoftObjectPath Path1 = TEXT("/Script/Engine.Texture2D'/Game/StarterContent/Textures/T_Brick_Clay_Beveled_D.T_Brick_Clay_Beveled_D'");
// 智能指针
TSharedPtr<FStreamableHandle> SyncStreamHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(Path1);
if (SyncStreamHandle)// 判断是否已经找到了内存
{
UTexture2D* Image1 = Cast<UTexture2D>(SyncStreamHandle->GetLoadedAsset());
if (Image1)// 判断图片是否加载成功
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("%s"),*Image1->GetName()));
}
}
// 异步加载资源
FSoftObjectPath Path2 = TEXT("/Script/Engine.Texture2D'/Game/StarterContent/Textures/T_Brick_Clay_Beveled_M.T_Brick_Clay_Beveled_M'");
// 智能指针
TSharedPtr<FStreamableHandle> ASyncStreamHandle = UAssetManager::GetStreamableManager().RequestSyncLoad(Path2);
if (ASyncStreamHandle)// 判断是否已经找到了内存
{
UTexture2D* Image2 = Cast<UTexture2D>(ASyncStreamHandle->GetLoadedAsset());
if (Image2)// 判断图片是否加载成功
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("%s"), *Image2->GetName()));
}
}
}
// Called every frame
void AMySoftActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
- 编译运行,打开UE,创建基于MySoftActor的蓝图类,将其拖入场景中,运行:
虚幻C++智能指针
为什么引入智能指针:是为了方便程序员管理堆内存而提出来的,核心思想是:程序员只管分配堆内存,释放内存交给智能指针。UE有自己的一套内存管理系统,但只针对与UObjet,为了方便对非Object对象的内存管理,UE设计了一套自己的智能指针库。包含:共享指针(TSharePtr)、弱指针(TWeakPtr)、以及特有的共享引用(TShareRef)。
使用智能指针的好处:
- std不能全平台通用
- 允许在所有编译器和平台上更一致的实现
- 可以和UE其他容器无缝衔接
- 更好的控制平台细节,包括线程和优化
- 希望线程安全特性是可选的(为了性能)
- 添加自己的改进(MakeShareable,assign to nullptr等)
- 不需要也不希望出现异常
- 希望对性能有更好的控制(内联、内存、虚拟的使用等)
- 更容易调试(自由代码注释等)
- 不需要的时候不要引入新的第三方依赖项
共享指针
- 打开UE,创建一个C++类Actor,命名为MySmartPtrActor
- MySmartPtrActor.h文件:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MySmartPtrActor.generated.h"
// 声明一个原生的C++类
class TestA
{
public:
int a = 0;
float b = 0;
TestA()//构造函数
{
// 初始化变量
a = 0;
b = 0;
};
TestA(int a, float b)// 带有两个参数的构造函数
{
this->a = a;
this->b = b;
};
~TestA()// 析构函数
{
UE_LOG(LogTemp, Warning, TEXT("xigou"));
};
private:
};
UCLASS()
class UESTUDY_API AMySmartPtrActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMySmartPtrActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 使用原生C++类 测试共享指针
void TestAFunc();
};
- MySmartPtrActor.cpp文件:
#include "MySmartPtrActor.h"
// Sets default values
AMySmartPtrActor::AMySmartPtrActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMySmartPtrActor::BeginPlay()
{
Super::BeginPlay();
// 调用函数
TestAFunc();
}
// Called every frame
void AMySmartPtrActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 使用原生C++类
void AMySmartPtrActor::TestAFunc()
{
// 用C++的方式创建一个原生指针
TestA* ptr1 = new TestA(1, 2.0f);
// 用共享指针左值初始化
TSharedPtr<TestA>SharedPtr2(ptr1);// 不推荐使用此方式,容易混淆共享指针和原生指针
ptr1 = nullptr;
// 用共享指针右值初始化
TSharedPtr<TestA>SharedPtr3(new TestA(3, 4.0f));
// 拷贝构造函数初始化 用共享指针初始化共享指针
TSharedPtr<TestA>SharedPtr4(SharedPtr2);// 调用拷贝构造函数以初始化共享指针
// UE的方法初始化共享指针
TSharedPtr<TestA>SharedPtr5 = nullptr;// 共享指针可以初始化为空
SharedPtr5 = MakeShareable(new TestA(5, 6.0f));
// 线程安全的共享指针
TSharedPtr<TestA, ESPMode::ThreadSafe>SharedPtr6(new TestA(7, 8.0f));
//共享指针常用的接口
if (SharedPtr5.IsValid())// 判断共享指针是否指向了一个有效的对象
{
// 声明一个共享引用
TSharedRef<TestA>SharedRef1(new TestA(9, 10.0f));// 共享引用必须执行一个对象,不能为空
// 将共享指针转化为共享引用 引用计数加一
SharedRef1 = SharedPtr5.ToSharedRef();
// 获得共享引用的引用计数
int32 Count1 = SharedPtr5.GetSharedReferenceCount();
UE_LOG(LogTemp, Warning, TEXT("Count1 is %d"), Count1);
if (!SharedPtr5.IsUnique())//判断其是不是唯一的共享指针,也就是引用计数是否为1
{
UE_LOG(LogTemp, Warning, TEXT("SharedPtr5 is not Unique"));
}
// 解引用,就是返回对象的原生指针 解引用:共享引用的名称.Get()
SharedPtr5.Get()->a;
UE_LOG(LogTemp, Warning, TEXT("SharedPtr5a is %d"), SharedPtr5.Get()->a);
// 重置共享指针,即把共享指针变为null 引用计数会变为0
SharedPtr5.Reset();
int32 Count2 = SharedPtr5.GetSharedReferenceCount();
UE_LOG(LogTemp, Warning, TEXT("Count2 is %d"), Count2);
}
}
- 编译运行,将MySmartPtrActor拖入场景中,运行,查看输出日志:
共享引用
- MySmartPtrActor.h文件:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MySmartPtrActor.generated.h"
// 声明一个原生的C++类
class TestA
{
public:
int a = 0;
float b = 0;
TestA()//构造函数
{
// 初始化变量
a = 0;
b = 0;
};
TestA(int a, float b)// 带有两个参数的构造函数
{
this->a = a;
this->b = b;
};
~TestA()// 析构函数
{
UE_LOG(LogTemp, Warning, TEXT("xigou"));
};
private:
};
UCLASS()
class UESTUDY_API AMySmartPtrActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMySmartPtrActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 测试共享引用
void TestBFunc();
};
- MySmartPtrActor.cpp文件:
#include "MySmartPtrActor.h"
// Sets default values
AMySmartPtrActor::AMySmartPtrActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMySmartPtrActor::BeginPlay()
{
Super::BeginPlay();
// 调用函数
TestBFunc();
}
// Called every frame
void AMySmartPtrActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 测试共享引用
void AMySmartPtrActor::TestBFunc()
{
// 共享引用初始化的时候必须指向一个有效的对象
TSharedRef<TestA> SharedRef2(new TestA(7, 8.0f));
// 共享引用常用的接口
if (SharedRef2.IsUnique())//判断其是不是唯一的共享引用
{
// 解引用
SharedRef2->a;// 虽然是共享引用,但是解引用的方式仍然是->
UE_LOG(LogTemp, Warning, TEXT("SharedRef2a is %d"), SharedRef2->a);
// 共享引用转化为共享指针
TSharedPtr<TestA>SharedPtr6;
SharedPtr6 = SharedRef2;// 可以直接转化
SharedPtr6.Get()->b;
UE_LOG(LogTemp, Warning, TEXT("SharedPtr6b is %f"), SharedPtr6.Get()->b);
}
}
- 编译运行,将MySmartPtrActor拖入场景中,运行,查看输出日志:
弱指针
弱指针的作用:
- 解决了循环引用,只对引用对象保留引用权,并不参与引用计数。当我们需要操作引用对象时,需要转换成智能指针。
- 不能阻止对象被销毁。如果弱指针指向的对象被销毁,弱指针会自动清空,不需要手动向智能指针一样置空
UE查找Actor的方式
- 在蓝图中设置tag属性,通过tag UGameplayStatics::GetAllActorsWithTag
#include "Kismet/GameplayStatics.h"
TArray<AActor*> Actors;
UGameplayStatics::GetAllActorsWithTag(GetWorld(), TEXT("actorName"), Actors);
for (AActor* Actor: Actors)
{
}
- 通过 UGameplayStatics::GetAllActorsOfClass 查找
#include "Kismet/GameplayStatics.h"
TArray<AActor*> Actors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass(), Actors);
for (AActor* Actor : Actors)
{
}