Unity魚羣算法

一:前言

魚羣算法又稱爲羣組行爲,比如說走路,但是不能很規律的執行走路,要有人工智能的感覺的去執行走路,更加真實的去模擬現實,並且人與人之間不能擁擠到一起。常用與鳥羣,魚羣,人羣等

魚羣算法的規則:
——分隔規則: 儘量避免與臨近成員過於擁擠
——對準規則: 儘量與臨近成員的平均方向一致
——內聚規則: 儘量朝臨近成員的中心移動


二:效果演示


三:代碼

——組的總控制器

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;
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章