一:前言
鱼群算法又称为群组行为,比如说走路,但是不能很规律的执行走路,要有人工智能的感觉的去执行走路,更加真实的去模拟现实,并且人与人之间不能拥挤到一起。常用与鸟群,鱼群,人群等
鱼群算法的规则:
——分隔规则: 尽量避免与临近成员过于拥挤
——对准规则: 尽量与临近成员的平均方向一致
——内聚规则: 尽量朝临近成员的中心移动
二:效果演示
三:代码
——组的总控制器
using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// 组的总控制器(挂载到组长身上,可以是玩家也可以是要跟随的物体)
/// </summary>
public class GroupController : MonoBehaviour
{
private static List<GroupController> groups;//所有组
[Header("组中成员的层")]
public LayerMask mask;
[Header("组中成员的ID")]
public int groupID = 0;
[Header("组中成员始终保持的距离")]
public float keepDis;
[Header("组中成员始终保持的距离的权重")]
public float keepWeight;
[Header("多少距离算离得太近")]
public float targetCloseDistance;
[Header("组中成员停止移动的距离")]
public float stopDis;
/// <summary>
/// 得到成员属于哪个组
/// </summary>
/// <param name="index">成员ID</param>
/// <returns></returns>
public static GroupController GetGroup(int index)
{
if (groups == null)
{
groups = new List<GroupController>(FindObjectsOfType(typeof(GroupController)) as GroupController[]);
}
for (int i = 0; i < groups.Count; i++)
{
if (groups[i].groupID == index)
{
return groups[i];
}
}
throw new System.Exception("没有找到相同ID的组");
}
}
——控制组中的每个成员自身
using UnityEngine;
/// <summary>
/// 挂载到组中的每个成员身上
/// </summary>
public class GroupMember : MonoBehaviour
{
private GroupController myGroup;//当前成员的GroupController组件
//速度和移动相关参数
private float targetSpeed;
private float speed;
private float currentSpeed;
private Vector3 myMovement;
[Header("属于的组ID")]
public int groupId;
[Header("移动速度")]
public float moveSpeed;
[Header("旋转速度")]
public float rotateSpeed;
private void Start()
{
myGroup = GroupController.GetGroup(groupId);
}
void Update()
{
Vector3 dis = myGroup.transform.position - transform.position;
Vector3 dir = dis.normalized;
//重新计算目的地距离权重
if (dis.magnitude < myGroup.targetCloseDistance)
{
dir *= dis.magnitude / myGroup.targetCloseDistance;
}
dir += GetAroundMemberInfo();//获取周围组的移动
//计算移动速度
if ((myGroup.transform.position - transform.position).magnitude < myGroup.stopDis)
{
targetSpeed = 0;
}
else
{
targetSpeed = moveSpeed;
}
speed = Mathf.Lerp(speed, targetSpeed, 2 * Time.deltaTime);
//————————————————————移动
transform.right = -dir;
Move(dir, speed);
}
/// <summary>
/// 得到周围成员的信息
/// </summary>
/// <returns></returns>
private Vector3 GetAroundMemberInfo()
{
Collider2D[] c = Physics2D.OverlapCircleAll(transform.position, myGroup.keepDis, myGroup.mask);//获取周围成员
Vector3 dis;
Vector3 v1 = Vector3.zero;
Vector3 v2 = Vector3.zero;
for (int i = 0; i < c.Length; i++)
{
GroupMember otherMember = c[i].GetComponent<GroupMember>();
dis = transform.position - otherMember.transform.position;//距离
v1 += dis.normalized * (1 - dis.magnitude / myGroup.keepDis);//查看与周围成员的距离
v2 += otherMember.myMovement;//查看周围成员移动方向
Debug.DrawLine(transform.position, otherMember.transform.position, Color.yellow);
}
return v1.normalized * myGroup.keepWeight + v2.normalized;//添加权重因素
}
/// <summary>
/// 移动
/// </summary>
/// <param name="_dir">方向</param>
/// <param name="_speed">速度</param>
private void Move(Vector3 _dir, float _speed)
{
Vector3 finialDirection = _dir.normalized;
float finialSpeed = _speed, finialRotate = 0;
float rotateDir = Vector3.Dot(finialDirection, transform.right);
float forwardDir = Vector3.Dot(finialDirection, transform.forward);
if (forwardDir < 0)
{
rotateDir = Mathf.Sign(rotateDir);
}
if (forwardDir < -0.2f)
{
finialSpeed = Mathf.Lerp(currentSpeed, -_speed * 8, 4 * Time.deltaTime);
}
//——————————防抖
if (forwardDir < 0.98f)
{
finialRotate = Mathf.Clamp(rotateDir * 180, -rotateSpeed, rotateSpeed);
}
finialSpeed *= Mathf.Clamp01(_dir.magnitude);
finialSpeed *= Mathf.Clamp01(1 - Mathf.Abs(rotateDir) * 0.8f);
transform.Translate(Vector3.left * finialSpeed * Time.deltaTime);
transform.Rotate(Vector3.forward * finialRotate * Time.deltaTime);
currentSpeed = finialSpeed;
myMovement = _dir * finialSpeed;
}
}