Unity人工智能学习—高级随机运动

2015年11月09日 11:35 0 点赞 0 评论 更新于 2025-11-21 19:21

在之前的学习中,我们已经接触过随机运动。本篇文章主要介绍对之前随机运动的改进。话不多说,先来看效果图。与之前的随机运动相比,这里的飞机能够随机转动方向,并且转弯过程平滑。

有一种实现随机运动的方法是每一帧都计算一个随机的驱动力,但这种方式会导致飞机运动产生抖动,无法实现持久的转弯。实际上,使用Perlin噪声这一优秀的随机函数可以实现平滑转弯,不过会带来较大的CPU开销。当然,在没有其他办法时,这仍然是一种可行的解决方案,而且Perlin噪声在很多应用场景中都有应用。

在本文中,我们采用的解决方案是:在飞机前端凸出位置界定一个圆圈,在圆圈内隐藏一个跟踪物,并将其运动范围限制在该圆圈上。然后随机地让这个隐藏的跟踪物运动起来,飞机只需持续追踪这个跟踪物,就可以实现平滑的随机转弯运动。这类似于在鱼的前方用鱼竿挂着一个诱饵,鱼永远也追不上这个诱饵,因为诱饵实际上是和鱼关联在一起的。以下是隐藏跟踪物的效果图。

主要代码

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

/// <summary>
/// 前面的随机运动比较粗糙,这种运动会产生抖动,不能达到持久的转弯(事实上,一个好的随机函数,Perlin噪声,可以产生光滑转弯,但是那样CPU的开销会很大。)。
/// 在飞机前面添加一个凸出的圆圈,目标被限制在圆圈上,然后移动飞机至目标位置,当然飞机永远也不会追上目标,就好像给鱼的前面吊一个钓鱼竿和诱饵一样。
/// </summary>
public class AIAdvancedRandMove : MonoBehaviour
{
public float m_weight; // 这个值用于控制转弯速度,值越大转弯越快
public PlayObject WanderTarget; // 这是一个点,被限制在半径为m_dWanderRadius的圆圈内,以大圆圈为中心
[HideInInspector]
private Vector2 vWanderTarget;
public PlayObject plane;
public Image Circle;
public float m_dWanderRadius; // wander圈的半径
public float m_dWanderDistance; // wander圈凸出在飞机前面的距离
public float m_dWanderJitter; // 每秒加到目标的随机位移的最大值

// Use this for initialization
void Start()
{
// 初始化小圆圈的位置并且限制小圆圈在大圆圈里面,大圆圈的半径为m_dWanderRadius
float thera = Random.Range(0, 361.0f) * Mathf.PI / 180; // 随机0~360度并转换成弧度
// Debug.Log(Mathf.Sin(30*Mathf.PI/180));
vWanderTarget = new Vector2(m_dWanderRadius * Mathf.Cos(thera), m_dWanderRadius * Mathf.Sin(thera));
WanderTarget.LocalPosition = vWanderTarget;
}

// Update is called once per frame
void Update()
{
Vector2 moveVec = AI_AdvancedRandMove();
float length = Mathf.Sqrt(moveVec.x * moveVec.x + moveVec.y * moveVec.y);
if (length != 0)
{
// Debug.Log("x:" + moveVec.x + "y:" + moveVec.y);
plane.Velocity += m_weight * moveVec / length;
Circle.transform.position = plane.Position;
plane.Move(1, true);
}
}

Vector2 AI_AdvancedRandMove()
{
// 随机位移值
float JitterThisTimeSlice = m_dWanderJitter * Time.deltaTime * 100;
// 首先,加一个小的随机向量到目标位置
vWanderTarget += new Vector2(Random.Range(-1, 2) * JitterThisTimeSlice, Random.Range(-1, 2) * JitterThisTimeSlice);
// 把这个向量归一化,也即把这个向量重新投影回单元圆周上
float lenght = Mathf.Sqrt(vWanderTarget.x * vWanderTarget.x + vWanderTarget.y * vWanderTarget.y);
vWanderTarget = vWanderTarget / lenght;
// 使向量的长度增加wander圆周的半径长度
vWanderTarget *= m_dWanderRadius;
Vector2 target = new Vector2(vWanderTarget.x + m_dWanderDistance, vWanderTarget.y);
WanderTarget.LocalPosition = target;
target = WanderTarget.Position;
// Vector2 Target = PointToWorldSpace(ref target, plane.vHeading, plane.vSide, plane.Position);
return target - plane.Position;
}

/// <summary>
/// 从局部坐标的位置转向世界坐标
/// </summary>
/// <param name="vec"></param>
/// <param name="Heading"></param>
/// <param name="Side"></param>
/// <returns></returns>
Vector2 PointToWorldSpace(ref Vector2 point, Vector2 Heading, Vector2 Side, Vector2 Postion)
{
Vector2 TransVec = point;
C2DMatrix matTransform = new C2DMatrix();
// 旋转矩阵
matTransform.Rotate(ref Heading, ref Side);
// 平移矩阵
matTransform.Translate(Postion.x, Postion.y);
matTransform.TransformVector2Ds(ref TransVec);
return TransVec;
}
}

另外,附上工程的演示下载地址,如果各位觉得不错,请点个赞! 网盘下载:http://www.taidous.com/bbs/thread-34245-1-1.html

作者信息

洞悉

洞悉

共发布了 3994 篇文章