unity 智慧巡邏兵
unity 智慧巡邏兵
一、遊戲要求
-
遊戲設計要求:
建立一個地圖和若干巡邏兵(使用動畫);
每個巡邏兵走一個3~5個邊的凸多邊型,位置資料是相對地址。即每次確定下一個目標位置,用自己當前位置為原點計算;
巡邏兵碰撞到障礙物,則會自動選下一個點為目標;
巡邏兵在設定範圍內感知到玩家,會自動追擊玩家;
失去玩家目標後,繼續巡邏;
計分:玩家每次甩掉一個巡邏兵計一分,與巡邏兵碰撞遊戲結束; -
程式設計要求:
必須使用訂閱與釋出模式傳訊息
subject:OnLostGoal
Publisher: ?
Subscriber: ?
工廠模式生產巡邏兵
二、遊戲效果
三、實現過程
本次專案參考自師兄的專案https://blog.csdn.net/C486C/article/details/80153548
並在此基礎上增加了自己的設計。
整體的設計思路在師兄的部落格中已經講得很清楚了,我就挑部分比較重要以及我做了修改的程式碼進行闡述。
PatrolData
儲存了巡邏兵的資料。
public class PatrolData : MonoBehaviour
{
public int sign; //標誌巡邏兵在哪一塊區域
public bool follow_player = false; //是否跟隨玩家
public int wall_sign = -1; //當前玩家所在區域標誌
public GameObject player; //玩家遊戲物件
public Vector3 start_position; //當前巡邏兵初始位置
public bool alive = true;
}
GoPatrolAction
和之前的程式碼結構一樣,Action繼承自SSAction。GoPatrolAction是控制士兵進行巡邏的動作。士兵預設按四角矩形的軌跡運動,如果跟隨玩家的時機被觸發,則銷燬巡邏動作,增加跟隨玩家動作。
public class GoPatrolAction : SSAction
{
private enum Dirction { EAST, NORTH, WEST, SOUTH };
private float pos_x, pos_z; //移動前的初始x和z方向座標
private float move_length; //移動的長度
private float move_speed = 1.5f; //移動速度
private bool move_sign = true; //是否到達目的地
private Dirction dirction = Dirction.EAST; //移動的方向
private PatrolData data; //偵察兵的資料
private GoPatrolAction() { }
public static GoPatrolAction GetSSAction(Vector3 location)
{
GoPatrolAction action = CreateInstance<GoPatrolAction>();
action.pos_x = location.x;
action.pos_z = location.z;
//設定移動矩形的邊長
action.move_length = Random.Range(7, 10);
return action;
}
public override void Update()
{
if (data.alive == false) {
this.destroy = true;
this.callback.SSActionEvent(this, 2, this.gameobject);
return;
}
//偵察移動
Gopatrol();
//如果偵察兵需要跟隨玩家並且玩家就在偵察兵所在的區域,偵查動作結束
if (data.follow_player && data.wall_sign == data.sign)
{
this.destroy = true;
this.callback.SSActionEvent(this,0,this.gameobject);
}
}
public override void Start()
{
this.gameobject.GetComponent<Animator>().SetBool("run", true);
data = this.gameobject.GetComponent<PatrolData>();
}
void Gopatrol()
{
if (move_sign)
{
//不需要轉向則設定一個目的地,按照矩形移動
switch (dirction)
{
case Dirction.EAST:
pos_x -= move_length;
break;
case Dirction.NORTH:
pos_z += move_length;
break;
case Dirction.WEST:
pos_x += move_length;
break;
case Dirction.SOUTH:
pos_z -= move_length;
break;
}
move_sign = false;
}
this.transform.LookAt(new Vector3(pos_x, 0, pos_z));
float distance = Vector3.Distance(transform.position, new Vector3(pos_x, 0, pos_z));
//當前位置與目的地距離浮點數的比較
if (distance > 0.9)
{
transform.position = Vector3.MoveTowards(this.transform.position, new Vector3(pos_x, 0, pos_z), move_speed * Time.deltaTime);
}
else
{
dirction = dirction + 1;
if(dirction > Dirction.SOUTH)
{
dirction = Dirction.EAST;
}
move_sign = true;
}
}
}
PatrolFollowAction
控制士兵跟隨玩家的動作,如果玩家脫離了追捕範圍或者出了士兵的巡邏區域,則跟隨動作結束並變回巡邏動作。
public class PatrolFollowAction : SSAction
{
private float speed = 2.5f; //跟隨玩家的速度
private GameObject player; //玩家
private PatrolData data; //偵查兵資料
private PatrolFollowAction() { }
public static PatrolFollowAction GetSSAction(GameObject player)
{
PatrolFollowAction action = CreateInstance<PatrolFollowAction>();
action.player = player;
return action;
}
public override void Update()
{
if (data.alive == false) {
this.destroy = true;
this.callback.SSActionEvent(this, 2, this.gameobject);
return;
}
Follow();
//如果偵察兵沒有跟隨物件,或者需要跟隨的玩家不在偵查兵的區域內
if (!data.follow_player || data.wall_sign != data.sign)
{
this.destroy = true;
this.callback.SSActionEvent(this,1,this.gameobject);
}
}
public override void Start()
{
data = this.gameobject.GetComponent<PatrolData>();
}
void Follow()
{
transform.position = Vector3.MoveTowards(this.transform.position, player.transform.position, speed * Time.deltaTime);
this.transform.LookAt(player.transform.position);
}
}
上述兩個動作的切換是通過callBack函式實現的
public override void SSActionEvent(SSAction source, int intParam = 0, GameObject objectParam = null)
{
if(intParam == 0)
{
//偵查兵跟隨玩家
PatrolFollowAction follow = PatrolFollowAction.GetSSAction(objectParam.gameObject.GetComponent<PatrolData>().player);
this.RunAction(objectParam, follow, this);
}
else if (intParam == 1)
{
//偵察兵按照初始位置開始繼續巡邏
GoPatrolAction move = GoPatrolAction.GetSSAction(objectParam.gameObject.GetComponent<PatrolData>().start_position);
this.RunAction(objectParam, move, this);
//玩家逃脫
Singleton<GameEventManager>.Instance.PlayerEscape();
}
else {
}
}
GameEventManager
與事件的釋出相關的管理器。
public class GameEventManager : MonoBehaviour
{
//分數變化
public delegate void ScoreEvent();
public static event ScoreEvent ScoreChange;
//遊戲結束變化
public delegate void GameoverEvent();
public static event GameoverEvent GameoverChange;
//水晶數量變化
public delegate void CrystalEvent();
public static event CrystalEvent CrystalChange;
//玩家射擊
public delegate void Shoot();
public static event Shoot ShootChange;
//玩家逃脫
public void PlayerEscape()
{
if (ScoreChange != null)
{
ScoreChange();
}
}
//玩家被捕
public void PlayerGameover()
{
if (GameoverChange != null)
{
GameoverChange();
}
}
//減少水晶數量
public void ReduceCrystalNum()
{
if (CrystalChange != null)
{
CrystalChange();
}
}
public void shoot() {
if (ShootChange != null) {
ShootChange();
}
}
}
當某一個事件發生時,通過呼叫GameEventManager中的某一個函式來發布這個事件,訂閱這個事件的其他地方將會接收到此次事件,並作出相應操作。
比如控制器檢測到滑鼠點選時通過GameEventManage的shoot釋出射擊事件,訂閱這個事件的音訊管理器將會播放一段射擊的音訊。
可以通過以下方法訂閱:
public AudioClip shootClip;
void OnEnable()
{
GameEventManager.ShootChange += Shoot;
}
void OnDisable()
{
GameEventManager.ShootChange -= Shoot;
}
void Shoot() {
FirstSceneController scene = SSDirector.GetInstance().CurrentScenceController as FirstSceneController;
//在一個玩家的位置播放音樂
AudioSource.PlayClipAtPoint(shootClip, scene.player.transform.position);
}
IUserAction
使用者介面,定義了玩家操作
public interface IUserAction
{
//移動玩家
void MovePlayer(float translationX, float translationZ, Vector3 mousePosition);
//得到分數
int GetScore();
//得到水晶數量
int GetCrystalNumber();
//得到遊戲結束標誌
bool GetGameover();
//重新開始
void Restart();
//射擊
void shoot();
}
碰撞檢測
巡邏兵需要做兩個碰撞檢測:
與玩家直接接觸;檢測玩家進入巡邏範圍。
由於有兩個檢測,所以需要設定兩個碰撞體,一個放在巡邏兵物件上,另一個放在巡邏兵的子物件上,然後分別附加上不同的碰撞處理程式碼。
對於直接接觸的碰撞,則遊戲結束,如果是進入巡邏範圍,則觸發追趕玩家的條件。
工廠模式產生巡邏兵
以及生產了水晶。
private GameObject patrol = null; //巡邏兵
private List<GameObject> used = new List<GameObject>(); //正在被使用的巡邏兵
private GameObject crystal = null; //水晶
private List<GameObject> usedcrystal = new List<GameObject>(); //正在被使用的水晶
private float range = 18; //水晶生成的座標範圍
private Vector3[] vec = new Vector3[9]; //儲存每個巡邏兵的初始位置
public FirstSceneController sceneControler; //場景控制器
public List<GameObject> GetPatrols()
{
int[] pos_x = { -12, 4, 20 };
int[] pos_z = { -20, -3, 12 };
int index = 0;
//生成不同的巡邏兵初始位置
for(int i=0;i < 3;i++)
{
for(int j=0;j < 3;j++)
{
vec[index] = new Vector3(pos_x[i], 0, pos_z[j]);
index++;
}
}
for(int i=0; i < 9; i++)
{
patrol = Instantiate(Resources.Load<GameObject>("Prefabs/Patrol"));
patrol.transform.position = vec[i];
patrol.GetComponent<PatrolData>().sign = i + 1;
patrol.GetComponent<PatrolData>().start_position = vec[i];
used.Add(patrol);
}
return used;
}
public List<GameObject> GetCrystal()
{
for(int i=0;i<12;i++)
{
crystal = Instantiate(Resources.Load<GameObject>("Prefabs/Crystal"));
float ranx = Random.Range(-range, range);
float ranz = Random.Range(-range, range);
crystal.transform.position = new Vector3(ranx, 0, ranz);
usedcrystal.Add(crystal);
}
return usedcrystal;
}
玩家跟隨滑鼠轉動、鍵盤移動
//玩家移動
public void MovePlayer(float translationX, float translationZ, Vector3 mousePosition)
{
if(!game_over)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//Debug.Log(ray);
RaycastHit hitInfo;
if(Physics.Raycast(ray, out hitInfo)){
Vector3 target = hitInfo.point;
//Debug.Log(target);
target.y = player.transform.position.y;
player.transform.LookAt(target);
}
if (translationX != 0 || translationZ != 0)
{
player.GetComponent<Animator>().SetBool("run", true);
}
else
{
player.GetComponent<Animator>().SetBool("run", false);
}
//移動和旋轉
player.transform.Translate(translationX * player_speed * Time.deltaTime, 0, translationZ * player_speed * Time.deltaTime);
}
點選滑鼠射擊
public void shoot() {
player.GetComponent<Animator>().SetBool("run", false);
player.GetComponent<Animator>().SetTrigger("shoot");
Singleton<GameEventManager>.Instance.shoot();
Ray ray = new Ray(player.transform.position, player.transform.forward);
RaycastHit hit;
if (Physics.Raycast(ray, out hit)) {
GameObject obj = hit.transform.gameObject;
if (obj.tag == "Patrol") {
obj.GetComponent<PatrolData>().alive = false;
obj.GetComponent<Animator>().SetBool("run", false);
obj.GetComponent<Animator>().SetBool("dead", true);
}
}
}
相機跟隨玩家移動
public class CameraFlow : MonoBehaviour
{
public GameObject follow; //跟隨的物體
public float smoothing = 5f; //相機跟隨的速度
Vector3 offset; //相機與物體相對偏移位置
public Transform player;
void Start()
{
offset = transform.position - follow.transform.position;
}
void FixedUpdate()
{
Vector3 target = follow.transform.position + offset;
//攝像機自身位置到目標位置平滑過渡
transform.position = Vector3.Lerp(transform.position, target, smoothing * Time.deltaTime);
}
}
專案地址
相關文章
- Unity3D 實現怪物巡邏、按路線行走操作Unity3D
- U3D基礎之人工智慧—敵人巡邏3D人工智慧
- P3629 [APIO2010] 巡邏API
- 羅豔兵:智慧購物中心的雲平臺
- 5G多卡聚合裝置在武警巡邏布控應用方案
- 這個安全系統讓無人機巡邏 超安全還倍兒有面子無人機
- 行算如將兵:智慧計算中的“華為兵法”
- Unity 技術開放日全國巡迴正式開啟,第一站——廈門Unity
- D50 樹的直徑 P3629 [APIO2010] 巡邏API
- 全國首款警察機器人 在鄭州高鐵站正式執行巡邏任務機器人
- 寶鋼寶山基地來了只智慧機器狗,它會幫皮帶機智慧巡檢
- 人工智慧-機器學習-邏輯迴歸人工智慧機器學習邏輯迴歸
- C++ 僱傭兵C++
- C#Light Unity邏輯熱更新解決方案0.20 釋出C#Unity
- C#開發Unity遊戲教程之遊戲物件的行為邏輯方法C#Unity遊戲物件
- oracle巡檢(轉)Oracle
- oracle的巡檢Oracle
- Unity正交相機智慧包圍物體(組)方案Unity
- 開啟“網際網路+”模式打造智慧移動APP巡檢系統模式APP
- mysql巡檢指令碼MySql指令碼
- dba巡檢指令碼指令碼
- Oracle 巡檢手冊Oracle
- sqlserver 巡檢指令碼SQLServer指令碼
- Oracle巡檢內容Oracle
- SQLServer巡檢指令碼SQLServer指令碼
- db2巡檢DB2
- IBM巡檢流程IBM
- oracle巡檢工具-RDAOracle
- AIX巡檢步驟AI
- 麥輪車巡線
- Megacli 批次磁碟巡檢
- 智慧巡檢丨為連鎖餐廳煥新生命力 | Whale 帷幄
- 2021年全球智慧駕駛產業鏈巡:從馬力到算力,All In智慧化時刻來臨產業
- 《PHP 微服務練兵》系列教程PHP微服務
- 紙上談兵: 拓撲排序排序
- 通訊兵(Chain of Responsibility) (轉)AI
- 用 Unity 做個遊戲(八) - 客戶端邏輯結構和網路同步機制Unity遊戲客戶端
- AlphaWar兵棋推演:虛擬硝煙中的AI指揮藝術與決勝智慧AI