【遊戲開發】unity教程6 巡邏兵小遊戲

github傳送門:https://github.com/dongzizhu/unity3DLearning/tree/master/hw6/Patrol

視頻傳送門:https://space.bilibili.com/472759319

要求

  • 遊戲設計要求:
    • 創建一個地圖和若干巡邏兵(使用動畫);
    • 每個巡邏兵走一個3~5個邊的凸多邊型,位置數據是相對地址。即每次確定下一個目標位置,用自己當前位置爲原點計算;
    • 巡邏兵碰撞到障礙物,則會自動選下一個點爲目標;
    • 巡邏兵在設定範圍內感知到玩家,會自動追擊玩家;
    • 失去玩家目標後,繼續巡邏;
    • 計分:玩家每次甩掉一個巡邏兵計一分,與巡邏兵碰撞遊戲結束;
  • 程序設計要求:
    • 必須使用訂閱與發佈模式傳消息
      • subject:OnLostGoal
      • Publisher: ?
      • Subscriber: ?
    • 工廠模式生產巡邏兵

 

訂閱與發佈模式

爲什麼需要訂閱與發佈模式

就像我們訂閱報刊,有什麼時事新聞出現,報社(發佈者)可以第一時間印刷數百份乃至數千份報紙(傳播媒介),發到各個訂閱了該報紙的人(訂閱者)手上,這樣就完成了信息傳播。

與觀察者模式的區別

在觀察者模式中,觀察者需要直接訂閱目標事件;在目標發出內容改變的事件後,直接接收事件並作出響應,往往發佈者需要對所有的觀察者維持一個列表,從而在每次發生變化時通知所有觀察者。在發佈訂閱模式中,發佈者和訂閱者之間多了一個發佈通道;一方面從發佈者接收事件,另一方面向訂閱者發佈事件;訂閱者需要從事件通道訂閱事件,以此避免發佈者和訂閱者之間產生依賴關係,從而降低代碼的耦合性。

在本次遊戲中的應用

我們通過訂閱與發佈模式,讓巡邏兵將自己的狀態傳遞展示出來,然後由EventManager觀察並做出反應。

 

代碼

其他的模式如工廠模式產生士兵,以及MVC架構這裏就不再贅述了,感興趣的讀者可以看之前的博客。

這裏主要講一下訂閱與發佈的模式以及巡邏兵的行動邏輯。

    void Update () {
		//Debug.Log("here1");
        if (Vector3.Distance(gameStatusOp.getHeroPosition().position, gameObject.transform.position) <= 10f)
        {   
            if (!isCatching)
            {
                isCatching = true;                
            }
            
            addAction.addDirectMovement(this.gameObject);
        }
        else
        {
            if (isCatching)
            {
                //stop catching 
                gameStatusOp.heroEscapeAndScore();
                isCatching = false;
				addAction.addRandomMovement(this.gameObject, false);     
				        
            }
            else
				addAction.addRandomMovement(this.gameObject, true);        
        }
    }

在PatrolControl中,如果hero出現在當前patrol的附近,那麼開始追逐;如果hero離開了,那麼停止追逐,同時將hero逃跑的信息通過firstController也就是之類的gameStatusOp發佈。然後在決定當前狀態後,和之前一樣(MVC模式),調用actionManager來控制移動。

void OnCollisionStay(Collision e)
    {
        if (e.gameObject.name.Contains("Patrol"))
        {
            isCatching = false;
            addAction.addRandomMovement(this.gameObject, false);
            gameStatusOp.heroEscapeAndScore();
        }

        if (e.gameObject.name.Contains("hero"))
        {
            gameStatusOp.patrolHitHeroAndGameover();
            Debug.Log("Game Over!");
        }
    }

當與物體相撞時,如果是和其他巡邏兵撞在一起,那麼同樣停止追逐,同時發佈逃跑狀態;如果撞到了hero,則發佈遊戲結束的狀態。

public class GameEventManager : MonoBehaviour {
    public delegate void GameScoreAction();
    public static event GameScoreAction myGameScoreAction;

    public delegate void GameOverAction();
    public static event GameOverAction myGameOverAction;

    private FirstControl scene;

    void Start () {
        scene = (FirstControl)Director.getInstance().sceneCtrl;
        scene.gameEventManager = this;
    }
	
	void Update () {
		
	}

    //hero escape
    public void heroEscapeAndScore() {
        if (myGameScoreAction != null)
            myGameScoreAction();
    }

    //hero gets caught
    public void patrolHitHeroAndGameover() {
        if (myGameOverAction != null)
            myGameOverAction();
    }
}

而每次發佈的狀態都有eventManager訂閱,在被調用相應的函數後實現對遊戲狀態和分數的調整。

至於巡邏兵的巡邏軌跡則主要由下一個方向決定。我將每個巡邏兵上一次行動的方向保存在PatrolLastDir中,將行動的距離保存在PatrolMoveLength中,然後在非追逐狀態下,巡邏兵會走一個一定距離的矩形。

int getRandomDirection(int index, bool isActive) {
        int randomDir;
		
		if (!isActive) {    
            if(PatrolLastDir[index] < 2)
				randomDir = PatrolLastDir[index] + 1;
			else
				randomDir = -1;
			PatrolLastDir[index] = randomDir;
			PatrolMoveLength[index] = 0;
        }
        else {              
			PatrolMoveLength[index]++;
			Debug.Log(PatrolMoveLength[index]);
			if(PatrolMoveLength[index] > 1000f){
				if(PatrolLastDir[index] < 2)
					randomDir = PatrolLastDir[index] + 1;
				else
					randomDir = -1;
				PatrolLastDir[index] = randomDir;
				PatrolMoveLength[index] = 0;
			}
			else{
				randomDir = PatrolLastDir[index];
			}	
				
        }
        return randomDir;
    }

其他具體的代碼就不貼上來了,感興趣的請移步github。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章