UnityAI——排队过窄洞

   大家好,这里是七七。

本文想要会用只看本文即可,若想要彻底理解加以从本系列的前些文章开始看


 今天要为大家介绍的例子是Unity中的排队过洞AI,为了让人群有序地通过窄洞,而不是挤在一起,我们需要用到两种操控性为,分别是靠近避开障碍

话不多说,先看效果

排队过窄洞

下面为大家介绍集体步骤

新建一个场景,建一个plane和两大块墙,这两块墙中间有一条窄路,为墙加上“obstacle”标签,并创建一个新layer,编号是9,名称也可以是“obstacle”,墙有“obstacle”标签,并在“obstacle”层

创建目标物体

创建空物体,来生成多个AI角色,为其添加脚本如下

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

public class GenerateBotsForQueue : MonoBehaviour
{
    public GameObject botPrefab;
    public GameObject target;
    public int botCount;
    public float minX = 27f;
    public float maxX = 30f;
    public float minZ = -5.0f;
    public float maxZ = 5.0f;
    public float Yvalue = 1.026003f;
    void Start()
    {
        Vector3 spawnPosition;
        GameObject bot;
        for (int i = 0; i < botCount; i++)
        {
            spawnPosition = new Vector3(Random.Range(minX, maxX), Yvalue, Random.Range(minZ, maxZ));//随机产生一个生成位置
            bot = Instantiate(botPrefab, spawnPosition, Quaternion.identity) as GameObject;
            bot.GetComponent<SteeringForArrive>().target = target;
        }
    }

  
}

创建一个小球作为AI角色,为其添加以下脚本

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

public class AILocomotion : Vehicle
{
    private CharacterController controller;  //AI�Ľ�ɫ������
    private Rigidbody theRigidbody;
    private Vector3 moveDistance;//AI��ɫÿ�ε��ƶ�����

    void Start()
    {
        controller = GetComponent<CharacterController>();
        theRigidbody = GetComponent<Rigidbody>();
        moveDistance = new Vector3(0, 0, 0);
        base.Start();//���û����start��������������ij�ʼ��
    }

    //������ز�����FixedUpdate�и���
    void FixedUpdate()
    {
        velocity += acceleration * Time.fixedDeltaTime;//�����ٶ�
        if (velocity.sqrMagnitude > sqrMaxSpeed)   //��������ٶ�
        velocity = velocity.normalized * maxSpeed;
        moveDistance = velocity * Time.fixedDeltaTime;
        if (isPlanar)  
        {
            velocity.y = 0;
            moveDistance.y = 0;
        }
        if (controller != null)//����Ѿ�ΪAI��ɫ���ӽ�ɫ����������ô���ý�ɫ������ʹ���ƶ�
            controller.SimpleMove(velocity);
        //�����ɫ��û��ɫ��������ҲûRigidbody
        //����Rigidbody����Ҫ�ɶ���ѧ�ķ�ʽ�������ƶ�
        else if (theRigidbody == null || !theRigidbody.isKinematic)
            transform.position += moveDistance;
        else //��Rigidbody���ƽ�ɫ���˶�
            theRigidbody.MovePosition(theRigidbody.position+moveDistance);
        if(velocity.sqrMagnitude>0.00001)//���³�������ٶȴ���һ����ֵ��Ϊ�˷�ֹ������
        {
            Vector3 newForward = Vector3.Slerp(transform.forward, velocity, damping * Time.deltaTime);
            if(isPlanar)
                newForward.y = 0;
            transform.forward = newForward;
        }
        //�������߶���
        gameObject.GetComponent<Animation>().Play("walk");
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SteeringForCollisionAvoidanceQueue : Steering 
{ 
    public bool isPlanar;
    private Vector3 desiredVelocity;
    private Vehicle m_vehicle;
    private float maxSpeed;
    private float maxForce;
    public float avoidanceForce;
    public float MAX_SEE_AHEAD ;
    private GameObject[] allColliders;
    private int layerid;
    private LayerMask layerMask;
    void Start()
    {
        m_vehicle = GetComponent<Vehicle>();
        maxSpeed = m_vehicle.maxSpeed;
        isPlanar = m_vehicle.isPlanar;
        maxForce = m_vehicle.maxForce;
        if (avoidanceForce > maxForce)
            avoidanceForce = maxForce;
        allColliders = GameObject.FindGameObjectsWithTag("obstacle");
        layerid = LayerMask.NameToLayer("vehicles");
        layerMask = 1 << layerid;
    }

    public override Vector3 Force()
    {
        RaycastHit hit;
        Vector3 force = new Vector3(0, 0, 0);
        Vector3 velocity = m_vehicle.velocity;
        Vector3 normalizedVelocity = velocity.normalized;
        if (Physics.Raycast(transform.position, normalizedVelocity, out hit, MAX_SEE_AHEAD ,layerMask))
        {
            Vector3 ahead = transform.position + normalizedVelocity * MAX_SEE_AHEAD; 
            force = ahead - hit.collider.transform.position;
            force *= avoidanceForce;
            if (isPlanar)
                force.y = 0;
        }
        return force;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SteeringForArrive : Steering
{
    public bool isPlanar = true;
    public float arrivalDistance = 0.3f;
    public float characterRadius = 1.2f;
    public float slowDownDistance;
    public GameObject target;
    private Vector3 desiredVelocity;//预期速度
    private Vehicle m_vehicle;//获得被操控的AI角色
    private float maxSpeed;
    void Start()
    {
        m_vehicle = GetComponent<Vehicle>();
        maxSpeed = m_vehicle.maxSpeed;
        isPlanar = m_vehicle.isPlanar;
    }
    public override Vector3 Force()
    {
        Vector3 toTarget = target.transform.position - transform.position;
        Vector3 desiredVelocity;
        Vector3 returnForce;
        if (isPlanar)
            toTarget.y = 0;
        float distance = toTarget.magnitude;
        if (distance > slowDownDistance)
        {
            desiredVelocity = toTarget.normalized * maxSpeed;
            returnForce = desiredVelocity - m_vehicle.velocity;
        }
        else
        {
            desiredVelocity = toTarget - m_vehicle.velocity;
            returnForce = desiredVelocity - m_vehicle.velocity;
        }
        return returnForce;
    }
    void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(target.transform.position, slowDownDistance);
    }
}
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;//设置检测哪一层的游戏对象

    void Start()
    {
        neighbors = new List<GameObject>();
    }

    void Update()
    {
        timer += Time.deltaTime;
        if(timer > checkInterval)
        {
            neighbors.Clear();
            colliders = Physics.OverlapSphere(transform.position, detectRadius, layersChecked);//查找当前AI角色邻域内的所有碰撞体
            for(int i = 0; i < colliders.Length; i++)//对于每个检测到的碰撞体,获取Vehicle组件,并且加入邻居列表钟
            {
                if (colliders[i].GetComponent<Vehicle>())
                    neighbors.Add(colliders[i].gameObject);
            }
            timer = 0;
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SteeringForSeparation : Steering
{
    public float comforDistance = 1;//可接受的距离
    public float multiplierInsideComfortDistance = 2;//当AI角色与邻居距离过近时的惩罚因子
 public override Vector3 Force()
    {
        Vector3 steeringForce = new Vector3(0, 0, 0);
        foreach(GameObject s in GetComponent<Radar>().neighbors)//遍历这个AI角色的邻居列表中的每个邻居
        {
            if ((s != null) && (s != this.gameObject))
            {
                Vector3 toNeighbor = transform.position - s.transform.position;//计算当前AI角色与邻居s之间的距离
                float length=toNeighbor.magnitude;
                steeringForce += toNeighbor.normalized / length;//计算这个邻居引起的操控力
                if (length < comforDistance)
                    steeringForce *= multiplierInsideComfortDistance;
            }
        }
        return steeringForce;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SteeringForQueue : Steering
{
    public float MAX_QUEUE_AHEAD;
    public float MAX_QUEUE_RADIUS;
    private Collider[] colliders;
    public LayerMask layersChecked;
    private Vehicle m_vehicle;
    private int layerid;
    private LayerMask layerMask;
    void Start()
    {
        m_vehicle = GetComponent<Vehicle>();
        layerid = LayerMask.NameToLayer("vehicles");
        layerMask = 1 << layerid;
    }

    public override Vector3 Force()
    {
        Vector3 velocity = m_vehicle.velocity;
        Vector3 normalizedVelocity = velocity.normalized;
        Vector3 ahead = transform.position + normalizedVelocity * MAX_QUEUE_AHEAD;
        colliders = Physics.OverlapSphere(ahead, MAX_QUEUE_RADIUS, layerMask);
        if (colliders.Length > 0)
        {
            foreach (Collider c in colliders)
            {
                if ((c.gameObject != this.gameObject) && (c.gameObject.GetComponent<Vehicle>().velocity.magnitude < velocity.magnitude))
                {
                    m_vehicle.velocity *= 0.8f;
                    break;
                }
            }
        }
        return new Vector3(0, 0, 0);
    }
}

摆放好场景,为脚本设置参数,为小球和墙体预设添加rigibody并设置好重量,就可以顺利运行了

猜你喜欢

转载自blog.csdn.net/m0_63024355/article/details/134398694