遇到的问题:
- 如何使用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 |