游戏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),决策 ...
随机推荐
- Hello, GitHub!
GitHub作为版本控制的软件,我决定重新系统学习这个东西,毕竟以前都是fork.clone... 1. 理解Git思维 首先呢,我一开始就被GitHub和Git两个东西搞昏了,所以有必要理解二者的关 ...
- 【题解】洛谷P1941 [NOIP2014TG] 飞扬的小鸟(背包DP)
次元传送门:洛谷P1941 思路 从题意可知 在每个单位时间内 可以无限地向上飞 但是只能向下掉一次 所以我们可以考虑运用背包解决这道题 上升时 用完全背包 下降时 用01背包 设f[x][y]为在坐 ...
- HDU 1273 漫步森林(数学 找规律)
传送门: http://acm.hdu.edu.cn/showproblem.php?pid=1273 漫步森林 Time Limit: 2000/1000 MS (Java/Others) M ...
- Spring整合Mybatis SQL语句的输出
[1.修改Spring-Mybatis] <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSes ...
- UICollectionView reloadItemsAtIndexPaths时 报错
在刷新下载进度时 Xcode报错误: Terminating app due to uncaught exception 'NSInternalInconsistencyException', rea ...
- 并发之AtomicIntegerFieldUpdater
基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新.此类用于原子数据结构,该结构中同一节点的几个字段都独立受原子更新控制. 先来看一段代码: package autom ...
- Codeforces Round #483 (Div. 2)C题
C. Finite or not? time limit per test 1 second memory limit per test 256 megabytes input standard in ...
- 纯js轮播图练习-1
偶尔练习,看视频自己学着做个简单的纯JS轮播. 简单的纯js轮播图练习-1. 样子就是上面图片那样,先不管好不好看,主要是学会运用和理解轮播的原理 掌握核心的理论知识和技术的操作,其他的都可以在这个基 ...
- 【JavaWeb】从零实现用户登录
1.数据库预备 1.1 SQL 创建数据库 create database db; 创建表 create table userInfo( id int primary key , name ), pa ...
- sea.js模块化工具
sea.js 一. sea.js向全局中引入了两个变量seajs.define: 1.seajs用加载文件 seajs.use(deps,callback)异步引入入口模块 路径要以sea.js文件所 ...