【Unity】入门学习笔记180709——人工智能(10)——听觉感知/触觉感知/记忆感知

      听觉感知可以用一个球星区域来模拟。另一种方法是党声音被创建时,为它加上一个强度属性,随着传播距离的增加,声音强度会衰减,而每个AI角色也有自己的听觉阈值,如果声音小于这个阈值,AI角色就听不到这个声音。

       这种具有特定生命周期的触发器,都可以从下面的TriggerlimitedLifetime类派生出来

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

public class TriggerLimitedLifetime : Trigger {

    //触发器的持续时间
    protected int lifetime;

    public override void Updateme()
    {
        //持续时间倒计时,如果剩余持续时间小于等于0,那么将它标记为需要移除;
        if (--lifetime <= 0)
        {
            toBeRemoved = true;
        }
    }

    // Use this for initialization
    void Start () {
        base.Start();
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}


例如,当武器开火时,在开火的位置会创建一个SoundTrigger,它的半径可以设置为与武器的声音大小成正比。此时,在一定范围内,具有声音感知器的感知体就能够“听到”这个声音,并做出反应。

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

public class SoundTrigger : TriggerLimitedLifetime
{
    //判断感知体是否能够听到声音触发器发出的声音,如果能,通知感知器
    public override void Try(Sensor s)
    {
        if (isTouchingTrigger(s))
        {
            s.Notify(this);
        }
        
    }

    //判断感知体是否能听到声音触发器发出的声音
    protected override bool isTouchingTrigger(Sensor sensor)
    {
        GameObject g = sensor.gameObject;
        //如果感知器能够感知声音
        if (sensor.sensorType == Sensor.SensorType.sound)
        {
            //如果感知体与声音触发器的距离在声音触发器的作用范围内,返回true;
            if ((Vector3.Distance(transform.position, g.transform.position) < radius)){
                return true;
            }
        }
        return false;
    }

    // Use this for initialization
    void Start () {
        
        //设置该触发器的持续时间;
        lifetime = 3;
        //调用基类的Start()函数
        base.Start();

        //将这个触发器加入到管理器的触发器列表中
        manager.RegisterTrigger(this);
	}
	
	// Update is called once per frame
	void Update () {
		
	}

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.blue;
        Gizmos.DrawWireSphere(transform.position, radius);
    }
}


为具有听觉的AI角色加上声音感知器,这个感知器时Sensor的派生类,用来感知由声音触发器出发的那些声音信息。

AIController类

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

public class SoundSensor : Sensor {

    //定义感知体的听觉范围,这里并没有实际使用
    public float hearingDistance = 30.0f;
    private AIController controller;

	// Use this for initialization
	void Start () {
        controller = GetComponent<AIController>();
        //设置感知器类型为声音感知器
        sensorType = SensorType.sound;
        //向管理器注册这个感知器
        manager.RegisterSensor(this);
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}

    public override void Notify(Trigger t)
    {
        //当感知体能够听到触发器的声音时被调用,做出相应行为,这里打印信息,并走向声音的位置
        print("I hear some sound at" + t.gameObject.transform.position + Time.time);
        controller.moveToTarget(t.gameObject.transform.position);
    }
}


    触觉感知可以交给U3D物理引擎来处理。

    

    记忆感知:

    为了让角色具有记忆,实现一个SenseMemory类,具有一个记忆列表,列表中保存了最近感知到的对象、感知类型、最后感知到该对象的时间以及还能在记忆中保留的时间。当一段时间没有感知到这个对象,这个时间超出了记忆时长时,就会将这个对象从记忆列表中删除。

    

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

public class SenseMemory : MonoBehaviour {

    //已经在列表中?
    private bool alreadyInList = false;
    //记忆留存时间
    public float memoryTime = 4.0f;
    //记忆列表
    public List<MemoryItem> memoryList = new List<MemoryItem>();
    //此时需要从记忆列表中删除的项
    private List<MemoryItem> removeList = new List<MemoryItem>();
    //在记忆列表中寻找玩家信息
    public bool FindInList()
    {
        foreach (MemoryItem mi in memoryList)
            if (mi.g.tag == "Player")
                return true;
        return false;
    }

    //项记忆列表中添加一个项
    public void AddToList(GameObject g,float type)
    {
        alreadyInList = false;
        //如果该项已经在列表中,那么更新最后感知时间等信息
        foreach(MemoryItem mi in memoryList)
        {
            if (g == mi.g)
            {
                alreadyInList = true;
                mi.lastMemoryTime = Time.time;
                mi.memoryTimeLeft = memoryTime;
                if (type > mi.sensorType)
                    mi.sensorType = type;
                break;
            }
        }

        //如果不在列表中,新建项并加入列表
        if (!alreadyInList)
        {
            MemoryItem newItem = new MemoryItem(g, Time.time, memoryTime, type);
            memoryList.Add(newItem);
        }
    }

    // Use this for initialization
    void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
        removeList.Clear();
        //遍历所有项,找到那些超时需要“忘记”的项,删除;
        foreach(MemoryItem mi in memoryList)
        {
            mi.memoryTimeLeft -= Time.deltaTime;
            if (mi.memoryTimeLeft < 0)
            {
                removeList.Add(mi);
            }
            else
            {
                //对没被删除的项,画出一条线,表示仍然在记忆中
                if (mi.g != null)
                    Debug.DrawLine(transform.position, mi.g.transform.position, Color.blue);
            }
        }
        foreach(MemoryItem m in removeList)
        {
            memoryList.Remove(m);
        }
		
	}

    public class MemoryItem
    {
        //感知到的游戏对象
        public GameObject g;
        //最近的感知时间
        public float lastMemoryTime;
        //还能留存在记忆中的时间
        public float memoryTimeLeft;
        //通过哪种方式感知到的该对象,视觉为1,听觉为0.66
        public float sensorType;
        public MemoryItem(GameObject objectToAdd, float time, float timeLeft, float type)
        {
            g = objectToAdd;
            lastMemoryTime = time;
            memoryTimeLeft = timeLeft;
            sensorType = type;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Dylan_Day/article/details/80847647