游戏AI之群组行为
群组行为指的是多个对象组队同时进行的情况。每个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之群组行为的更多相关文章
- 如何建立一个完整的游戏AI
http://blog.friskit.me/2012/04/how-to-build-a-perfect-game-ai/ 人工智能(Artificial Intelligence)在游戏中使用已经 ...
- 游戏AI之初步介绍(0)
目录 游戏AI是什么? 游戏AI和理论AI 智能的假象 (更新)游戏AI和机器学习 介绍一些游戏AI 4X游戏AI <求生之路>系列 角色扮演/沙盒游戏中的NPC 游戏AI 需要学些什么? ...
- 游戏AI玩伴,是“神队友”还是“猪队友”?
“一代英豪”暴雪迎来了自己的暴风雪. 2月13日,动视暴雪公布了2018年全年财报.财报显示,暴雪第四季度营业收入仅为28.4亿美元,低于华尔街分析师预期的30.4亿美元.在公布了财报业绩后,该公司又 ...
- Omad群组部署、依赖部署一键解决
本文来自网易云社区 作者:李培斌 前言 基于omad部署平台实现一键部署的实践已有很多成功的经验,杭研QA的技术先锋们也在ks圈里有很多不同的文章去阐述关于这类需求的实现和思路,当然包括我们金融事业部 ...
- 游戏AI的生命力源自哪里?为你揭开MOBA AI的秘密!
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由wataloo发表在专栏wataloo的试验田 1 设计概要 1.1 设计原则和目的 英雄AI的目的主要有: 1.新手过渡局,让玩家刚 ...
- 游戏AI系列内容 咋样才能做个有意思的AI呢
游戏AI系列内容 咋样才能做个有意思的AI呢 写在前面的话 怪物AI怎么才能做的比较有意思.其实这个命题有点大,我作为一个仅仅进入游戏行业两年接触怪物AI还不到一年的程序员来说,来谈这个话题,我想我是 ...
- 微信小程序的机会在于重新理解群组与二维码
历时一年,唯一一个尚未发布就获得Pony Ma与Allen Zhang站台的产品:微信小程序,将于2017年1月9日正式上线了.我很期待.唯一要警惕的是:防止长考出臭棋. 在上线前夕,我对于如何借助小 ...
- 趣说游戏AI开发:对状态机的褒扬和批判
0x00 前言 因为临近年关工作繁忙,已经有一段时间没有更新博客了.到了元旦终于有时间来写点东西,既是积累也是分享.如题目所示,本文要来聊一聊在游戏开发中经常会涉及到的话题--游戏AI.设计游戏AI的 ...
- 使用行为树(Behavior Tree)实现游戏AI
——————————————————————— 谈到游戏AI,很明显智能体拥有的知识条目越多,便显得更智能,但维护庞大数量的知识条目是个噩梦:使用有限状态机(FSM),分层有限状态机(HFSM),决策 ...
随机推荐
- SQLServer如何批量替换某一列中的某个字符串
我们在开发系统的时候经常会碰到类似如下这样的情况:比如我有一张数据表 假如我现在要把红圈中这列的的http://www.mylanqiu.com/ 这个字符串批量替换成mylanqiu 这个字符串,这 ...
- mysql 8.0.12 日常出错
最近不知道怎么回事,数据库老是会输出一个: [Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and co ...
- 集合,ArrayList练习
import java.util.ArrayList; import java.util.Iterator; public class ArrayListTest { public static vo ...
- 关于js复制的那些事儿
window.clipboardData的作用是在页面上将需要的东西复制到剪贴板上,提供了对于预定义的剪贴板格式的访问,以便在编辑操作中使用. 三个方法 (1)clearData(sDataForma ...
- go加密算法:非对称加密(一)--RSA
椭圆曲线加密__http://blog.51cto.com/11821908/2057726 // MyRas.go package main import ( "crypto/rand&q ...
- WIN10下WNMP开发环境部署
刚刚开始学习PHP时,一直使用phpstudy,后面发现很多东西自己单独配置安装会理解更深刻,所以自己总结了一下windows下开发环境的部署教程. 以前经常在CSDN和博客园看别人的教程,今天才注册 ...
- asp.net在一般处理程序里面操作Session
1.要在一般处理程序中获取其他页面的session值,需要引用名空间: using System.Web.SessionState; 2.然后继承一个接口:IRequiresSessionState ...
- Ubuntu12.04下zxing源码编译
1.下载zxing源码 git clone https://github.com/15903016222/zxing-cpp.git 2.安装依赖工具cmake sudo apt-get instal ...
- 动态链接库函数内的静态变量,奇妙的UNIQUE Bind
title: 动态链接库函数内的静态变量,奇妙的UNIQUE Bind date: 2018-09-28 09:28:22 tags: --- 介绍 模板函数和内敛函数中的静态变量,在跨so中的表现, ...
- Python学习笔记:第3天 字符串的操作
目录 1. python的数据类型 2. int类型的操作 3. bool类型 4. 字符串的操作 5. for循环 1. python的数据类型 int 整数 str 字符串.一般不会用字符串保存大 ...