【Unity】入门学习笔记180522——人工智能(5)——群体操控(1)

1、模仿群体行为需要下面几种操控行为

分离(Separation):避免个体在局部过于拥挤的操控力

队列(Alignment):朝向附近同伴的平均朝向的操控力

聚集(Cohesion):像附近同伴的平均位置移动的操控力


2、检测附近AI角色

为了实现组行为,首先需要检测位于当前AI角色“邻域”中的其他AI角色,用一个雷达脚本实现:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Radar : MonoBehaviour {

    //碰撞体的数组
    private Collider[] colliders;
    //计时器
    private float timer = 0;
    //邻居列表
    public List<GameObject> neighbors;
    //无需每帧进行检测,该变量设置检测的时间间隔
    public float checkInterval = 0.3f;
    //设置邻域半径
    public float detectRadius = 10f;
    //设置检测哪一层的游戏对象
    public LayerMask layersChecked;

	// Use this for initialization
	void Start () {
        //初始化邻居列表
        neighbors = new List<GameObject>();
		
	}
	
	// Update is called once per frame
	void Update () {
        timer += Time.deltaTime;
        //如果距离上次检测的时间大于所设置的检测时间间隔,那么再次检测;
        if (timer > checkInterval)
        {
            //清除邻居列表
            neighbors.Clear();
            //查找当前AI角色邻域内的所有碰撞体
            colliders = Physics.OverlapSphere(transform.position, detectRadius, layersChecked);
            //对于每个检测到的碰撞体,获取Vehicle组件,并且加入邻居列表中;
            for(int i = 0; i < colliders.Length; i++)
            {
                if (colliders[i].GetComponent<Vehicle>())
                    neighbors.Add(colliders[i].gameObject);
            }
            //计时器归0
            timer = 0;

        }
		
	}
}


3、与群中邻居保持适当距离——分离

为了计算分离行为所需的操控力,首先要搜索指定领域内的其他邻居,然后对每个邻居,计算AI角色到该邻居的向量r,将向量r归一化(r/|r|,这里|r|表示向量r的长度),得到排斥力的方向,由于排斥力的大小与距离成反比的,因此还需要除以|r|,得到该邻居对它的排斥力,即r/|r|2,然后把来自所有邻居的排斥力相加,就得到了分离行为的总操控力

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SteeringForSeparation : Steering {
    //可接受距离
    public float comfortDistance = 1;
    //当AI角色与邻居之间距离过近时的惩罚因子;
    public float multiplierInsideComfortDistance = 2;
    // Use this for initialization

    void Start () {
	 
	}

    public override Vector3 Force()
    {
        Vector3 steeringForce = new Vector3(0, 0, 0);
        //遍历这个AI角色的邻居列表中的每个邻居
        foreach(GameObject s in GetComponent<Radar>().neighbors)
        {
            //如果s不是当前AI角色
            if ((s != null) && (s != this.gameObject))
            {
                //计算当前AI角色与邻居s之间的距离
                Vector3 toNeighbor = transform.position - s.transform.position;
                float length = toNeighbor.magnitude;
                //计算这个邻居引起的操控力(可以认为是排斥力,大小与距离成反比)
                steeringForce += toNeighbor.normalized / length;
                //如果二者之间距离大于可接受距离,排斥力再乘以一个额外因子
                if (length < comfortDistance)
                    steeringForce *= multiplierInsideComfortDistance;
            }
        }
        return steeringForce;
    }

    // Update is called once per frame
    void Update () {
		
	}
}


4、与群中邻居朝向一致——队列

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*
 * 与群中邻居朝向一致——队列
 *通过迭代所有邻居,可以求出AI角色朝向向量的平均值以及速度的平均值 
 * 得到想要的朝向
 * 然后减去AI角色的当前朝向,就可以得到队列操控力
 */

public class SteeringForAlignment : Steering {

	// Use this for initialization
	void Start () {
		
	}

    public override Vector3 Force()
    {
        //当前AI角色的邻居的平均朝向
        Vector3 averageDirection = new Vector3(0, 0, 0);
        //邻居的数量
        int neighborCount = 0;
        //遍历当前AI角色的所有邻居
        foreach (GameObject s in GetComponent<Radar>().neighbors)
        {
            //如果s不是当前AI角色
            if ((s != null) && (s != this.gameObject)) {
                //将s的朝向向量加到averageDirection之中;
                averageDirection += s.transform.forward;
                //邻居数量加1
                neighborCount++;
            }
        }
            //如果邻居数量大于0
        if (neighborCount > 0)
        {
            //将累加得到朝向向量除以邻居的个数,求出平均朝向向量
            averageDirection /= (float)neighborCount;
            //平均朝向向量减去当前朝向向量,得到操控向量
            averageDirection -= transform.forward;
        }

        return averageDirection;
    }

    // Update is called once per frame
    void Update () {
		
	}
}


5、成群聚集在一起——聚集

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


/* 成群聚集在一起——聚集
 * 聚集行为产生一个使AI角色移向邻居的质心的操控力
 * 迭代所有邻居求出AI角色位置的平均值,利用靠近行为,将这个平均值作为目标位置
 */

public class SteeringForCohesion : Steering {

    private Vector3 desiredVelocity;
    private Vehicle m_vehicle;
    private float maxSpeed;

	// Use this for initialization
	void Start () {
        m_vehicle = GetComponent<Vehicle>();
        maxSpeed = m_vehicle.maxSpeed;
		
	}

    public override Vector3 Force()
    {
        //操控向量
        Vector3 steeringForce = new Vector3(0, 0, 0);
        //AI角色的邻居的质心,即平均位置
        Vector3 centerOfMass = new Vector3(0, 0, 0);
        //AI角色的邻居数量
        int neighborCount = 0;
        //遍历邻居列表中的每个邻居
        foreach(GameObject s in GetComponent<Radar>().neighbors)
        {
            //如果s不是当前AI角色
            if ((s != null) && (s != this.gameObject))
            {
                //累加s的位置
                centerOfMass += s.transform.position;
                //邻居数量加1
                neighborCount++;
            }
        }
        //如果邻居数量大于0
        if (neighborCount > 0)
        {
            //将位置的累加值除以邻居数量,得到平均值
            centerOfMass /= (float)neighborCount;
            //预期速度为邻居位置平均值与当前位置之差
            desiredVelocity = (centerOfMass - transform.position).normalized * maxSpeed;
            //预期速度减去当前速度,求出操控向量
            steeringForce = desiredVelocity - m_vehicle.velocity;

        }
        return steeringForce;
    }

    // Update is called once per frame
    void Update () {
		
	}
}

猜你喜欢

转载自blog.csdn.net/dylan_day/article/details/80407333
今日推荐