2016年12月15日 星期四

[Unity教學] 2D連線小遊戲製作 Part.4

這篇會講兩個部分

1.整體遊戲流程控制
2.各別玩家的流程控制

前言:
unity在5.x以後使用了的新的網路連線方法,簡稱UNet

為了解說方便,製作一個遊戲名叫 搶奪20 / Rob20 的連線小遊戲

規則:
兩個人輪流喊1~2個數字,先搶到20的人勝利

看此篇前建議先看前面的篇章喔
連線小遊戲製作part1
連線小遊戲製作part2
連線小遊戲製作part3


這篇是程式碼時間~dadatada~

我們使用enum來做流程個控制,enum就是列舉的意思,把遊戲的流程一一列舉出來,依據需要去切換~

首先呢先在GM.cs裡增加這幾行

public enum Round
{
    non,
    player1,
    player2
}

public enum Process
{
    start,
    waitLogin,
    decidePlayer,
    p1Action,
    p2Action,
    checkWin,
    end,
}

Round是回合,表示現在是哪一個玩家的回合

Process是遊戲流程

解說:

start遊戲初始化
waitLogin等待玩家登入
decidePlayer決定現在是哪個玩家進行動作
p1Action玩家1的回合
p2Action玩家2的回合
checkWin檢查數字是否達到勝利條件20
end遊戲結束


就玩家的角度來說,流程就比較點單,主要是你的回合或他的回合這兩個部分

Player.cs 新增這幾行

public enum Process
{
    start,
    action,
    wait,
    end
}

這裡的Process是玩家的流程:

start遊戲初始化
action自己的回合
wait對方的回合
end遊戲結束

列舉了之後呢,我們希望遊戲達到想要的流程:

1.GM.cs可以控制每個玩家的流程狀態
2.一開始會先等待兩個玩家登入,Player.cs的流程狀態就是start
3.直到有兩個玩家登入時,GM.cs要讓玩家1先開始回合
4.玩家1的回合時玩家1的流程狀態就是action ,玩家2就是wait,反過來也一樣
5.兩個玩家輪流進行遊戲直到達勝利條件,GM.cs就會讓流程狀態變為end,結束遊戲


所以們要在Player.cs裡增加這幾行,讓Server有辦法控制Client的流程狀態

[SyncVar]
public Process process = Process.start;

[SyncVar]
public string SysMsg;

public void SetProcess(Process process)
{
    this.process = process;
}

[SyncVar]會同步Server端的Player.cs的參數到Client端
SysMsg是用來接收Server傳送過來的訊息用的


為了要讓SysMsg的訊息可以顯示在玩家的畫面上,要在Player.cs的Start()與Update()裡增加幾行~

void Start()
{
    if (isServer)
    {
        gm = GameObject.Find("GM").GetComponent<GM>();
        gm.Login(this);
    }
    if (isLocalPlayer)
    {
        txtSysMsg = GameObject.Find("txtSysMsg").GetComponent<Text>();★
        txtMunber = GameObject.Find("txtMunber").GetComponent<Text>();
        btnAddOne = GameObject.Find("btn1").GetComponent<Button>();
        btnAddTwo = GameObject.Find("btn2").GetComponent<Button>();
        btnAddOne.onClick.AddListener(() => CmdAddMunbers(1));
        btnAddTwo.onClick.AddListener(() => CmdAddMunbers(2));
    }
}

void Update()
{
    if (isServer)
        Munber = gm.Mumber;
    if (isLocalPlayer)
    {
        txtSysMsg.text = SysMsg;★
        txtMunber.text = Munber.ToString();
    }
}

有☆表示新增的,其他都依樣~
跟所有介面一樣,要在Start()先抓到實體,在用Update跟新文字的內容


接著換GM.cs,先新增這幾行,這裡我們用Update()來做流程的處理

public Round round = Round.non;
public Process process = Process.start;
int roundCount = 0;

void Start()
{
    process = Process.waitLogin;
}

void Update()
{
    switch (process)
    {
        case Process.waitLogin:
            foreach (Player pl in allPlayer)
                pl.SysMsg = "等待玩家連線...";
            break;

        case Process.decidePlayer:
            roundCount++;
            if (roundCount % 2 == 1)
            {
                round = Round.player1;
                process = Process.p1Action;
            }
            else
            {
                round = Round.player2;
                process = Process.p2Action;
            }
            break;

        case Process.p1Action:
            allPlayer[0].SysMsg = "輪到你了!";
            allPlayer[0].SetProcess(Player.Process.action);
            allPlayer[1].SysMsg = "等待對方...";
            allPlayer[1].SetProcess(Player.Process.wait);
            break;

        case Process.p2Action:
            allPlayer[1].SysMsg = "輪到你了!";
            allPlayer[1].SetProcess(Player.Process.action);
            allPlayer[0].SysMsg = "等待對方...";
            allPlayer[0].SetProcess(Player.Process.wait);
            break;

        case Process.checkWin:
            switch (round)
            {
                case Round.player1:
                    allPlayer[0].SysMsg = "Winner";
                    allPlayer[1].SysMsg = "Loser";
                    break;
                case Round.player2:
                    allPlayer[1].SysMsg = "Winner";
                    allPlayer[0].SysMsg = "Loser";
                    break;
            }
            allPlayer[0].SetProcess(Player.Process.end);
            allPlayer[1].SetProcess(Player.Process.end);
            process = Process.end;
            break;
    }
}

這裡可以發現每個流程都有相對應的case區塊去做處理

首先在遊戲啟動時Start()會先把流程狀態切換成waitLogin,用foreach的方式來告訴已經登入的玩家,目前正等待第2個玩家


那流程狀態如何從 waitLogin 切換到正式開始遊戲呢

要在GM.cs裡的 AddPlayer()增加判斷

public void AddPlayer(ClientLogic cl)
{
    if (process == Process.waitPlayer)
    {
        Players.Add(cl);
        if (Players.Count == 2)
            process = Process.decidePlayer;
    }
}

這樣只要登入玩家有兩個的時候,流程狀態就會變成decidePlayer,就是遊戲正式開始囉

接著在decidePlayer中,主要的任務是決定現在是誰的回合

1.roundCount從0開始每回合+1,用%2來取除以2的餘數
2.如果餘數是1就是玩家1的回合,餘數是0就是玩家2的回合
3.決定是誰的回合後,流程狀態就會切換p1Action或是p2Action


p1Action主要任務是讓玩家1的Player.cs的流程狀態切換為Action,玩家2則是wait
並發"送輪到你了~"或是"等待對方的訊息",反過來也是一樣

而且玩家只有在自己的回合才能喊數字,為了達成這樣的效果,我們要在Player.cs的CmdAddMunbers()裡增加一行判斷

[Command]
public void CmdAddMunbers(int addMun)
{
    if (process == Process.action)★
        gm.AddMunber(addMun);
}

玩家的流程狀態要在action時按下按鈕才能喊數字,其他的流程狀態喊是沒有用的喔。

玩家喊完數字(+1或+2)的時候,就要換另外一位玩家的回合,流程狀態也就接著改變,所以要在GM.cs裡的AddMunber()裡增加一行

public void AddMunber(int addMun)
{
    Mumber += addMun;

    process = Process.decidePlayer;★
}

這樣的話每當玩家喊了數字以後在GM.cs這裡的流程狀態就會變成decidePlayer,經過roundCount的計算與判斷後,就換下一個玩家的回合。

直到數字是20! 那喊到20的那位玩家就贏了,那繼續在AddMunber()裡增加幾行

public void AddMunber(int addMun)
{
    Mumber += addMun;
    if (Mumber >= 20)★
        process = Process.checkWin;★
    else★
        process = Process.decidePlayer;
}

如果Munber大於或等於20,那流程狀態就會切換成checkWin,這時就會判斷目前是誰的回合,然後給贏家發送Winner的訊息,輸家則是發送Loser的訊息


寫到這邊,遊戲已經完成90%囉~~恭喜~

不過還是先編譯成exe來測試看看吧



左邊是Host右邊是另一個玩家,如果你的遊戲畫面跟上面的一樣~那恭喜你!!!


這篇先到這邊結束了~

下一篇就來稍微美化一下遊戲,還有講解Match Maker的用法

先恭喜看到這邊的你~ 遊戲已經接近完成囉!!

1 則留言:

  1. error CS0246: The type or namespace name `ClientLogic' could not be found. Are you missing an assembly reference?

    這是我出現的問題,想了很久都不知道怎樣解決.

    回覆刪除