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

}

猜你喜欢

转载自blog.csdn.net/LLLLL__/article/details/106713870