日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Unity3D学习(五):实现一个简单的视觉感知

發布時間:2023/12/10 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unity3D学习(五):实现一个简单的视觉感知 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

在很多第一人稱或者第三人稱射擊游戲的單人模式中,玩家的樂趣往往來源于和各式各樣的AI敵人的戰斗。而戰斗的爆發很多時候是因為這些AI在“看見”玩家后就會立即做出反應,比如開火、呼叫同伴、躲藏或者逃跑等。

所以這些AI到底是如何探測,或者說”看到“玩家位置的?

別人的例子

參考了知乎?給貓看的游戲AI實戰(二)視覺感知初步?這篇文章。

這篇文章中,原作者讓玩家站在敵人的角度來探測目標,它通過向正前方一定扇形區域發射一堆射線來探測目標的位置,如下圖:

?這種方法雖然實現起來比較簡單,但它主要有兩個弊端:

1.同一時間內發射大量的射線,對游戲本身的優化來說很不好,容易造成卡頓。

2.如果要探測的物體比較小,甚至比兩條射線之間的間隔還小,那么射線是無法探測到這個物體的。

另一種解決思路

原文的評論中,有人提到可以基于探測者自身構建一個球體來探測周圍的物體。

因此我們可以用Unity自帶的Sphere觸發器或者Physics里的OverLaps來構建一個球體探測區域,如下圖:

?這里我用的是Overlaps,代碼如下:

玩家,即探測者

using System.Collections; using System.Collections.Generic; using UnityEngine;public class Player : MonoBehaviour {public float moveSpeed; //移動速度public float EyeViewDistance; //視野距離public float viewAngle = 120f; //視野角度private Rigidbody rb;private Collider[] SpottedEnemies; //附近的敵人// Use this for initializationvoid Start () {rb = GetComponent<Rigidbody>();}private void FixedUpdate(){DetectEnemy();}// Update is called once per framevoid Update (){//AutoMove(); MoveAndTurn();Debug.DrawLine(transform.position, transform.forward * 100, Color.red); //紅色射線,面對的方向 }void AutoMove() //向面對的方向自動移動 {transform.position += transform.forward * moveSpeed * Time.deltaTime;}void MoveAndTurn() //玩家移動 {Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hitInfo = new RaycastHit();//shoot a ray from cam to mouse position which is only detected by gameobject with "Plane" layer.Physics.Raycast(ray, out hitInfo, 100, LayerMask.GetMask("Plane"));if (hitInfo.collider != null){transform.LookAt(new Vector3(hitInfo.point.x, transform.position.y, hitInfo.point.z));}rb.velocity = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized * moveSpeed;}void DetectEnemy() //探測敵人 {//OverlapSphere內的敵人SpottedEnemies = Physics.OverlapSphere(transform.position, EyeViewDistance, LayerMask.GetMask("Enemy"));for(int i = 0;i < SpottedEnemies.Length;i++) //檢測每一個敵人是否在視野區中 {Vector3 EnemyPosition = SpottedEnemies[i].transform.position; //敵人的位置//Debug.Log(transform.forward + " 面對的方向");//Debug.Log("夾角為:" + Vector3.Angle(transform.forward, EnemyPosition - transform.position)); Debug.DrawRay(transform.position, EnemyPosition - transform.position, Color.yellow); //玩家位置到敵人位置的向量if (Vector3.Angle(transform.forward, EnemyPosition - transform.position) <= viewAngle/2) //這個敵人是否在視野內 {//如果在視野內RaycastHit info = new RaycastHit();int layermask = LayerMask.GetMask("Enemy", "Obstacles"); //指定射線碰撞的對象Physics.Raycast(transform.position, EnemyPosition - transform.position, out info,EyeViewDistance,layermask); //向敵人位置發射射線 Debug.Log(info.collider.gameObject.name);if(info.collider == SpottedEnemies[i]) //如果途中無其他障礙物,那么射線就會碰撞到敵人 {DiscoveredEnemy(SpottedEnemies[i]);}}}}void DiscoveredEnemy(Collider Enemy) //發現敵人 {//Do somethingDebug.Log("發現敵軍:" + Enemy.gameObject.name);Enemy.GetComponent<Enemy>().BeDiscovered();} }

SpottedEnemies是一個Collider數組,我用它來保存這一幀當中處于OverlapSphere形成的球體區域的所有敵人對象,(LayerMask可以讓Overlaps的球體只和指定layer的對象發生交互)。然后計算玩家面對的方向和探測到的目標方向的夾角Vector3.Angle(transform.forward, EnemyPosition - transform.position),如下圖:

forwar向量代表玩家面朝的方向,v1代表探測到的物體相對于玩家位置的方向,紅色扇形區域代表玩家的視野范圍。那么計算這兩個向量的夾角,然后判斷下這個夾角是否小于扇形區夾角的一半(即探測目標是否在玩家視野內)就行了。如果夾角小于視野夾角的一半,那么我們再向目標位置發射一根射線,然后看下射線碰撞到的物體是否是目標對象就行,因為如果玩家和目標之間有障礙物的話,那么射線是會被障礙物擋下來的(也就是說玩家的"視野"被障礙物"遮擋"了)。

Enemy,被探測的目標

這里設定被探測的目標脫離玩家視野一定時間后重新進入隱形狀態。

using System.Collections; using System.Collections.Generic; using UnityEngine;public class Enemy : MonoBehaviour {public float HideCoolDown = 0.3f; //隱藏自己的冷卻時間public float AppearTime; //最后一次被發現的時刻private MeshRenderer mr;// Use this for initializationvoid Awake(){mr = GetComponent<MeshRenderer>();}private void Start(){mr.enabled = false;}// Update is called once per framevoid Update (){if (Time.time - AppearTime < HideCoolDown) return;if (mr.enabled)mr.enabled = false;}public void BeDiscovered() //被發現了 {mr.enabled = true;AppearTime = Time.time;} }

最后實現的效果圖如下:

這種方法就不用發射大量的射線,而且只會對進入球形探測區域且處于視野范圍內的物體發射射線,并且也避免了小物體無法被探測的bug。

?

參考資料

知乎:U3d開發中大部分事件都是用數學進行計算判定的嗎? 最高票答案? 作者:Meta42

貓看的游戲AI實戰(二)視覺感知初步? ? 作者:馬遙

轉載于:https://www.cnblogs.com/0kk470/p/7667655.html

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的Unity3D学习(五):实现一个简单的视觉感知的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。