UnityAI——动物迁徙中的跟随实现实例

2023-11-10 14:15

本文主要是介绍UnityAI——动物迁徙中的跟随实现实例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  大家好,我是七七,今天来给大家介绍的是Unity中用操控行为实现的跟随领队行为。

看本文若是想了解和实现,只看本文即可,若是想彻底弄透,建议从七七的游戏AI专栏开始看。

废话不多说,先上视频:

我们的目标是让后面的人跟着领头的人,并遵循一些规则

对于领头的人:

  1. 随机地移动
  2. 检测前方是否有人

对于跟随的人;

  1. 跟的不要太近
  2. 人与人直接不要拥挤
  3. 如果发现挡道领头人路了,赶紧让开 

 我们只需要实现这些规则,就可以得到理想的效果,这与神经网络的思想类似,下面我们就来实现这些规则。

规则1

这个脚本是挂载领头人身上的,目的是在前方LEADER_BEHIND_DIST处画一个圆球,若是说前方有人,则让这些人避开。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DrawGizmos : MonoBehaviour
{public float evadeDistance;public Vector3 center;private Vehicle vehicleScript;private float LEADER_BEHIND_DIST;void Start(){vehicleScript = GetComponent<Vehicle>();LEADER_BEHIND_DIST = 2.0f;}void Update(){center = transform.position + vehicleScript.velocity.normalized * LEADER_BEHIND_DIST;}void OnDrawGizoms(){Gizmos.DrawWireSphere(center, evadeDistance);}
}

规则2

这个脚本是挂在一个空物体上的,目的是生成多个跟随者

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GenerateBotsForFollowLeader : MonoBehaviour
{public GameObject botPrefab;public GameObject leader;public int botCount;public float minX = -5f;public float maxX = 5.0f;public float minZ = -5.0f;public float maxZ = 5.0f;public float Yvalue = 1.026003f;void Start(){Vector3 spawnPosition;GameObject bot;for(int i = 0; i < botCount; i++){spawnPosition = new Vector3(Random.Range(minX, maxX), Yvalue, Random.Range(minZ, maxZ));//随机产生一个生成位置bot = Instantiate(botPrefab, spawnPosition, Quaternion.identity) as GameObject;bot.GetComponent<SteeringForLeaderFollowing>().leader = leader;bot.GetComponent<SteeringForEvade>().target = leader;bot.GetComponent<SteeringForEvade>().enabled = false;bot.GetComponent<EvadeController>().leader = leader;}}
}

规则3

这个脚本是挂在跟随者身上的,是为了确定跟随者的路径目标点,即领导人身后LEADER_BEHIND_DIST处

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(SteeringForArrive))]
public class SteeringForLeaderFollowing : Steering
{public Vector3 target;private Vector3 desiredVelocity;//预期速度private Vehicle m_vehicle;//获得被操控的AI角色private float maxSpeed;private bool isPlanar;public GameObject leader;private Vehicle leaderController;private Vector3 leaderVelocity;private float LEADER_BEHIND_DIST=2.0f;private SteeringForArrive arriveScript;private Vector3 randomOffset;void Start(){m_vehicle = GetComponent<Vehicle>();maxSpeed = m_vehicle.maxSpeed;isPlanar = m_vehicle.isPlanar;leaderController=leader.GetComponent<Vehicle>();arriveScript= GetComponent<SteeringForArrive>();//为抵达行为指定目标点arriveScript.target = new GameObject("arriveTarget");arriveScript.target.transform.position = leader.transform.position;}public override Vector3 Force(){leaderVelocity = leaderController.velocity;target=leader.transform.position+LEADER_BEHIND_DIST*(-leaderVelocity).normalized;//计算目标点arriveScript.target.transform.position = target;return new Vector3(0, 0, 0);}
}

 规则4

这个脚本是挂在跟随者身上的,目的是为了避免跟随者挡住领导人的路

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EvadeController : MonoBehaviour
{public GameObject leader;private Vehicle leaderLocomotion;private Vehicle m_vehicle;private bool isPlanar;private Vector3 leaderAhead;private float LEADER_BEHIND_DIST;private Vector3 dist;public float evadeDistance;private float sqrEvadeDistance;private SteeringForEvade evadeScript;void Start(){leaderLocomotion = leader.GetComponent<Vehicle>();evadeScript= GetComponent<SteeringForEvade>();m_vehicle= GetComponent<Vehicle>();isPlanar=m_vehicle.isPlanar;LEADER_BEHIND_DIST = 2.0f;sqrEvadeDistance=sqrEvadeDistance*sqrEvadeDistance;}void Update(){leaderAhead=leader.transform.position+leaderLocomotion.velocity.normalized*LEADER_BEHIND_DIST;  //计算领队前方的一个点dist = transform.position - leaderAhead;if (isPlanar){dist.y = 0;}if(dist.sqrMagnitude < sqrEvadeDistance){evadeScript.enabled = true;Debug.DrawLine(transform.position, leader.transform.position);}else{evadeScript.enabled = false;}}
}

实现

前提

有3个基类

UnityAI——操控行为编程的主要基类-CSDN博客

第一步

创建一个场景,一个Plane

第二步

创建一个Cube作为领队,起名为Leader,为其挂上三个脚本,如下所示:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class AILocomotion : Vehicle
{private CharacterController controller;  //AI�Ľ�ɫ������private Rigidbody theRigidbody;private Vector3 moveDistance;//AI��ɫÿ�ε��ƶ�����void Start(){controller = GetComponent<CharacterController>();theRigidbody = GetComponent<Rigidbody>();moveDistance = new Vector3(0, 0, 0);base.Start();//���û����start��������������ij�ʼ��}//������ز�����FixedUpdate�и���void FixedUpdate(){velocity += acceleration * Time.fixedDeltaTime;//�����ٶ�if (velocity.sqrMagnitude > sqrMaxSpeed)   //��������ٶ�velocity = velocity.normalized * maxSpeed;moveDistance = velocity * Time.fixedDeltaTime;if (isPlanar)  {velocity.y = 0;moveDistance.y = 0;}if (controller != null)//����Ѿ�ΪAI��ɫ���ӽ�ɫ����������ô���ý�ɫ������ʹ���ƶ�controller.SimpleMove(velocity);//�����ɫ��û��ɫ��������ҲûRigidbody//����Rigidbody����Ҫ�ɶ���ѧ�ķ�ʽ�������ƶ�else if (theRigidbody == null || !theRigidbody.isKinematic)transform.position += moveDistance;else //��Rigidbody���ƽ�ɫ���˶�theRigidbody.MovePosition(theRigidbody.position+moveDistance);if(velocity.sqrMagnitude>0.00001)//���³�������ٶȴ���һ����ֵ��Ϊ�˷�ֹ������{Vector3 newForward = Vector3.Slerp(transform.forward, velocity, damping * Time.deltaTime);if(isPlanar)newForward.y = 0;transform.forward = newForward;}//�������߶���gameObject.GetComponent<Animation>().Play("walk");}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SteeringForWander : Steering
{public float wanderRadius; //徘徊半径public float wanderDistance; //徘徊距离public float wanderJitter; //每秒加到目标的随即位移的最大值public bool isPlanar;private Vector3 desiredVelocity;//预期速度private Vehicle m_vehicle;//获得被操控的AI角色private float maxSpeed;private Vector3 circleTarget;private Vector3 wanderTarget;void Start(){m_vehicle = GetComponent<Vehicle>();maxSpeed = m_vehicle.maxSpeed;isPlanar = m_vehicle.isPlanar;circleTarget = new Vector3(wanderRadius * 0.707f, 0, wanderRadius * 0.707f);  //选取与安全上的一个点作为初始点}public override Vector3 Force(){Vector3 randomDisplacement = new Vector3((Random.value - 0.5f) * 2 * wanderJitter, (Random.value - 0.5f) * 2 * wanderJitter, (Random.value - 0.5f) * 2 * wanderJitter);if (isPlanar)randomDisplacement.y = 0;circleTarget+=randomDisplacement;//将随机位移加到初始点上circleTarget = wanderRadius * circleTarget.normalized;//由于新位置很可能不在圆周上,因此需要投影到圆周上wanderTarget = m_vehicle.velocity.normalized * wanderDistance + circleTarget + transform.position;//之前计算出的值是相对于AI的,需要转换为世界坐标desiredVelocity = (wanderTarget - transform.position).normalized * maxSpeed;return (desiredVelocity - m_vehicle.velocity);}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DrawGizmos : MonoBehaviour
{public float evadeDistance;public Vector3 center;private Vehicle vehicleScript;private float LEADER_BEHIND_DIST;void Start(){vehicleScript = GetComponent<Vehicle>();LEADER_BEHIND_DIST = 2.0f;}void Update(){center = transform.position + vehicleScript.velocity.normalized * LEADER_BEHIND_DIST;}void OnDrawGizoms(){Gizmos.DrawWireSphere(center, evadeDistance);}
}

第三步

创建一个空物体,起名为follersGenerator,用于生成跟随者,并添加脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GenerateBotsForFollowLeader : MonoBehaviour
{public GameObject botPrefab;public GameObject leader;public int botCount;public float minX = -5f;public float maxX = 5.0f;public float minZ = -5.0f;public float maxZ = 5.0f;public float Yvalue = 1.026003f;void Start(){Vector3 spawnPosition;GameObject bot;for(int i = 0; i < botCount; i++){spawnPosition = new Vector3(Random.Range(minX, maxX), Yvalue, Random.Range(minZ, maxZ));//随机产生一个生成位置bot = Instantiate(botPrefab, spawnPosition, Quaternion.identity) as GameObject;bot.GetComponent<SteeringForLeaderFollowing>().leader = leader;bot.GetComponent<SteeringForEvade>().target = leader;bot.GetComponent<SteeringForEvade>().enabled = false;bot.GetComponent<EvadeController>().leader = leader;}}
}

第四步

创建一个方块预设,作为跟随者,挂上下列脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class AILocomotion : Vehicle
{private CharacterController controller;  //AI�Ľ�ɫ������private Rigidbody theRigidbody;private Vector3 moveDistance;//AI��ɫÿ�ε��ƶ�����void Start(){controller = GetComponent<CharacterController>();theRigidbody = GetComponent<Rigidbody>();moveDistance = new Vector3(0, 0, 0);base.Start();//���û����start��������������ij�ʼ��}//������ز�����FixedUpdate�и���void FixedUpdate(){velocity += acceleration * Time.fixedDeltaTime;//�����ٶ�if (velocity.sqrMagnitude > sqrMaxSpeed)   //��������ٶ�velocity = velocity.normalized * maxSpeed;moveDistance = velocity * Time.fixedDeltaTime;if (isPlanar)  {velocity.y = 0;moveDistance.y = 0;}if (controller != null)//����Ѿ�ΪAI��ɫ���ӽ�ɫ����������ô���ý�ɫ������ʹ���ƶ�controller.SimpleMove(velocity);//�����ɫ��û��ɫ��������ҲûRigidbody//����Rigidbody����Ҫ�ɶ���ѧ�ķ�ʽ�������ƶ�else if (theRigidbody == null || !theRigidbody.isKinematic)transform.position += moveDistance;else //��Rigidbody���ƽ�ɫ���˶�theRigidbody.MovePosition(theRigidbody.position+moveDistance);if(velocity.sqrMagnitude>0.00001)//���³�������ٶȴ���һ����ֵ��Ϊ�˷�ֹ������{Vector3 newForward = Vector3.Slerp(transform.forward, velocity, damping * Time.deltaTime);if(isPlanar)newForward.y = 0;transform.forward = newForward;}//�������߶���gameObject.GetComponent<Animation>().Play("walk");}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SteeringForArrive : Steering
{public bool isPlanar = true;public float arrivalDistance = 0.3f;public float characterRadius = 1.2f;public float slowDownDistance;public GameObject target;private Vector3 desiredVelocity;//预期速度private Vehicle m_vehicle;//获得被操控的AI角色private float maxSpeed;void Start(){m_vehicle = GetComponent<Vehicle>();maxSpeed = m_vehicle.maxSpeed;isPlanar = m_vehicle.isPlanar;}public override Vector3 Force(){Vector3 toTarget = target.transform.position - transform.position;Vector3 desiredVelocity;Vector3 returnForce;if (isPlanar)toTarget.y = 0;float distance = toTarget.magnitude;if (distance > slowDownDistance){desiredVelocity = toTarget.normalized * maxSpeed;returnForce = desiredVelocity - m_vehicle.velocity;}else{desiredVelocity = toTarget - m_vehicle.velocity;returnForce = desiredVelocity - m_vehicle.velocity;}return returnForce;}void OnDrawGizmos(){Gizmos.DrawWireSphere(target.transform.position, slowDownDistance);}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(SteeringForArrive))]
public class SteeringForLeaderFollowing : Steering
{public Vector3 target;private Vector3 desiredVelocity;//预期速度private Vehicle m_vehicle;//获得被操控的AI角色private float maxSpeed;private bool isPlanar;public GameObject leader;private Vehicle leaderController;private Vector3 leaderVelocity;private float LEADER_BEHIND_DIST=2.0f;private SteeringForArrive arriveScript;private Vector3 randomOffset;void Start(){m_vehicle = GetComponent<Vehicle>();maxSpeed = m_vehicle.maxSpeed;isPlanar = m_vehicle.isPlanar;leaderController=leader.GetComponent<Vehicle>();arriveScript= GetComponent<SteeringForArrive>();//为抵达行为指定目标点arriveScript.target = new GameObject("arriveTarget");arriveScript.target.transform.position = leader.transform.position;}public override Vector3 Force(){leaderVelocity = leaderController.velocity;target=leader.transform.position+LEADER_BEHIND_DIST*(-leaderVelocity).normalized;//计算目标点arriveScript.target.transform.position = target;return new Vector3(0, 0, 0);}
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Radar : MonoBehaviour
{private Collider[] colliders;//碰撞体的组数private float timer = 0;//计时器public List<GameObject> neighbors;public float checkInterval = 0.3f;//设置检测的时间间隔public float detectRadius = 10f;//设置邻域半径public LayerMask layersChecked;//设置检测哪一层的游戏对象void Start(){neighbors = new List<GameObject>();}void Update(){timer += Time.deltaTime;if(timer > checkInterval){neighbors.Clear();colliders = Physics.OverlapSphere(transform.position, detectRadius, layersChecked);//查找当前AI角色邻域内的所有碰撞体for(int i = 0; i < colliders.Length; i++)//对于每个检测到的碰撞体,获取Vehicle组件,并且加入邻居列表钟{if (colliders[i].GetComponent<Vehicle>())neighbors.Add(colliders[i].gameObject);}timer = 0;}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SteeringForSeparation : Steering
{public float comforDistance = 1;//可接受的距离public float multiplierInsideComfortDistance = 2;//当AI角色与邻居距离过近时的惩罚因子public override Vector3 Force(){Vector3 steeringForce = new Vector3(0, 0, 0);foreach(GameObject s in GetComponent<Radar>().neighbors)//遍历这个AI角色的邻居列表中的每个邻居{if ((s != null) && (s != this.gameObject)){Vector3 toNeighbor = transform.position - s.transform.position;//计算当前AI角色与邻居s之间的距离float length=toNeighbor.magnitude;steeringForce += toNeighbor.normalized / length;//计算这个邻居引起的操控力if (length < comforDistance)steeringForce *= multiplierInsideComfortDistance;}}return steeringForce;}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SteeringForEvade :Steering
{public GameObject target;private Vector3 desiredVelocity;//预期速度private Vehicle m_vehicle;//获得被操控的AI角色private float maxSpeed;void Start(){m_vehicle = GetComponent<Vehicle>();maxSpeed = m_vehicle.maxSpeed;}public override Vector3 Force(){Vector3 toTarget = target.transform.position - transform.position;float lookaheadTime = toTarget.magnitude / (maxSpeed + target.GetComponent<Vehicle>().velocity.magnitude);//向前预测的时间desiredVelocity = (transform.position - (target.transform.position+target.GetComponent<Vehicle>().velocity*lookaheadTime)).normalized * maxSpeed;return (desiredVelocity - m_vehicle.velocity);}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EvadeController : MonoBehaviour
{public GameObject leader;private Vehicle leaderLocomotion;private Vehicle m_vehicle;private bool isPlanar;private Vector3 leaderAhead;private float LEADER_BEHIND_DIST;private Vector3 dist;public float evadeDistance;private float sqrEvadeDistance;private SteeringForEvade evadeScript;void Start(){leaderLocomotion = leader.GetComponent<Vehicle>();evadeScript= GetComponent<SteeringForEvade>();m_vehicle= GetComponent<Vehicle>();isPlanar=m_vehicle.isPlanar;LEADER_BEHIND_DIST = 2.0f;sqrEvadeDistance=sqrEvadeDistance*sqrEvadeDistance;}void Update(){leaderAhead=leader.transform.position+leaderLocomotion.velocity.normalized*LEADER_BEHIND_DIST;  //计算领队前方的一个点dist = transform.position - leaderAhead;if (isPlanar){dist.y = 0;}if(dist.sqrMagnitude < sqrEvadeDistance){evadeScript.enabled = true;Debug.DrawLine(transform.position, leader.transform.position);}else{evadeScript.enabled = false;}}
}

收尾

最后再给各个角色装上刚体,设置好Leader等参数就可以了。

很多行为我们都可以通过设定规则来实现,可能乍一看行为很难摸索,但慢慢分析出其中的规则并逐一实现后,问题往往就会被解决

这篇关于UnityAI——动物迁徙中的跟随实现实例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/383183

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现