本文分享Unity中的AI算法和实现1-Waypoint

在Unity中, 我们有一些有趣且常见的AI算法可以研究和使用, 其中最常见的就是怪物的简单AI, 比如在RPG游戏里, 怪物在某些点定点巡逻, 当玩家进入检测区域时, 怪物会主动追击玩家, 追击到玩家后对玩家进行伤害, 或者在超过最大距离后脱离追击状态, 由恢复到巡逻状态.

我们接下来几篇文章会简单的实现一个基于有限状态机的怪物AI, 这篇文章是最基础的部分, 介绍Waypoint.

Waypoint顾名思义, 是用来描述点的一个抽象概念, 简单点说就是一个一个位置, 我们的怪物向这些预先设定的位置定点巡逻.

这个算法本身非常简单:

  • 我们有一个Waypoint列表, 有一个怪物.
  • 怪物根据顺序(可以另外定义顺序算法), 向目标点转向, 位移, 到达目标点后, 继续向下一个目标点转向和位移(也可以在这个点等待一会, 观望一番后才向下一个点前进).
  • 到达最后一个目标点(数组最后一个元素), 切换到第一个目标点, 继续前进.

下面开始我们的示例.

创建场景

新建工程后, 往场景中拖一个Plane, 充当地面, 再拖一个Capsule充当怪物, 并给怪物创建一个眼睛, 用来表示方向, 最后调整一下整个场景的颜色(可选).

unity saturate 算法_游戏引擎

然后创建一个空的GameObject充当Waypoint容器, 然后创建空的子GameObject充当Waypoint, 我们可以给Waypoint节点增加Icon方便观察:

unity saturate 算法_Time_02

然后多创建几个Waypoint节点并摆好位置:

unity saturate 算法_unity saturate 算法_03

对应的Hierarchy如下:

unity saturate 算法_游戏引擎_04

最后创建控制脚本: MonsterContoller_Wp.cs并挂载到Monster身上.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Debug = UnityEngine.Debug;

public class MonsterContoller_Wp : MonoBehaviour
{
    [SerializeField] private Transform[] m_Waypoints;
    [SerializeField] private float m_MoveSpeed = 10f;
    [SerializeField] private float m_RotateSpeed = 10f;
    [SerializeField] private float m_MinTargetDistance = 0.5f;

    /// <summary>
    /// 当前wp索引
    /// </summary>
    private int m_CurrentWpIndex = 0;

    /// <summary>
    /// 缓存Transform, 避免每帧使用属性获取
    /// </summary>
    private Transform m_SelfTrans;

    private void Start()
    {
        m_SelfTrans = transform;

        Application.targetFrameRate = 60;
    }

    private void Update()
    {
        var target = m_Waypoints[m_CurrentWpIndex];
        if (Vector3.Distance(target.position, m_SelfTrans.position) < m_MinTargetDistance)
        { // 已经靠近, 切换到下一个点
            m_CurrentWpIndex++;
            m_CurrentWpIndex %= m_Waypoints.Length; // 越界后从头开始

            target = m_Waypoints[m_CurrentWpIndex];
        }

        var targetDir = (target.position - m_SelfTrans.position).normalized;

        // 移动和转向
        m_SelfTrans.Translate(targetDir * Time.deltaTime * m_MoveSpeed, Space.World); // 匀速向forward移动Time.deltaTime * m_MoveSpeed长度的距离
        m_SelfTrans.rotation = Quaternion.Lerp(m_SelfTrans.rotation, Quaternion.LookRotation(targetDir), Time.deltaTime * m_RotateSpeed);
    }
}

代码很简单, 也有对应的注释, 这里不再赘述, 下面是效果:

unity saturate 算法_unity saturate 算法_05

好了, 今天的内容就是这些, 希望对大家有所帮助.