遇到的问题:
- 如何使用UEC++实现UE蓝图中的方法
- 世界中存在一个不是自己放置的类
- 在一个C++类中调用另一个C++类中的变量或者函数
- Get All Actors of Class的替代
- 蓝图Actor的名字和在世界中获取到的Actor中的名字不相同
详见博客置顶文章-UE遇到的各种问题
实现A星算法规划路径
代码
AStar_test2Character.h
1 |
|
AStar_test2Character.cpp
1 |
|
A星算法路径存入样条点数组中
难点
如何获取样条线组件
优化前:在
AStar_test2Character.h
创建一个ASpine
类的变量SpineRef
,然后在AStar_test2Character.cpp
中创建了一个ASpine
类的对象,对该对象执行函数操作;此时Spine.cpp
类中的this
指的是系统创建的对象。优化后:直接在
AStar_test2Character.cpp
中使用GetAllActorsOfClass
方法获取到真正的ASpine
的引用,然后直接对ASpine
本体执行函数操作;此时Spine.cpp
类中的this
指的是Spine.cpp
本身。
优化前
AStar_test2Character
类和Spine
类
1 | // AStar_test2Character.h |
优化后(减少了很多不必要的代码)
1 | // AStar_test2Character.cpp |
代码
1 | // AStar_test2Character.cpp |
人物实现AI行为树——按照样条线行走
难点
自定义实现AI行为树中的Decorator和Task节点
1. 创建C++类
Decorator应继承BTDecorator
,Task应继承BTTaskNode
;如果希望使用黑板键,可以继承自相应的有_BlackboardBase
后缀的类,这个类会提供一个黑板键成员变量,当然也可以不继承这个_BlackboardBase
,自己写
2. 关键函数
只举例了代码中用到的部分函数,其他函数请自己参考
BTDecorator.h
和BTTaskNode.h
中的函数以及注释
Decorator的判断函数为
1 | virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const override; |
重写这个函数,返回值是能否执行子节点
Task的开始执行函数
1 | /** starts this task, should return Succeeded, Failed or InProgress |
可用的返回值由三种
1 | EBTNodeResult::Failed |
前两种自然就是立刻返回当前节点执行成功与否,最后一种是表示节点虽然已经开始执行,但暂时还没执行结束,当希望结束执行InProgress的节点(以成功执行举例),就需要调用
1 | FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded); |
该函数一般在TickTask
中调用,也就是每个Tick都去检查是否执行完成。
TickTask函数原型
1 | /** ticks this task |
根据上面的注释,我们可以知道,只有当设置bNotifyTick
= true时TickTask
才会执行;同理还有bNotifyActivation
于OnNodeActivation
、bNotifyDeactivation
于OnNodeDeactivation
如何在AIController
中获取黑板,并修改其中的变量值
1. 定义变量
1 | // 获取控制角色,初始化AI逻辑和状态 |
UBlackboardComponent* BlackboardComp
和UBlackboardData* BlackboardAsset
的区别
- **
UBlackboardComponent
**:这是一个实际的游戏性组件(Actor Component),你可以将其添加到你的AI控制器或者需要访问黑板数据的角色上。它负责运行时存储所有黑板键值对(Key-Value Pairs),这些键值对代表了影响AI决策的数据点。例如,目标位置、最近看到的敌人等。简单来说,UBlackboardComponent
就是AI用来在游戏过程中实时记录和查询各种状态的地方。 - **
UBlackboardData
**:这是一个资产(Asset),定义了黑板的结构或模式。它描述了可以在UBlackboardComponent
中使用的不同键(Keys)以及每个键的类型(比如Object, Vector, Bool等)。这个资产主要用于设计阶段,在编辑器中设置,决定了哪些键可用及其默认值。因此,UBlackboardData
更像是一个模板或蓝图,指定了你希望在黑板上跟踪的所有信息的种类。
2. 在OnPossess
函数中初始化行为树、黑板组件
当一个 AIController
开始控制一个新的 Pawn
(通常是NPC或玩家角色的基类)时,就会调用 OnPossess
函数。这个函数的主要作用是初始化与新控制的角色相关的任何AI逻辑或状态。具体来说,它允许AI控制器执行一些设置操作,比如为该角色创建行为树、设置黑板数据、初始化感知系统等。
1 | void AMyAIController::OnPossess(APawn* possesPawn) |
UseBlackboard(BlackboardAsset, BlackboardComp)
函数调用的作用
具体来说,该函数的作用如下:
- 参数1 (
BlackboardAsset
): 这是一个指向UBlackboardData
类型对象的指针,定义了黑板中可用键(Keys)的结构和类型。它描述了哪些信息将被存储在黑板上,例如目标位置、最近发现的敌人等。 - 参数2 (
BlackboardComp
): 这是一个指向UBlackboardComponent
类型对象的引用,表示实际运行时用来存储和访问黑板数据的组件。通过这个组件,AI 系统可以在运行时读取和写入黑板上的数据。
当调用 UseBlackboard()
函数时,它会执行以下操作:
- 初始化黑板组件:基于提供的
BlackboardAsset
,初始化关联的BlackboardComp
,使其准备好根据定义的模式存储和管理数据。 - 配置黑板键:根据
BlackboardAsset
中定义的键值对,配置BlackboardComp
以确保所有必要的键都已准备就绪,并可以被行为树中的任务和服务所使用。 - 返回值:如果初始化成功,则返回
true
,否则返回false
。这允许你在初始化失败的情况下进行错误处理或日志记录。
3. 在AIController
类的函数中修改黑板中的变量值
1 | // 设置黑板中的值 |
修改黑板中的变量值后,会触发Decorator中的判断函数CalculateRawConditionValue
,若返回True,则执行Task中的ExecuteTask
函数,实现AI行为树的全部过程。
蓝图中AI行为树的配置如下
代码
继承BTDecorator
的UBTD_Judge
类
1 | UBTD_Judge::UBTD_Judge(FObjectInitializer const& ObjectInitializer) |
继承BTTaskNode
的UUBTT_MoveBySpine
Task的代码有问题,以后有时间会修改
1 | UUBTT_MoveBySpine::UUBTT_MoveBySpine() |
继承AIController
的AMyAIController
1 | // .h |
1 | .cpp |