一:前言
魚羣算法又稱爲羣組行爲,比如說走路,但是不能很規律的執行走路,要有人工智能的感覺的去執行走路,更加真實的去模擬現實,並且人與人之間不能擁擠到一起。常用與鳥羣,魚羣,人羣等
魚羣算法的規則:
——分隔規則: 儘量避免與臨近成員過於擁擠
——對準規則: 儘量與臨近成員的平均方向一致
——內聚規則: 儘量朝臨近成員的中心移動
二:效果演示
三:代碼
——組的總控制器
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;
}
}