大家要的Unity聯網對戰遊戲小Demo

前言

開發3D遊戲聽起來門檻很高,可是Unity的出現讓門檻大大下降。開發聯網實時對戰的3D遊戲門檻就更高,由於即使熟悉掌握了Unity的開發技術,聯網的遊戲還要涉及到熟悉網絡協議棧、掌握後端知識以及面對服務器帶來的高額成本。可是Bmob最近在內測一款遊戲sdk,讓普通開發者開發一款聯網實時對戰遊戲這個夢想變得觸手可及。java


第一步,準備一個單機的Unity遊戲

訪問 Unity 的 Asset Store下載遊戲項目,而且Import到 Unity 內。json

我選擇了一款可愛的射擊打怪遊戲:Survival Shooter Tutorial後端

項目導入後是這個樣子的:
項目圖片api

下面簡要介紹項目結構:數組

  • 場景上的擺設物體都包括在Environment上,好比圖上的鬧鐘、櫃子等,它們的Layer都設置爲了Shootable字段,代碼在玩家開激光槍、激光碰撞檢測時檢測到Shootable爲Layer的物體纔會觸發碰撞事件。
  • 怪物的生成由EnemyManage來控制,可以管理何種怪物在哪一個出生點按什麼時間間隔出生。
  • 怪物主要由三個腳原本管理,分別是EnemyHealth.cs(管理怪物的hp,被玩家射擊到時扣血,血量小於等於0時死掉)、EnemyMovement.cs(管理怪物的移動,用UnityEngine.AI.NavMeshAgent,把玩家的座標設爲目的地,這樣怪物會按設置的行走速度自動走向玩家位置)和EnemyAttack.cs(管理怪物的自動攻擊,按必定的時間間隔進行發招)。
  • 玩家也主要由三個腳原本管理,分別是PlayerHealth.cs(管理玩家的hp,主要是收到怪物攻擊時掉血)、PlayerMovement.cs(管理玩家的移動,當鍵盤按wsad時上下左右的移動)和PlayerShooting.cs(管理玩家的射擊動做,當鼠標左鍵點擊時對玩家槍口面對的方向發出激光Ray,若是在範圍內碰撞檢測到了怪物則對怪物進行扣血)。

這裏不說太詳細,想學習更多細節的童鞋能夠去看Unity官方的教程服務器


第二步,開始改造

將玩家角色(和角色控制器)克隆一份,去掉主動操做行爲,添加被動展示方法。微信

能夠在上一步驟中看到項目中只有一個玩家Player,要改形成聯網的遊戲就須要多個玩家,因此我把場景中的Player物體克隆一份,命名爲Player2,固然控制它的腳本也不能少,克隆克隆克隆!網絡

image

  1. Player2Health.cs:
    由於Player2Health控制的是其餘玩家的血量,因此把玩家收到怪物攻擊時減的血量設爲0,讓其餘玩家的血量不受本地控制。
  2. Player2Movement.cs:
    刪掉根據鍵盤的操做引發的移動,加上傳入數據時的操做角色移動方法。ide

    //  移動到相應座標
    public void MoveTo(float x, float z){
        playerRigidbody.MovePosition (new Vector3(x, playerRigidbody.position.y,z));
        Animating (x, z);
    }
    
    //  旋轉到相應角度
    public void TurnTo(float y){
        transform.eulerAngles = new Vector3 (0, y, 0);
    }
  3. Player2Shooting.cs:

刪掉根據鼠標的操做引發的射擊,加上傳入射擊指令時的方法,考慮到網絡延時緣由,是否射中怪物的判斷不禁這裏判斷,這個射擊方法給怪物的傷害要設置爲0,僅顯示出UI效果。學習

以上改好後再把對應的腳本替換掉原腳本放到Player2物體上,將物體拖進Project一欄中,這樣物體就變成預設物體,能夠隨時調用啦。除了克隆、修改玩家以外,還須要修改一些細節:

  • 更改怪物的移動方式:

上面咱們有提到怪物是根據玩家的位置來自動尋路的,那如今有兩個玩家了怎麼辦呢?根據玩遊戲的經驗告訴咱們,怪物會跟着離他更近的玩家走喲。下面貼出代碼:

// UnityEngine.AI.NavMeshAgent nav;
// nav = GetComponent <UnityEngine.AI.NavMeshAgent> ();
void Update ()
{
    // If the enemy and the player have health left...
    if(enemyHealth.currentHealth > 0)
    {
        Vector3 enemyPosition = transform.position, 
                tempPosition;
        float minDist = float.MaxValue, 
                tempFloat;
        Vector3 target = Vector3.zero;
        for (int i = 0; i < targetHealths.Length; i++) {
            if (targetHealths [i].currentHealth > 0) {
                tempPosition = trackTargets [i].position;
                tempFloat = Vector3.Distance (enemyPosition, tempPosition);
                if (tempFloat < minDist) {
                    minDist = tempFloat;
                    target = tempPosition;
                }
            }
        }
        if (minDist != float.MaxValue) {
            // ... set the destination of the nav mesh agent to the player.
            nav.SetDestination (target);
        }
    }
    // Otherwise...
    else
    {
        // ... disable the nav mesh agent.
        nav.enabled = false;
    }
}
  • 更改怪物受到傷害減血的觸發方式:

上面提到,其餘玩家射擊到怪物的事件不能在我這裏減血,那麼怪物的血量怎麼控制呢,我把EnemyManager.cs的腳本改了下,把生成的每一個怪物都命名,當檢測到射擊時,把我傷害的怪物的名稱發送給其餘玩家,就能同步好每一個怪物的血量了。

第三步,結合Bmob Game Sdk

  1. 訪問 BGS官網,註冊帳號並下載 Unity SDK、GameCloud SDK;
  2. 將 BmobGame_UnitySDK_vx.x.x_xxxxxx.unitypackage Import 到Unity內;
  3. 修改SDK,將遊戲開始跳轉的 Scene 改成本遊戲的場景"_Complete-Game/_Complete-Game";

    SceneManager.LoadSceneAsync ("_Complete-Game/_Complete-Game");
  4. 在 Demo Scene 進行SDK的初始化,綁定 delegate 用於處理各類通知;
  5. 將處理事件轉發的腳本綁定給本地角色Player,將Player的移動、旋轉、hp等數據,調用SDK接口同步到服務器;

    void Update ()
       {
           BmobGame.UpdateFrame ();
           if (isOver)
               return;
           Vector3 position = transform.position;
    
           BmobGame.EditMyStatus ("position", new float[]{ position.x, position.z });
           BmobGame.EditMyStatus ("rotation", transform.eulerAngles.y);
           BmobGame.EditMyStatus ("hp", GetComponent<PlayerHealthBase>().currentHealth<0?0:GetComponent<PlayerHealthBase>().currentHealth);
       }
  6. 將 Player 的瞬時動做射擊、射中的怪物名經過transfer接口直接發送到其餘玩家;

    // Game_BmobSDKTest裏面SendFireEvent和SendDamageEvent方法,
       // 都是把傳來的參數轉成byte數組(數組第一位設爲事件類別),
       // 經過transfer接口傳遞數組給其餘玩家:BmobGame.SendTransferToAllExceptSelf (notify);
    
       // Game_BmobSDKTest mBGS;
       // mBGS = GetComponentInParent<Game_BmobSDKTest> ();
       
       // 把發射起點、角度、長度,用transfer接口傳
       mBGS.SendFireEvent (transform.position.x, transform.position.z, transform.eulerAngles.y, range);
       
       // 把射中的怪物名發送出去,用transfer接口傳
       mBGS.SendDamageEvent (shootHit.collider.name);
  7. 讀取服務器同步的數據,渲染其它玩家的位置、角度。獲取其它玩家直接發送的瞬時動做,做出射擊和射中某個怪物的處理;

    //對收到其餘玩家信息的處理
    void OnOthersGameStatus (int no, ArrayList attrNames, Hashtable status)
      {
          Debug.Log ("Player[" + no + "] game status is changed: " + status.Count);
    
          if(attrNames.Contains("position")){
              float[] position = status ["position"] as float[];
              mOtherPlayers [no].GetComponent<Player2Movement> ().MoveTo (position [0], position [1]) ;
          }
          if(attrNames.Contains("rotation")){
              float y = (float)(status ["rotation"]);
              mOtherPlayers [no].GetComponent<Player2Movement> ().TurnTo (y) ;
          }
          if(attrNames.Contains("hp")){
              int hp = (int)(status ["hp"]);
              mOtherPlayers [no].GetComponent<Player2Health> ().currentHealth = hp;
          }
      }
      
      //對收到transfer接口的信息的處理
      void OnTransfer (int fromNo, byte[] data)
      {
          Debug.Log ("Get transfer data flag = " + data[0] + " & len = " + data.Length + " & from: " + fromNo);
          switch(data[0]){
          case 1:
              ReceiveFireEvent (fromNo, data);//開火事件
              break;
          case 2:
              ReceiveDamageEvent (fromNo, data);//擊中怪物事件
              break;
          }
          Debug.Log ("Player[" + fromNo + "] transfer: " + data [0] + ", len = " + data.Length);
      }
      
      //對收到雲端通知的處理
      void OnCloudNotifyJson(string jsonStr){
          Debug.Log ("Handle cloud notify: " + jsonStr);
          JSONNode json = JSON.Parse (jsonStr);
          if (json == null) {
              return;
          }
          string a = json ["action"];
          if (a == null || a.Length == 0) {
              return;
          }
          if ("gameover".Equals (a)) {
              // 遊戲結束,3秒後回到房間
              Invoke ("BackToRoom", 3);
          }
      }
  8. 在 BGS官網 登陸管理後臺,建立遊戲,修改服務器運行配置,包括:每秒幀率(默認60Hz)、房間最多玩家數 (2個或以上);
  9. 修改 玩家眷性配置,設置各個屬性的名稱、類型、長度、值域、由雲端/客戶端編輯、其它玩家是否可見等。我這裏僅有hp、position和rotation。

"player": { // 玩家的相關信息

"attributes": {                 // 玩家在遊戲內的屬性,下面的都是示例,實際狀況由開發者自定義
    "hp": {                     // 玩家的HP    
        "type": "int",          // HP屬性類型爲數字
        "max": 101              // HP的上限,int類型的屬性,均可以設置其max,設置得越緊密,運行效率越高
    },
    "position": {
        "type": "float[]",
        "count": 2,
        "editable": true,
        "export": true
    },
    "rotation": {
        "type": "float",
        "editable": true,
        "export": true
    }
}

}

10 . 打開 Eclipse 或 Android Studio,建立Java項目,導入 BmobGame_JavaCloud_vx.x.x_xxxxxx.jar,並建立 Player.java 和 Room.java,分別繼承自 PlayerBase.class 和 RoomBase.class 後,編寫遊戲邏輯代碼。

Player.java :

package cn.bmob.gamesdk.server.been;

import cn.bmob.gamesdk.server.api.BmobGameSDKHook;
import cn.bmob.gamesdk.server.api.JSON;
import cn.bmob.gamesdk.server.api.PlayerBase;

public class Player extends PlayerBase {
    @BmobGameSDKHook
    public native int getHp();

    @BmobGameSDKHook
    public strictfp void onUpdate_Hp() {
        for (Player p : roommates)
            if (p.getHp() != 0)
                return;
        gameOver();
    }

    private final void gameOver() {
        sendToAll(JSON.toJson("action", "gameover").toString().getBytes());
        room.dispatchGameOver();
    }
}

Room.java :

package cn.bmob.gamesdk.server.been;

import cn.bmob.gamesdk.server.api.RoomBase;

public class Room extends RoomBase{
}

11 . 打包運行遊戲,就能夠多人同時在線對戰啦~

運行效果

Demo測試運行視頻 (B站無廣告傳送門)

怎麼樣,會了嗎?
不服來戰,不懂來問!

重要提示

若是你恰好有想法開發Unity/CocosCreator/微信小遊戲項目,並想要挑戰聯網版本;
若是你有必定Unity/CocosCreator/微信小遊戲開發經驗,且熱情度較高;
若是你是想入門遊戲開發、熱愛學習、時間充足、精力充沛的在校大學生;

來找我,加小小琪QQ:2967459363
給你提供免費試用的服務器,以及響應快速的技術支持!!!
(限人數,要儘快)

其餘教程:

放大招!!!落地成盒?教你開發本身的聯網"吃雞"遊戲
如何實現各類遊戲的思路雜想

相關文章
相關標籤/搜索