八股
-
对象池是什么:
- 对象池维护了一组可重复使用的对象集合。当游戏需要一个新对象时,它首先检查对象池中是否有空闲的对象可用,如果有,则重新配置该对象并将其激活使用,而不是创建一个新的实例。当对象不再需要时,它不会被销毁,而是返回到池中,等待下次重用。
- 优点:减少内存分配;提高性能;减少垃圾收集
-
unity资源动态加载方式
- Resources.Load:从
Resources
文件夹中加载资源。(缺点:所有放在Resources
文件夹中的资源都会被打包进最终的游戏包中,可能会导致游戏包体积增大,且运行时管理较为复杂。)Sprite sprite = Resources.Load<Sprite>("Sprites/MySprite");
AssetBundle
是一种更为高级的资源打包和加载机制。通过这种方式,可以将多个资源打包成一个单独的文件,在运行时按需加载。
- Resources.Load:从
-
NavMesh 寻路原理
- 首先生成 NavMesh,根据角色大小、可爬坡的坡度等,生成可行走区域的导航网格(用简化的多边形网格覆盖所有可行走的表面)
- 寻路:在导航网格上找到一系列连接两点的多边形,这些多边形的边缘构成了路径(为了更加自然,还需要对路径进行平滑处理)
- A*找不到路时的处理:如寻找近似路径,考虑寻找距离目标最近的可达点
-
AI 状态机实现方式
- 基本组成:
- 状态(State):代表 AI 在特定时间点的行为或者状态。
- 转换(Transition):定义了从一个状态转换到另一个状态的条件。
- 事件(Event):触发状态转换的外部输入。
- 通常使用枚举类型来表示所有可能的状态
- 实现原理:
- 初始化:在 AI 启动时,初始化状态机,创建所有状态的实例,并填充状态字典。同时,设置一个变量来保存当前状态。
- 状态更新:在 AI 的更新循环中,根据当前状态执行对应的行为(例如,调用当前状态对象的
Update
方法)。 - 状态转换:当满足转换条件时(例如,在
Update
方法中检测到玩家进入追击范围),AI 将切换到新的状态。这通常涉及调用当前状态的Exit
方法,更改当前状态变量,然后调用新状态的Enter
方法。 - 事件处理:状态机可以订阅游戏事件(如玩家的行为、游戏环境变化等),并在事件发生时评估是否需要进行状态转换。
//接口定义 public interface IState { void OnEnter(); void OnExit(); void Update(); } //状态类 public class PatrolState : IState { private readonly EnemyController enemyController; public PatrolState(EnemyController enemyController) { this.enemyController = enemyController; } public void OnEnter() { Debug.Log("Enter Patrol State"); } public void OnExit() { Debug.Log("Exit Patrol State"); } public void Update() { // 实现巡逻逻辑 // 检查是否满足转换到其他状态的条件 if (enemyController.IsPlayerInSight()) { enemyController.SetState(new ChaseState(enemyController)); } } } //状态控制器 public class EnemyController : MonoBehaviour { public float patrolRotateSpeed = 30f; private IState currentState; private void Start() { SetState(new PatrolState(this)); } private void Update() { //状态的自动转移 currentState?.Update(); } public void SetState(IState newState) { currentState?.OnExit(); currentState = newState; currentState.OnEnter(); } }
- 基本组成:
-
AI 行为树的实现方式
- 节点的分类:
- 根节点(Root):行为树的起点,整个行为树只有一个根节点。
- 组合节点(Composite Nodes):控制子节点的执行顺序。
- 序列节点(Sequence):按顺序执行子节点,直到一个子节点失败或所有子节点成功。
- 选择节点(Selector):也称为优先级选择器,按顺序执行子节点,直到一个子节点成功或所有子节点失败。
- 并行节点(Parallel):同时执行所有子节点,直到特定数目的子节点成功或失败。
- 叶子节点(Leaf Nodes):实际执行任务的节点
- 动作节点(Action):执行具体的行为,如移动、攻击等。
- 条件节点(Condition):检查某个条件是否满足
- 工作原理:行为树从根节点开始执行,通过递归遍历树中的节点来控制AI的行为。每个节点根据其类型和逻辑决定执行哪个子节点,直到达到叶子节点执行具体动作或判断条件。节点执行的结果通常是成功、失败或运行中这三种状态之一。
//定义节点基类 public abstract class BTNode { public abstract bool Execute(); } //实现组合节点 // 选择器节点 public class Selector : BTNode { private List<BTNode> children = new List<BTNode>(); public Selector(List<BTNode> children) { this.children = children; } public override bool Execute() { foreach (var child in children) { if (child.Execute()) { return true; } } return false; } } // 序列节点 public class Sequence : BTNode { private List<BTNode> children = new List<BTNode>(); public Sequence(List<BTNode> children) { this.children = children; } public override bool Execute() { foreach (var child in children) { if (!child.Execute()) { return false; } } return true; } } //实现行为节点 // 一个简单的行为节点示例:成功节点 public class SucceedNode : BTNode { public override bool Execute() { // 实现具体行为 Debug.Log("Action Succeeded"); return true; // 总是返回成功 } } // 条件节点示例:检查条件 public class CheckConditionNode : BTNode { private Func<bool> condition; public CheckConditionNode(Func<bool> condition) { this.condition = condition; } public override bool Execute() { return condition(); } } //构建行为树并执行 public class BehaviourTreeController : MonoBehaviour { private BTNode root; private void Start() { BuildBehaviourTree(); } private void BuildBehaviourTree() { // 构建行为树结构 root = new Selector(new List<BTNode> { new Sequence(new List<BTNode> { new CheckConditionNode(() => true), // 条件示例 new SucceedNode() // 行为示例 }) // 在这里添加更多的节点 }); } private void Update() { if (root != null) { root.Execute(); } } }
- 节点的分类:
-
A*算法的优化
- 分层路径规划(Hierarchical Path Planning):在复杂的环境中,可以将地图分解为几个层次,先在一个较粗糙的层次上进行寻路,然后在细节层次上进行局部优化。这种方法可以大大减少需要考虑的节点数量。
- 增量式路径搜索(Incremental Search):当环境发生轻微变化时,可以利用之前的搜索结果来加快寻路。例如,D* Lite 算法就是基于 A*的增量式搜索算法,适用于动态环境下的路径规划。
- 对于格点地图优化:在特定的场景中,如格点地图上,跳点搜索(JPS)是对 A*算法的一种优化。它通过跳过某些节点来减少需要考虑的节点总数,显著提高了搜索效率,尤其是在开放区域较多的地图上。