Unity AI行为之群组行为

发表于2018-09-30
评论0 2.3k浏览
群组行为是指多个对象组队同时行进的情况,我们可以坐下来,告诉每一个对象它该如何移动,但这样做的工作量太大。取而代之的是,我们去创建一个群组的领导,让它来为我们做这些,这样我们所要做的就只是设置一些规则,然后群组中的boid就会自行组队。在本篇文章中,我们将学习如何在unity3d中实现这种群组行为。

每个boid都可以应用一下三个基本的规则:
  • 分离(seperation):群组中的每个个体都与相邻个体保持一定距离
  • 列队(alignment):群组以相同速度,向相同方向移动
  • 凝聚(cohesion):群组中心保持最小距离

在本篇文章中我们将创建自己的场景,场景里会有一群对象,并使用C#实现群组行为。有两个主要的组成部分:每个boid行为,以及维持并领导整个群组的主要控制者。我们的场景层级如图,在一个名为UnityFlockController的控制器下面有一些boid实体----UnityFlock。每一个UnityFlock实体都是一个boid对象,它们会引用其父对象UnityFlockController实体作为它们的领导者。UnityFlockController将会在到达目标位置后,随机的更新下一个目标位置。UnityFlock是一个预制体,这个预制体仅仅是一个立方体网格,并拥有UnityFlock脚本。我们可以使用任何更有意思的其他的网格标识这个预制体,比如小鸟。

个体行为boid是Craig Reynold创造的术语,用以表示类似小鸟这样的对象。我们将使用这个术语描述群组中的每个个体对象。UnityFlock这个脚本控制群组中每一个boid的行为。
using UnityEngine;
using System.Collections;
public class UnityFlock : MonoBehaviour {
    public float minSpeed = 20;//最小移动速度
    public float turnSpeed = 20;//旋转速度
    public float randomFreq = 20;//用来确定更新randomPush变量的次数
    public float randomForce = 20;//这个力产生出一个随机增长和降低的速度,并使得群组的移动看上去更加真实
    //alignment variables列队变量
    public float toOriginForce = 50;//用这个来保持所有boid在一个范围内,并与群组的原点保持一定距离
    public float toOriginRange = 100;//群组扩展的程度
    public float gravity = 2;
    //seperation variables分离变量
    public float avoidanceRadius = 50;
    public float avoidanceForce = 20;//这两个变量可以让每个boid个体之间保持最小距离
    //cohesion variables凝聚变量,这两个变量可用来与群组的领导者或群组的原点保持最小距离。
    public float followVelocity = 4;
    public float followRadius = 40;
    //这些变量控制了boid的移动
    private Transform origin;//设为父对象,以控制整个群组中的对象。
    private Vector3 velocity;
    private Vector3 normalizeedVelocity;
    private Vector3 randomPush;//更新基于randomFore的值
    private Vector3 originPush;
    //以下两个变量存储相邻boid的信息,当前boid需要知道群组中其他boid的信息
    private Transform[] objects;
    private UnityFlock[] otherFlocks;
    private Transform transformComponent;
    /*
         */
	void Start () {
        randomFreq = 1.0f / randomFreq;
        //将父类指定为origin
        origin = transform.parent;
        //Flock transform
        transformComponent = transform;
        //Temporary components临时
        Component[] tempFlocks = null;
        //Get all the unity flock omponents from the parent transform in the group
        if (transform.parent )
        {
            tempFlocks = transform.parent.GetComponentsInChildren<UnityFlock>();
        }
        //Assign and store all the flock objects in this group
        objects = new Transform[tempFlocks.Length];
        otherFlocks = new UnityFlock[tempFlocks.Length];
        for (int i = 0; i < tempFlocks.Length; i++)
        {
            objects[i] = tempFlocks[i].transform;
            otherFlocks[i] = (UnityFlock)tempFlocks[i];
        }
        //Null Parent as the flok leader will be UnityFlockController object
        transform.parent = null;
        //Calculate random push depends on the random frequency provided
        StartCoroutine(UpdateRandom());
	}
    IEnumerator UpdateRandom()
    {
        while (true)
        {
            randomPush = Random.insideUnitSphere * randomForce;
            yield return new WaitForSeconds(randomFreq + Random.Range(-randomFreq / 2.0f, randomFreq / 2.0f));
        }
    }
	void Update () {
        //internal variables
        float speed = velocity.magnitude;//获取速度大小
        Vector3 avgVelocity = Vector3.zero;
        Vector3 avgPosition = Vector3.zero;
        float count = 0;
        float f = 0.0f;
        float d = 0.0f;
        Vector3 myPosition = transformComponent.position;
        Vector3 forceV;
        Vector3 toAvg;
        Vector3 wantedVel;
        for (int i = 0; i < objects.Length; i++)
        {
            Transform transform = objects[i];
            if (transform != transformComponent)
            {
                Vector3 otherPosition = transform.position;
                //Average position to calculate cohesion
                avgPosition += otherPosition;
                count++;
                //Directional vector from other flock to this flock
                forceV = myPosition - otherPosition;
                //Magnitude of that diretional vector(length)
                d = forceV.magnitude;
                //Add push value if the magnitude,the length of the vetor,is less than followRadius to the leader
                if (d < followRadius)
                {
                    //calculate the velocity,the speed of the object,based current magnitude is less than the specified avoidance radius
                    if (d < avoidanceRadius)
                    {
                        f = 1.0f - (d / avoidanceRadius);
                        if (d > 0)
                        {
                            avgVelocity += (forceV / d) * f * avoidanceForce;
                        }
                    }
                    //just keep the current distance with the leader
                    f = d / followRadius;
                    UnityFlock otherSealgull = otherFlocks[i];
                    //we normalize the otherSealgull veloity vector to get the direction of movement,then wo set a new veloity
                    avgVelocity += otherSealgull.normalizeedVelocity * f * followVelocity;
                }
            }
        }
        //上述代码实现了分离规则,首先,检查当前boid与其他boid之间的距离,并相应的更新速度,接下来,用当前速度除以群组中的boid的数目,计算出群组的平均速度
        if (count > 0)
        {
            //Calculate the aveage flock veloity(Alignment)
            avgVelocity /= count;
            //Calculate Center value of the flock(Cohesion)
            toAvg = (avgPosition / count) - myPosition;
        }
        else
        {
            toAvg = Vector3.zero;
        }
        //Directional Vector to the leader
        forceV = origin.position - myPosition;
        d = forceV.magnitude;
        f = d / toOriginRange;
        //Calculate the velocity of the flok to the leader
        if (d > 0)
        {
            originPush = (forceV / d) * f * toOriginForce;
        }
        if (speed < minSpeed && speed > 0)
        {
            velocity = (velocity / speed) * minSpeed;
        }
        wantedVel = velocity;
        //Calculate final velocity
        wantedVel -= wantedVel * Time.deltaTime;
        wantedVel += randomPush * Time.deltaTime;
        wantedVel += originPush * Time.deltaTime;
        wantedVel += avgVelocity * Time.deltaTime;
        wantedVel += toAvg.normalized * gravity * Time.deltaTime;
        //Final Velocity to rotate the flock into
        velocity = Vector3.RotateTowards(velocity, wantedVel, turnSpeed * Time.deltaTime, 100.0f);
        transformComponent.rotation = Quaternion.LookRotation(velocity);
        transformComponent.Translate(velocity * Time.deltaTime, Space.World);
        normalizeedVelocity = velocity.normalized;
	}
} 

现在是时候创建控制器了,这个类会更新自己的位置,这样其他的boid个体对象就知道该去哪里,这个对象由前面的UnityFlock脚本中的origin变量引用而来。
using UnityEngine;
using System.Collections;
public class UnityFlockController : MonoBehaviour {
    public Vector3 offset;
    public Vector3 bound;
    public float speed = 100.0f;
    private Vector3 initialPosition;
    private Vector3 nextMovementPoint;
	void Start () {
        initialPosition = transform.position;
        CalculateNextMovementPoint();
    }
	void Update () {
        transform.Translate(Vector3.forward * speed * Time.deltaTime);
        transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(nextMovementPoint - transform.position), 1.0f * Time.deltaTime);
        if (Vector3.Distance(nextMovementPoint,transform.position) <= 10.0f)
        {
            CalculateNextMovementPoint();
        }
	}
    /*
     在我们的Update()方法中,检查控制器对象是否在最终目标位置附近,如果在,使用我们刚刚讨论过的CalculatNextMovementPoint()方法再次更新nextMovementPoint变量
         */
    void CalculateNextMovementPoint()
    {
        float posX = Random.Range(initialPosition.x - bound.x, initialPosition.x + bound.x);
        float posY = Random.Range(initialPosition.y - bound.y, initialPosition.y + bound.y);
        float posZ = Random.Range(initialPosition.z - bound.z, initialPosition.z + bound.z);
        nextMovementPoint = initialPosition + new Vector3(posX, posY, posZ);
    }
}


来自:https://blog.csdn.net/qq_27880427/article/details/72771510

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引