群组行为指的是多个对象组队同时进行的情况。每个boid需满足分离,队列,凝聚三个基本的规则。

分离:群组中的每个个体都与相邻的个体保持一定的距离。

队列:群组以相同的速度,向相同的方向移动。

凝聚:与群组的中心保持最小距离。

参见:http://www.red3d.com/cwr/boids/

结构:

控制器:即头鸟下有controller类来控制自身的移动。

个体成员:单独的个体,通过引用控制器的位置信息来产生群组跟随的效果。

群组中的个体:

 using UnityEngine;
using System.Collections; /// <summary>
/// 该类是对群体中的每个个体行为的约束,即单个的鸟
/// </summary>
public class UnityFlock : MonoBehaviour
{ //最小速度,转向速度,随机频率,随机力
public float minSpeed = 20.0f;
public float turnSpeed = 20.0f;
public float randomFreq = 20.0f;
public float randomForce = 20.0f; //队列属性 :向心力,向心区间,吸引力
public float toOriginForce = 50.0f;
public float toOriginRange = 100.0f; public float gravity = 2.0f; //分离属性:规避力,规避半径
public float avoidanceForce = 20.0f;
public float avoidanceRadius = 50.0f; //凝聚属性:追随速度,追随半径(相对于领导者即头鸟)
public float followVelocity = 4.0f;
public float followRadius = 40.0f; //控制单个个体运动的属性:父对象即头鸟,速度,归一化速度,随机推力,父对象的推力。。。
private Transform origin;
private Vector3 velocity;
private Vector3 normalizedVelicity;
private Vector3 randomPush;
private Vector3 originPush;
private Transform[] objects;
private UnityFlock[] otherFlocks;//其他个体集合
private Transform transformCompont; // Use this for initialization
void Start ()
{
randomFreq = 1.0f/randomFreq;//获取随机变化的频率
//设置父节点为origin
origin = transform.parent; transformCompont = transform; //临时组件数组
Component[] tempFlocks = null; if (transform.parent)
{
tempFlocks = transform.parent.GetComponentsInChildren<UnityFlock>();
} objects=new Transform[tempFlocks.Length];
otherFlocks=new UnityFlock[tempFlocks.Length]; //将群体的位置信息和群体加载到数组
for (int i = ; i < tempFlocks.Length; i++)
{
objects[i] = tempFlocks[i].transform;
otherFlocks[i] = (UnityFlock)tempFlocks[i];
} transform.parent = null; StartCoroutine(UpdateRandom());
} //基于randomFreq的频率来更新randompush的频率
IEnumerator UpdateRandom()
{
while (true)
{
randomPush = Random.insideUnitSphere*randomForce;//Random.insideUnitSphere随机返回单位球体类一点坐标,配合随机力度来跟新randomPush
yield return new WaitForSeconds(randomFreq+Random.Range(-randomFreq/,randomFreq/));//依据随机频率在一定时间分为类变换randomPush
}
} // Update is called once per frame
void Update ()
{
float speed = velocity.magnitude;
Vector3 avgVelocity = Vector3.zero;
Vector3 avgPosition = Vector3.zero;
float count = ;
float f = 0.0f;
float d = 0.0f;
Vector3 myPosition = transformCompont.position;
Vector3 forceV;
Vector3 toAvg;
Vector3 wantedVel; for (int i = ; i < objects.Length; i++)
{
Transform transform = objects[i];
if (transform != transformCompont)
{
Vector3 otherPositon = transform.position; //平均位置来计算聚合
avgPosition += otherPositon;
count++; //从其他群体到这个的向量
forceV = myPosition - otherPositon; //上面向量的长度
d = forceV.magnitude; //如果向量长度比规避半径小的话,则加大推力
if (d < followRadius)
{
//如果当前的向量长度小于规定的逃离半径的话,则基于 逃离半径计算对象的速度
if (d > )
{
f = 1.0f - (d/avoidanceRadius);
avgVelocity += (forceV / d) * f * avoidanceForce;
//向量除以它的模得到自己的单位向量
} } //保持与头儿的距离
f = d/followRadius;
UnityFlock otherSealgull = otherFlocks[i]; //标准化otherSealgul的速度来获取移动的方向,接下来设置一个新的速度
avgVelocity += otherSealgull.normalizedVelicity * f *followVelocity; }
} if (count > )
{
//得到平均速度
avgVelocity /= count;
//获得平均位置与对象间的向量
toAvg = (avgPosition/count) - myPosition;
}
else
{
toAvg = Vector3.zero;
} //
forceV = origin.position - myPosition;
d = forceV.magnitude;
f = d/toOriginRange;
//
if (d > )
originPush = (forceV/d)*f*toOriginForce;
if (speed < minSpeed && speed > )
velocity = (velocity/speed)*minSpeed; wantedVel = velocity; //最终速度
wantedVel -= wantedVel*Time.deltaTime;
wantedVel += randomPush*Time.deltaTime;
wantedVel += originPush*Time.deltaTime;
wantedVel += avgVelocity*Time.deltaTime;
wantedVel += toAvg.normalized*gravity*Time.deltaTime; //调整速度使之转向最终速度
velocity = Vector3.RotateTowards(velocity, wantedVel,turnSpeed*Time.deltaTime, 100.00f); transformCompont.rotation = Quaternion.LookRotation(velocity); //移动对象
transformCompont.Translate(velocity*Time.deltaTime,Space.World); //跟新标准化向量的引用
normalizedVelicity = velocity.normalized;
} }

群组控制器(头鸟):

 using UnityEngine;
using System.Collections; /// <summary>
/// 头鸟决定飞行的整体方向,在unityflock中被origin引用
/// </summary>
public class UnityFlockController : MonoBehaviour
{ public Vector3 offset;//偏移
public Vector3 bound;//范围
public float speed = 100.0f; private Vector3 initialPosition;
private Vector3 nextMovementPoint; // // Use this for initialization
void Start ()
{
initialPosition = transform.position;
CalculateNextMovementPoint();
} // Update is called once per frame
void Update () {
transform.Translate(Vector3.forward*speed*Time.deltaTime);
transform.rotation=Quaternion.Slerp(transform.rotation,Quaternion.LookRotation(nextMovementPoint-transform.position),1.0f*Time.deltaTime );//调整飞行角度 if(Vector3.Distance(nextMovementPoint,transform.position)<=10.0f)
{
CalculateNextMovementPoint();
} } void CalculateNextMovementPoint()
{
float posx = Random.Range(initialPosition.x - bound.x, initialPosition.x + bound.x);
float posy = Random.Range(initialPosition.y - bound.y, initialPosition.y + bound.y);
float posz = Random.Range(initialPosition.z - bound.z, initialPosition.z + bound.z); nextMovementPoint = initialPosition + new Vector3(posx, posy, posz);
}
}

效果:

游戏AI之群组行为的更多相关文章

  1. 如何建立一个完整的游戏AI

    http://blog.friskit.me/2012/04/how-to-build-a-perfect-game-ai/ 人工智能(Artificial Intelligence)在游戏中使用已经 ...

  2. 游戏AI之初步介绍(0)

    目录 游戏AI是什么? 游戏AI和理论AI 智能的假象 (更新)游戏AI和机器学习 介绍一些游戏AI 4X游戏AI <求生之路>系列 角色扮演/沙盒游戏中的NPC 游戏AI 需要学些什么? ...

  3. 游戏AI玩伴,是“神队友”还是“猪队友”?

    “一代英豪”暴雪迎来了自己的暴风雪. 2月13日,动视暴雪公布了2018年全年财报.财报显示,暴雪第四季度营业收入仅为28.4亿美元,低于华尔街分析师预期的30.4亿美元.在公布了财报业绩后,该公司又 ...

  4. Omad群组部署、依赖部署一键解决

    本文来自网易云社区 作者:李培斌 前言 基于omad部署平台实现一键部署的实践已有很多成功的经验,杭研QA的技术先锋们也在ks圈里有很多不同的文章去阐述关于这类需求的实现和思路,当然包括我们金融事业部 ...

  5. 游戏AI的生命力源自哪里?为你揭开MOBA AI的秘密!

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由wataloo发表在专栏wataloo的试验田 1 设计概要 1.1 设计原则和目的 英雄AI的目的主要有: 1.新手过渡局,让玩家刚 ...

  6. 游戏AI系列内容 咋样才能做个有意思的AI呢

    游戏AI系列内容 咋样才能做个有意思的AI呢 写在前面的话 怪物AI怎么才能做的比较有意思.其实这个命题有点大,我作为一个仅仅进入游戏行业两年接触怪物AI还不到一年的程序员来说,来谈这个话题,我想我是 ...

  7. 微信小程序的机会在于重新理解群组与二维码

    历时一年,唯一一个尚未发布就获得Pony Ma与Allen Zhang站台的产品:微信小程序,将于2017年1月9日正式上线了.我很期待.唯一要警惕的是:防止长考出臭棋. 在上线前夕,我对于如何借助小 ...

  8. 趣说游戏AI开发:对状态机的褒扬和批判

    0x00 前言 因为临近年关工作繁忙,已经有一段时间没有更新博客了.到了元旦终于有时间来写点东西,既是积累也是分享.如题目所示,本文要来聊一聊在游戏开发中经常会涉及到的话题--游戏AI.设计游戏AI的 ...

  9. 使用行为树(Behavior Tree)实现游戏AI

    ——————————————————————— 谈到游戏AI,很明显智能体拥有的知识条目越多,便显得更智能,但维护庞大数量的知识条目是个噩梦:使用有限状态机(FSM),分层有限状态机(HFSM),决策 ...

随机推荐

  1. MVC学习十:MVC 特性作用和MVC 验证

    根据代码分析特性用处 [DisplayName("学员名")] [DataType(DataType.Text)] [StringLength(,ErrorMessage=&quo ...

  2. 如何搭建私人的ghost博客

    可参考 http://blog.pandax.me/cjbk/ 此文

  3. 微信小程序中的app文件介绍

    [app] 一.app.json 1.对当前小程序的全局配置 2.页面路径.界面表现.网络超时时间.底部 tab 等 { "pages":[ "pages/index/i ...

  4. BZOJ2037: [Sdoi2008]Sue的小球(区间DP)

    Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 869  Solved: 483[Submit][Status][Discuss] Description ...

  5. Spring的入门学习笔记 (AOP概念及操作+AspectJ)

    AOP概念 1.aop:面向切面(方面)编程,扩展功能不通过源代码实现 2.采用横向抽取机制,取代了传统的纵向继承重复代码 AOP原理 假设现有 public class User{ //添加用户方法 ...

  6. linux 安全防护

    一.禁止ROOT用户远程登录 linux中root用户是超级管理员,可以针对root用户暴力破解密码,这样很不安全,工作中我们一般禁止root用户直接远程登陆,开设一个或多个普通用户,只允许登陆普通用 ...

  7. ssh登录等待时间超时问题的解决

    最近使用ssh登录服务器时,发现许多服务器会报告等待时间超时的错误,通过网上查找发现是由于ssh中的配置中开启了DNS反查的功能,导致在反查过程中消耗了很长的时间,现将解决方法总结如下: 使用root ...

  8. 原型 - 实现自己的jQuery

    每个第一次使用jq的开发者都感到惊叹,jq的$太神奇了,究竟是怎么做到的使用$控制dom 赞叹前人之余,探究其本源才是前端开发者应该做的事,社区常常说,不要重复造轮子, 可是啊,连轮子都造不出来,又怎 ...

  9. S3C2440启动程序运行过程

    s3c2440有两种启动方式,一种Nor flash 启动,一种Nand flash 启动. 由于NAND FLASH是接在NAND FLASH控制器上而不是系统总线上,所以没有在S3C2440A的8 ...

  10. (杭电 1702)ACboy needs your help again!

    ACboy needs your help again! Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Jav ...