1987年Craig W.Reynolds发表一篇名为《鸟群、牧群、鱼群:分布式行为模式》的论文,描述了一种非常简单的、以面向对象思维模拟群体类行为的方法,称之为 Boids ,Boids 采用了三个核心的规则:

  • 排斥性:避免与群体内邻近个体发生碰撞
  • 同向性:趋向与邻近的个体采用相同的速度方向
  • 凝聚向心性:向邻近个体的平均位置靠近

由此我们采用Unity来实现算法并演示,演示结果:

制作思路

每个boid对象,每帧都有2个关键的表:与该boid邻近的boids的表及与该boid最近的boids的表。根据两个表求得一些确定该boid位置、方向、速度的因素(例如其他boids的平均速度,其他boids的平均位置,该boid与其他boid的平均距离等),根据所提出的三规则,设置各影响因素的权重比例,最终所有影响因素加和成为确定的、该boid下一帧的方向、位置、速度。

Boid模型的创建与配置

  1. 创建空对象取名Boid,再创建其空对象子物体,取名Fuselage

    • Fuselage->position(0,0,0),Rotation(7.5,0,0),Scale(0.5,0.5,2)
  2. 创建一个Cube作为Fuselage子物体,移除 Box Collider,并添加拖尾渲染器 TrailRenderer
    • Cube->position(0,0,0),Rotation(45,0,45),Scale(1,1,1)
    • Component->Effects->TrailRenderer,选择材质Defualt-Particle(Material),Time值0.5,End Witdth值0.25
  3. 复制Fuselage创建另一个名为Wing的组件添加到Boid,两个都属于Boid的子物体
  4. 修改主相机位置到顶视图大范围
  5. 将boid设为预制体,boid 模型制作完毕

脚本配置

  • BoidSpawner.cs 绑定于主相机
  • Boid.cs 绑定于预制体Boid上
  • 主相机检视面板中,设定 boidPrefab 变量为预制体 boid

项目地址:Boids

/*-------BoidSpawner.cs-------*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BoidSpawner : MonoBehaviour
{//BoidSpawner 的单例模式,只允许存在BoidSpawner的一个实例,所以存放在静态变量S中static public BoidSpawner S;//配置参数,调整Boid对象的行为public int numBoids = 100;                  //boid 的个数public GameObject boidPrefab;               //boid 在unity中的预制体public float spawnRadius = 100f;            //实例化 boid 的位置范围public float spawnVelcoty = 10f;            //boid 的速度public float minVelocity = 0f;public float maxVelocity = 30f;public float nearDist = 30f;                //判定为附近的 boid 的最小范围值public float collisionDist = 5f;            //判定为最近的 boid 的最小范围值(具有碰撞风险)public float velocityMatchingAmt = 0.01f;   //与 附近的boid 的平均速度 乘数(影响新速度)public float flockCenteringAmt = 0.15f;     //与 附近的boid 的平均三维间距 乘数(影响新速度)public float collisionAvoidanceAmt = -0.5f; //与 最近的boid 的平均三维间距 乘数(影响新速度)public float mouseAtrractionAmt = 0.01f;    //当 鼠标光标距离 过大时,与其间距的 乘数(影响新速度)public float mouseAvoidanceAmt = 0.75f;     //当 鼠标光标距离 过小时,与其间距的 乘数(影响新速度)public float mouseAvoiddanceDsit = 15f;public float velocityLerpAmt = 0.25f;       //线性插值法计算新速度的 乘数public bool ______________;public Vector3 mousePos;        //鼠标光标位置private void Start(){//设置单例变量S为BoidSpawner的当前实例S = this;//初始化NumBoids(当前为100)个Boidsfor (int i = 0; i < numBoids; i++)Instantiate(boidPrefab);}private void LateUpdate(){//读取鼠标光标位置Vector3 mousePos2d = new Vector3(Input.mousePosition.x, Input.mousePosition.y, this.transform.position.y);//从世界空间到屏幕空间变换位置mousePos = this.GetComponent<Camera>().ScreenToWorldPoint(mousePos2d);}
}/*-------Boid.cs-------*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Boid : MonoBehaviour
{static public List<Boid> boids;     //实例化Boid 的表public Vector3 velocity;        //当前速度public Vector3 newVelocity;     //下一帧中的速度public Vector3 newPosition;     //下一帧中的位置public List<Boid> neighbors;        //附近所有的 Boid 的表public List<Boid> collisionRisks;   //距离过近的所有 Boid 的表(具有碰撞风险,需要处理)public Boid closest;                //最近的 Boid//初始化Boidprivate void Awake(){//如果List变量boids未定义,则对其进行定义if (boids == null)boids = new List<Boid>();//向Boids List 中添加Boidboids.Add(this);//为当前Boid实例提供一个随机的位置和速度//实例化的boid位置在 半径为 1*spawnRadius 的球形范围内Vector3 randPos = Random.insideUnitSphere * BoidSpawner.S.spawnRadius;//只让Boid在xz平面上移动,并设定起始坐标randPos.y = 0;this.transform.position = randPos;//Random.onUnitSphere 返回 一个半径为1的 球体表面的点velocity = Random.onUnitSphere;velocity *= BoidSpawner.S.spawnVelcoty;//初始化两个Listneighbors = new List<Boid>();collisionRisks = new List<Boid>();//让this.transform成为Boid游戏对象的子对象this.transform.parent = GameObject.Find("Boids").transform;//给Boid设置一个随机的颜色Color randColor = Color.black;//设置颜色的颜色要 较深,非透明while (randColor.r + randColor.g + randColor.b < 1.0f)randColor = new Color(Random.value, Random.value, Random.value);//渲染 boidRenderer[] rends = gameObject.GetComponentsInChildren<Renderer>();foreach (Renderer r in rends)r.material.color = randColor;}private void Update(){//获取到 当前boid 附近所有的Boids 的表List<Boid> neighbors = GetNeighbors(this);//使用当前位置和速度初始化新位置和新速度newVelocity = velocity;newPosition = this.transform.position;//速度匹配//取得于 当前Boid 的速度接近的 所有邻近Boid对象 的平均速度Vector3 neighborVel = GetAverageVelocity(neighbors);//将 新速度 += 邻近boid的平均速度*velocityMatchingAmtnewVelocity += neighborVel * BoidSpawner.S.velocityMatchingAmt;/*凝聚向心性:使 当前boid 向 邻近Boid对象 的中心 移动*///取得于 当前Boid 的三位坐标接近的 所有邻近Boid对象 的平均三位间距Vector3 neighborCenterOffset = GetAveragePosition(neighbors) - this.transform.position;//将 新速度 += 邻近boid的平均间距*flockCenteringAmtnewVelocity += neighborCenterOffset * BoidSpawner.S.flockCenteringAmt;/*排斥性:避免撞到 邻近的Boid*/Vector3 dist;if (collisionRisks.Count > 0)   //处理 最近的boid 表{//取得 最近的所有boid 的平均位置Vector3 collisionAveragePos = GetAveragePosition(collisionRisks);dist = collisionAveragePos - this.transform.position;//将 新速度 += 与最近boid的平均间距*flockCenteringAmtnewVelocity += dist * BoidSpawner.S.collisionAvoidanceAmt;}//跟随鼠标光标:无论距离多远都向鼠标光标移动dist = BoidSpawner.S.mousePos - this.transform.position;//若距离鼠标光标太远,则靠近;反之离开(修改新速度)if (dist.magnitude > BoidSpawner.S.mouseAvoiddanceDsit)newVelocity += dist * BoidSpawner.S.mouseAtrractionAmt;elsenewVelocity -= dist.normalized * BoidSpawner.S.mouseAvoidanceAmt;//至此在Update()内 确定了 新速度和新位置,需要在后续LateUpdate()内应用//一般都是Update()内确定参数,在LateUpdate()内实现移动}private void LateUpdate(){//使用线性插值法//基于计算出的新速度 进而修改 当前速度velocity = (1 - BoidSpawner.S.velocityLerpAmt) * velocity + BoidSpawner.S.velocityLerpAmt * newVelocity;//确保 速度值 在上下限范围内(超过范围就设定为范围值)if (velocity.magnitude > BoidSpawner.S.maxVelocity)velocity = velocity.normalized * BoidSpawner.S.maxVelocity;if (velocity.magnitude < BoidSpawner.S.minVelocity)velocity = velocity.normalized * BoidSpawner.S.minVelocity;//确定新位置(附加新方向),相当于1s移动 velocity 的距离newPosition = this.transform.position + velocity * Time.deltaTime;//将所有对象限制在XZ平面//修改当前boid的方向:从原有位置看向新位置newPositionthis.transform.LookAt(newPosition);//position移动方式,移动到新位置this.transform.position = newPosition;}//查找那些Boid距离当前Boid距离足够近,可以被当作附近对象public List<Boid> GetNeighbors(Boid boi){float closesDist = float.MaxValue;  //最小间距,MaxValue 为浮点数的最大值Vector3 delta;              //当前 boid 与其他某个 boid 的三维间距 float dist;                 //三位间距转换为的 实数间距neighbors.Clear();          //清理上次表的数据collisionRisks.Clear();     //清理上次表的数据//遍历目前所有的 boid,依据设定的范围值筛选出 附近的boid 与 最近的boid 于各自表中foreach (Boid b in boids){if (b == boi)   //跳过自身continue;delta = b.transform.position - boi.transform.position;  //遍历到的 b 与当前持有的 boi(都为boid) 的三维间距dist = delta.magnitude;     //实数间距if (dist < closesDist){closesDist = dist;      //更新最小间距closest = b;            //更新最近的 boid 为 b}if (dist < BoidSpawner.S.nearDist)  //处在附近的 boid 范围neighbors.Add(b);if (dist < BoidSpawner.S.collisionDist) //处在最近的 boid 范围(有碰撞风险)collisionRisks.Add(b);}if (neighbors.Count == 0)   //若没有其他满足邻近范围的boid,则将自身boid纳入附近的boid表中neighbors.Add(closest);return (neighbors);}//获取 List<Boid>当中 所有Boid 的平均位置public Vector3 GetAveragePosition(List<Boid> someBoids){Vector3 sum = Vector3.zero;foreach (Boid b in someBoids)sum += b.transform.position;Vector3 center = sum / someBoids.Count;return (center);}//获取 List<Boid> 当中 所有Boid 的平均速度public Vector3 GetAverageVelocity(List<Boid> someBoids){Vector3 sum = Vector3.zero;foreach (Boid b in someBoids)sum += b.velocity;Vector3 avg = sum / someBoids.Count;return (avg);}
}

参考

《游戏设计、原型与开发》 - Jeremy Gibson

转载于:https://www.cnblogs.com/SouthBegonia/p/10975851.html

Unity项目 - Boids集群模拟算法相关推荐

  1. p2p 文件服务器集群,基于云计算P2P流媒体服务器集群部署算法.doc

    基于云计算P2P流媒体服务器集群部署算法 基于云计算P2P流媒体服务器集群部署算法 摘 要: 针对云计算数据中心网络(DCN)环境下,P2P流媒体服务器集群部署引起的较高带宽占用问题,提出了一种基于云 ...

  2. p2p 文件服务器集群,基于云计算的P2P流媒体服务器集群部署算法.doc

    基于云计算的P2P流媒体服务器集群部署算法.doc 基于云计算的P2P流媒体服务器集群部署算法 摘 要: 针对云计算数据中心网络(DCN)环境下,P2P流媒体服务器集群部署引起的较高带宽占用问题,提出 ...

  3. nginx实现项目服务器集群管理案例和正反代理的解释

    本博客整体内容包括 nginx静态服务器 了解nginx nginx具体实现的功能 nginx简单使用 nginx的代理 正向代理 反向代理 反向代理简单案例 反向代理解决跨域问题 nginx 动静分 ...

  4. k8s管理java项目_Kubernetes集群部署项目-部署Java项目(推送镜像

    Kubernetes(简称k8s)是谷歌开源的一套容器化集群管理系统,当下已被众多大厂及中小企业采用,容器化技术是目前的大势所趋. 本套教程k8s版本升级为最新版1.18.0,内容由浅入深,且更加深化 ...

  5. 算法高级(20)-集群容错算法

    一.集群容错场景 集群服务调用失败后,服务框架需要能够在底层自动容错,容错策略很多,分别适用于不同场景. 在分布式服务框架中,业务消费者不需要了解服务提供者的具体位置,它发起的调用请求也不包含服务提供 ...

  6. 1,多路径 2,nfs cifs 3,集群wrr算法,路由规则 4,haproxy 5, keepalived

    第一天 李子岩  cluster hpc lb ha top500 cpu+gpu(ibm,nvidia) 1PB = 1024T  10GBps 04-11 linux python (14-16) ...

  7. 网络云盘项目——FastDFS集群部署

    一.本文目的 本项目分为6篇博客文章完成: 1.项目总体介绍:https://blog.csdn.net/qq_41453285/article/details/107871393. 2.Redis部 ...

  8. 安装hadoop集群模拟大数据集群踩到的坑(一)

    1.安装虚拟机 使用VMWARE安装虚拟机CentOS7时候踩到的第一个坑,当安装完毕CentOS7的时候,使用vmware workstation pro 14进行启动虚拟机,win10系统有概率蓝 ...

  9. 摩拜单车项目05-Redis集群

    文章目录 业务 将kafka中的数据存入Hdfs Flume方案 优化方案 数据处理的流程 指标计算 实时指标 离线指标 保修指标 能计算哪些指标 活动参与 Redis集群 主从结构示意图 一致性ha ...

最新文章

  1. ASP中Randomize随机函数的使用
  2. __cdecl __stdcall区别-转
  3. 浅谈我的销售体会(一)
  4. L - 土拨鼠掷鼬鼠(二分查找)
  5. 营销推广中心设计(一)营销架构与策略
  6. Blazor带我重玩前端(五)
  7. jzoj4208-线段树什么的最讨厌了【dfs】
  8. matlab中有哪些输出函数,MATLAB中查找并输出的函数有什么
  9. AI+社交,快手商业化落地之道
  10. win10添加环境变量后没用_python 学习之在 win10 下安装 Anaconda
  11. 极通EWEBS 常见问题及其解决办法
  12. 报错解决:Failed to load config “react-app“ to extend from.
  13. 浏览器兼容video视频播放的多种方法
  14. 如何将Python文件.py打包成.exe可执行程序(最简教程吗)
  15. 用位运算来代替乘法、除法和取余的方式
  16. python02 函数 等额本金贷款
  17. Chrome浏览器设置网站前自动加https
  18. Raytheon 业务架构:J. Bryan Lail 访谈记录
  19. 求职季找工作心得与应聘经验分享(二)
  20. PandoraBox 扩大overlay容量

热门文章

  1. linux中sem_wait函数,semwait sem_wait的函数说明
  2. 写个小爬虫:camera360一拍即传照片直播平台 照片一键下载
  3. 【线激光扫描三维成像】原理介绍
  4. linux有端口找不到进程,linux查看端口和进程
  5. UCOS2系统内核讲述(三)_TCB任务控制块
  6. 【scratch案例教学】Scratch多彩的海底世界 scratch编程案例教学 scratch创意编程 少儿编程教案
  7. 基于STM32F103C8T6的高速DMA传输多通道ADC数据
  8. MAUI 入门教程系列(1.框架简介)
  9. webgl中加载模型要求以及优化方案
  10. 中断原理及WDT驱动编程