The hearing function using a collider-based system

In this recipe, we will emulate the sense of hearing by developing two entities: a sound emitter and a sound receiver. It is based on the principles proposed by Millington for simulating a hearing system, and it uses the power of Unity colliders to detect receivers near an emitter.

Getting ready

As with the other recipes based on colliders, we will need collider components attached to every object that is to be checked, and rigid body components attached to either emitters or receivers.

How to do it…

We will create the SoundReceiver class for our agents, and SoundEmitter for things such as alarms:

  1. Create the class for the sound-receiver object:
    using UnityEngine;
    using System.Collections;
    
    public class SoundReceiver : MonoBehaviour
    {
        public float soundThreshold;
    }
  2. Define the function for our own behavior that is handling the reception of sound:
    public virtual void Receive(float intensity, Vector3 position)
    {
        // TODO
        // code your own behaviour here
    }
  3. Now, let's create the class for the sound-emitter object:
    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    public class SoundEmitter : MonoBehaviour
    {
        public float soundIntensity;
        public float soundAttenuation;
        public GameObject emitterObject;
        private Dictionary<int, SoundReceiver> receiverDic;
    }
  4. Initialize the list of nearby receivers and the emitter object, in case the component is attached directly:
    void Start()
    {
        receiverDic = new Dictionary<int, SoundReceiver>();
        if (emitterObject == null)
            emitterObject = gameObject;
    }
  5. Implement the function for adding new receivers to the list when they enter the emitter bounds:
    public void OnTriggerEnter(Collider coll)
    {
        SoundReceiver receiver;
        receiver = coll.gameObject.GetComponent<SoundReceiver>();
        if (receiver == null)
            return;
        int objId = coll.gameObject.GetInstanceID();
        receiverDic.Add(objId, receiver);
    }
  6. Also, implement the function for removing receivers from the list when they are out of reach:
    public void OnTriggerExit(Collider coll)
    {
        SoundReceiver receiver;
        receiver = coll.gameObject.GetComponent<SoundReceiver>();
        if (receiver == null)
            return;
        int objId = coll.gameObject.GetInstanceID();
        receiverDic.Remove(objId);
    }
  7. Define the function for emitting sound waves to nearby agents:
    public void Emit()
    {
        GameObject srObj;
        Vector3 srPos;
        float intensity;
        float distance;
        Vector3 emitterPos = emitterObject.transform.position;
        // next step here
    }
  8. Compute sound attenuation for every receiver:
    foreach (SoundReceiver sr in receiverDic.Values)
    {
        srObj = sr.gameObject;
        srPos = srObj.transform.position;
        distance = Vector3.Distance(srPos, emitterPos);
        intensity = soundIntensity;
        intensity -= soundAttenuation * distance;
        if (intensity < sr.soundThreshold)
            continue;
        sr.Receive(intensity, emitterPos);
    }

How it works…

The collider triggers help register agents in the list of agents assigned to an emitter. The sound emission function then takes into account the agent's distance from the emitter in order to decrease its intensity using the concept of sound attenuation.

How it works…

There is more…

We can develop a more flexible algorithm by defining different types of walls that affect sound intensity. It works by casting rays and adding up their values to the sound attenuation:

  1. Create a dictionary for storing wall types as strings (using tags), and their corresponding attenuation:
    public Dictionary<string, float> wallTypes;
  2. Reduce sound intensity this way:
    intensity -= GetWallAttenuation(emitterPos, srPos);
  3. Define the function that was called in the previous step:
    public float GetWallAttenuation(Vector3 emitterPos, Vector3 receiverPos)
    {
        // next steps here
    }
  4. Compute the necessary values for ray casting:
    float attenuation = 0f;
    Vector3 direction = receiverPos - emitterPos;
    float distance = direction.magnitude;
    direction.Normalize();
  5. Cast the ray and retrieve the hits:
    Ray ray = new Ray(emitterPos, direction);
    RaycastHit[] hits = Physics.RaycastAll(ray, distance);
  6. For every wall type found via tags, add up its value (stored in the dictionary):
    int i;
    for (i = 0; i < hits.Length; i++)
    {
        GameObject obj;
        string tag;
        obj = hits[i].collider.gameObject;
        tag = obj.tag;
        if (wallTypes.ContainsKey(tag))
            attenuation += wallTypes[tag];
    }
    return attenuation;
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.186.83