unity官方demo学习之Stealth(二十)敌人视听范围

时间:2021-12-30 14:59:05

为敌人添加脚本DoneEnemySight

using UnityEngine;
using System.Collections;

public class DoneEnemySight : MonoBehaviour
{
	public float fieldOfViewAngle = 110f;				// Number of degrees, centred on forward, for the enemy see.
    public bool playerInSight;							// Whether or not the player is currently sighted.
	public Vector3 personalLastSighting;				// Last place this enemy spotted the player.
	
	
	private NavMeshAgent nav;							// Reference to the NavMeshAgent component.
	private SphereCollider col;							// Reference to the sphere collider trigger component.
	private Animator anim;								// Reference to the Animator.
	private DoneLastPlayerSighting lastPlayerSighting;	// Reference to last global sighting of the player.
    private GameObject player;							// Reference to the player.
	private Animator playerAnim;						// Reference to the player's animator component.
	private DonePlayerHealth playerHealth;				// Reference to the player's health script.
	private DoneHashIDs hash;							// Reference to the HashIDs.
	private Vector3 previousSighting;					// Where the player was sighted last frame.
	
	
	void Awake ()
	{
		// Setting up the references.
		nav = GetComponent<NavMeshAgent>();
		col = GetComponent<SphereCollider>();
		anim = GetComponent<Animator>();
		lastPlayerSighting = GameObject.FindGameObjectWithTag(DoneTags.gameController).GetComponent<DoneLastPlayerSighting>();
		player = GameObject.FindGameObjectWithTag(DoneTags.player);
		playerAnim = player.GetComponent<Animator>();
		playerHealth = player.GetComponent<DonePlayerHealth>();
		hash = GameObject.FindGameObjectWithTag(DoneTags.gameController).GetComponent<DoneHashIDs>();
		
		// Set the personal sighting and the previous sighting to the reset position.
		personalLastSighting = lastPlayerSighting.resetPosition;
		previousSighting = lastPlayerSighting.resetPosition;
	}
	
	
	void Update ()
	{
		// If the last global sighting of the player has changed...
		if(lastPlayerSighting.position != previousSighting)
			// ... then update the personal sighting to be the same as the global sighting.
			personalLastSighting = lastPlayerSighting.position;
		
		// Set the previous sighting to the be the sighting from this frame.
		previousSighting = lastPlayerSighting.position;
		
		// If the player is alive...
		if(playerHealth.health > 0f)
			// ... set the animator parameter to whether the player is in sight or not.
			anim.SetBool(hash.playerInSightBool, playerInSight);
		else
			// ... set the animator parameter to false.
			anim.SetBool(hash.playerInSightBool, false);
	}
	

	void OnTriggerStay (Collider other)
    {
		// If the player has entered the trigger sphere...
        if(other.gameObject == player)
        {
			// By default the player is not in sight.
			playerInSight = false;
			
			// Create a vector from the enemy to the player and store the angle between it and forward.
            Vector3 direction = other.transform.position - transform.position;
			float angle = Vector3.Angle(direction, transform.forward);
			
			// If the angle between forward and where the player is, is less than half the angle of view...
			if(angle < fieldOfViewAngle * 0.5f)
			{
				RaycastHit hit;
				
				// ... and if a raycast towards the player hits something...
				if(Physics.Raycast(transform.position + transform.up, direction.normalized, out hit, col.radius))
				{
					// ... and if the raycast hits the player...
					if(hit.collider.gameObject == player)
					{
						// ... the player is in sight.
						playerInSight = true;
						
						// Set the last global sighting is the players current position.
						lastPlayerSighting.position = player.transform.position;
					}
				}
			}
			
			// Store the name hashes of the current states.
			int playerLayerZeroStateHash = playerAnim.GetCurrentAnimatorStateInfo(0).nameHash;
			int playerLayerOneStateHash = playerAnim.GetCurrentAnimatorStateInfo(1).nameHash;
			
			// If the player is running or is attracting attention...
			if(playerLayerZeroStateHash == hash.locomotionState || playerLayerOneStateHash == hash.shoutState)
			{
				// ... and if the player is within hearing range...
				if(CalculatePathLength(player.transform.position) <= col.radius)
					// ... set the last personal sighting of the player to the player's current position.
					personalLastSighting = player.transform.position;
			}
        }
    }
	
	
	void OnTriggerExit (Collider other)
	{
		// If the player leaves the trigger zone...
		if(other.gameObject == player)
			// ... the player is not in sight.
			playerInSight = false;
	}
	
	
	float CalculatePathLength (Vector3 targetPosition)
	{
		// Create a path and set it based on a target position.
		NavMeshPath path = new NavMeshPath();
		if(nav.enabled)
			nav.CalculatePath(targetPosition, path);
		
		// Create an array of points which is the length of the number of corners in the path + 2.
		Vector3 [] allWayPoints = new Vector3[path.corners.Length + 2];
		
		// The first point is the enemy's position.
		allWayPoints[0] = transform.position;
		
		// The last point is the target position.
		allWayPoints[allWayPoints.Length - 1] = targetPosition;
		
		// The points inbetween are the corners of the path.
		for(int i = 0; i < path.corners.Length; i++)
		{
			allWayPoints[i + 1] = path.corners[i];
		}
		
		// Create a float to store the path length that is by default 0.
		float pathLength = 0;
		
		// Increment the path length by an amount equal to the distance between each waypoint and the next.
		for(int i = 0; i < allWayPoints.Length - 1; i++)
		{
			pathLength += Vector3.Distance(allWayPoints[i], allWayPoints[i + 1]);
		}
		
		return pathLength;
	}
}

参数:(公有) 敌人的视角范围

判断敌人是否看见玩家

每个敌人独立的变量 用于储存敌人听到玩家脚步声/喊叫声的位置

(私有)

引用NavMeshAgent组件
     引用sphere collider触发器组件
     引用Animator组件
     引用LastPlayerSighting脚本
     引用玩家对象
     引用玩家的Animator组件
     引用PlayerHealth脚本
    引用HashIDs脚本
     上一帧玩家被发现的位置
函数
Awake
获取引用对象和组件
把敌人单独发现的位置 和上一帧发现玩家的位置都重置(1000,1000,1000)

Update
如果发现玩家位置的全局变量发生改变(即玩家被发现)
那么每个敌人的单独位置变量都将等于全局变量(即告诉所有敌人发现玩家了,赶快来)
并把上一帧发现玩家的位置等于全局变量
如果玩家HP值等于0 也就是玩家还活着
把playerInSight的值传递到Animator中相应的参数 也就是设为true(触发敌人的各种动作)
否则设为false

OnTriggerStay (Collider other)
如果玩家进入触发器范围内
默认玩家还没有被发现
创建敌人到玩家位置的向量 然后获取它与敌人正前方向量的夹角
unity官方demo学习之Stealth(二十)敌人视听范围
如果夹角小于敌人视角的二分之一
创建光线碰撞检测对象
如果从敌人到玩家的光线投射碰撞到某个对象(因为敌人坐标在脚上,投射可能会碰到地面,所以向上位移一个单位,敌人为2个单位)
如果碰撞对象是玩家
判定玩家被发现
更新玩家的坐标让玩家最后被发现的位置的全局变量 等于玩家当前位置
储存Animator中的跑步状态和喊叫状态为hash(0代表base层,1代表shooting层)
如果玩家在跑步状态 或者在喊叫状态时
并且玩家在敌人听觉范围内(函数返回的玩家到敌人的路径长度 < 视听碰撞器的范围)
敌人单独储存玩家位置的变量 等于玩家当前位置

OnTriggerExit (Collider other)
如果玩家离开触发器范围
判定玩家没有被发现

CalculatePathLength (Vector3 targetPosition)计算玩家到敌人的路径长度
创建路径 并让路径基于目标位置 (玩家位置) NavMeshPath是一个引用类,可以在任何脚本中使用
创建一个数组allWayPoints 长度等于path.cornners.Length+2 (这里的cornners(NavMeshPath类的一个Vector数组)存储路径中的拐弯点,由unity内置计算)
unity官方demo学习之Stealth(二十)敌人视听范围
数组第一个值为敌人位置
最后一个值为玩家位置
path.cornners数组插入到allWayPoints数组中
创建路径长度变量 并赋予初始值0
迭代计算路径总长度