unity-doc-鱼群模拟

特征

  • 聚集到群组中心
  • 趋向群体的平均方向
  • 避免与其它个体碰撞

实现-通过物理射线

例子运行图
Boid.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using UnityEngine;

namespace WithPhysic
{
/// <summary>
/// 单个鸟实例
/// </summary>
public class Boid
{
BoidSettings settings;

public Vector3 position;
public Vector3 forward;
Vector3 velocity;

Vector3 acceleration;
public Vector3 avgFlockHeading;
public Vector3 avgAvoidanceHeading;
public Vector3 centreOfFlockmates;
public int numPerceivedFlockmates;

Transform cachedTransform;
Transform target;


public void Init (BoidSettings settings, Transform target, GameObject goInst) {
this.target = target;
this.settings = settings;
this.cachedTransform = goInst.transform;

position = cachedTransform.position;
forward = cachedTransform.forward;

float startSpeed = (settings.minSpeed + settings.maxSpeed) / 2;
velocity = cachedTransform.forward * startSpeed;
}

public void UpdateBoid () {
Vector3 acceleration = Vector3.zero;

if (target != null) {
Vector3 offsetToTarget = (target.position - position);
acceleration = SteerTowards (offsetToTarget) * settings.targetWeight;
}

if (numPerceivedFlockmates != 0) {
centreOfFlockmates /= numPerceivedFlockmates;

Vector3 offsetToFlockmatesCentre = (centreOfFlockmates - position);

var alignmentForce = SteerTowards (avgFlockHeading) * settings.alignWeight;
var cohesionForce = SteerTowards (offsetToFlockmatesCentre) * settings.cohesionWeight;
var seperationForce = SteerTowards (avgAvoidanceHeading) * settings.seperateWeight;

acceleration += alignmentForce;
acceleration += cohesionForce;
acceleration += seperationForce;
}

if (IsHeadingForCollision ()) {
Vector3 collisionAvoidDir = ObstacleRays ();
Vector3 collisionAvoidForce = SteerTowards (collisionAvoidDir) * settings.avoidCollisionWeight;
acceleration += collisionAvoidForce;
}

velocity += acceleration * Time.deltaTime;
float speed = velocity.magnitude;
Vector3 dir = velocity / speed;
speed = Mathf.Clamp (speed, settings.minSpeed, settings.maxSpeed);
velocity = dir * speed;

cachedTransform.position += velocity * Time.deltaTime;
cachedTransform.forward = dir;
position = cachedTransform.position;
forward = dir;
}

bool IsHeadingForCollision () {
RaycastHit hit;
if (Physics.SphereCast (position, settings.boundsRadius, forward, out hit, settings.collisionAvoidDst, settings.obstacleMask)) {
return true;
} else { }
return false;
}

Vector3 ObstacleRays () {
Vector3[] rayDirections = Help.DIRECTIONS;

for (int i = 0; i < rayDirections.Length; i++) {
Vector3 dir = cachedTransform.TransformDirection (rayDirections[i]);
Ray ray = new Ray (position, dir);
if (!Physics.SphereCast (ray, settings.boundsRadius, settings.collisionAvoidDst, settings.obstacleMask)) {
return dir;
}
}

return forward;
}

Vector3 SteerTowards (Vector3 vector) {
Vector3 v = vector.normalized * settings.maxSpeed - velocity;
return Vector3.ClampMagnitude (v, settings.maxSteerForce);
}
}
}
BoidsMgr.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using System.Collections.Generic;
using UnityEngine;

namespace WithPhysic
{
public class BoidsMgr
{
private static BoidsMgr _inst;

public static BoidsMgr Inst
{
get
{
if (_inst == null)
{
_inst = new BoidsMgr();
}

return _inst;
}
}


private BoidSettings settings;
private List<Boid> listBoids = new List<Boid>(); // 鸟实例列表

public void Init(BoidSettings settings)
{
this.settings = settings;
}

/// <summary>
/// 创建鸟
/// </summary>
public void CreateBoid(GameObject goInst)
{
Boid boid = new Boid();
boid.Init(settings, null, goInst);

listBoids.Add(boid);
}

public void UpdateBoids()
{
int numBoids = listBoids.Count;
var boidData = new BoidData[numBoids];

for (int i = 0; i < listBoids.Count; i++)
{
listBoids[i].avgFlockHeading = boidData[i].flockHeading;
listBoids[i].centreOfFlockmates = boidData[i].flockCentre;
listBoids[i].avgAvoidanceHeading = boidData[i].avoidanceHeading;
listBoids[i].numPerceivedFlockmates = boidData[i].numFlockmates;

listBoids[i].UpdateBoid();
}
}
}

public struct BoidData {
public Vector3 flockHeading;
public Vector3 flockCentre;
public Vector3 avoidanceHeading;
public int numFlockmates;
}
}

代码地址 https://gitee.com/hahafox_0/unity-example-boids