[关闭]
@z77 2018-03-12T01:16:49.000000Z 字数 5975 阅读 1786

Unity联网对战游戏小Demo

--- Bmob Game SDK(BGS) 系列教程(二)

前言

开发3D游戏听起来门槛很高,但是Unity的出现让门槛大大降低。开发联网实时对战的3D游戏门槛就更高,因为即便熟悉掌握了Unity的开发技术,联网的游戏还要涉及到熟悉网络协议栈、掌握后端知识以及面对服务器带来的高额成本。但是Bmob最近在内测一款游戏sdk,让普通开发者开发一款联网实时对战游戏这个梦想变得触手可及。


第一步,准备一个单机的Unity游戏

访问 Unity 的 Asset Store下载游戏项目,并且Import到 Unity 内。

我选择了一款可爱的射击打怪游戏:Survival Shooter Tutorial

游戏图片

项目导入后是这个样子的:
项目图片

下面简要介绍项目结构:

这里不说太详细,想学习更多细节的童鞋可以去看Unity官方的教程


第二步,开始改造

将玩家角色(和角色控制器)克隆一份,去掉主动操作行为,添加被动展现方法。

可以在上一步骤中看到项目中只有一个玩家Player,要改造成联网的游戏就需要多个玩家,所以我把场景中的Player物体克隆一份,命名为Player2,当然控制它的脚本也不能少,克隆克隆克隆!

  1. Player2Health.cs:
    因为Player2Health控制的是其他玩家的血量,所以把玩家收到怪物攻击时减的血量设为0,让其他玩家的血量不受本地控制。
  2. Player2Movement.cs:
    删掉根据键盘的操作引起的移动,加上传入数据时的操作角色移动方法。

    1. // 移动到相应坐标
    2. public void MoveTo(float x, float z){
    3. playerRigidbody.MovePosition (new Vector3(x, playerRigidbody.position.y,z));
    4. Animating (x, z);
    5. }
    6. // 旋转到相应角度
    7. public void TurnTo(float y){
    8. transform.eulerAngles = new Vector3 (0, y, 0);
    9. }
  3. Player2Shooting.cs:
    删掉根据鼠标的操作引起的射击,加上传入射击指令时的方法,考虑到网络延时原因,是否射中怪物的判断不由这里判断,这个射击方法给怪物的伤害要设置为0,仅显示出UI效果。

以上改好后再把对应的脚本替换掉原脚本放到Player2物体上,将物体拖进Project一栏中,这样物体就变成预设物体,可以随时调用啦。除了克隆、修改玩家之外,还需要修改一些细节:

  1. // UnityEngine.AI.NavMeshAgent nav;
  2. // nav = GetComponent <UnityEngine.AI.NavMeshAgent> ();
  3. void Update ()
  4. {
  5. // If the enemy and the player have health left...
  6. if(enemyHealth.currentHealth > 0)
  7. {
  8. Vector3 enemyPosition = transform.position,
  9. tempPosition;
  10. float minDist = float.MaxValue,
  11. tempFloat;
  12. Vector3 target = Vector3.zero;
  13. for (int i = 0; i < targetHealths.Length; i++) {
  14. if (targetHealths [i].currentHealth > 0) {
  15. tempPosition = trackTargets [i].position;
  16. tempFloat = Vector3.Distance (enemyPosition, tempPosition);
  17. if (tempFloat < minDist) {
  18. minDist = tempFloat;
  19. target = tempPosition;
  20. }
  21. }
  22. }
  23. if (minDist != float.MaxValue) {
  24. // ... set the destination of the nav mesh agent to the player.
  25. nav.SetDestination (target);
  26. }
  27. }
  28. // Otherwise...
  29. else
  30. {
  31. // ... disable the nav mesh agent.
  32. nav.enabled = false;
  33. }
  34. }

第三步,结合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";

    1. SceneManager.LoadSceneAsync ("_Complete-Game/_Complete-Game");
  4. 在 Demo Scene 进行SDK的初始化,绑定 delegate 用于处理各种通知;

  5. 将处理事件转发的脚本绑定给本地角色Player,将Player的移动、旋转、hp等数据,调用SDK接口同步到服务器;

    1. void Update ()
    2. {
    3. BmobGame.UpdateFrame ();
    4. if (isOver)
    5. return;
    6. Vector3 position = transform.position;
    7. BmobGame.EditMyStatus ("position", new float[]{ position.x, position.z });
    8. BmobGame.EditMyStatus ("rotation", transform.eulerAngles.y);
    9. BmobGame.EditMyStatus ("hp", GetComponent<PlayerHealthBase>().currentHealth<0?0:GetComponent<PlayerHealthBase>().currentHealth);
    10. }
  6. 将 Player 的瞬时动作射击、射中的怪物名通过transfer接口直接发送到其他玩家;

    1. // Game_BmobSDKTest里面SendFireEvent和SendDamageEvent方法,
    2. // 都是把传来的参数转成byte数组(数组第一位设为事件类别),
    3. // 通过transfer接口传递数组给其他玩家:BmobGame.SendTransferToAllExceptSelf (notify);
    4. // Game_BmobSDKTest mBGS;
    5. // mBGS = GetComponentInParent<Game_BmobSDKTest> ();
    6. // 把发射起点、角度、长度,用transfer接口传
    7. mBGS.SendFireEvent (transform.position.x, transform.position.z, transform.eulerAngles.y, range);
    8. // 把射中的怪物名发送出去,用transfer接口传
    9. mBGS.SendDamageEvent (shootHit.collider.name);
  7. 读取服务器同步的数据,渲染其它玩家的位置、角度。获取其它玩家直接发送的瞬时动作,作出射击和射中某个怪物的处理;

    1. //对收到其他玩家信息的处理
    2. void OnOthersGameStatus (int no, ArrayList attrNames, Hashtable status)
    3. {
    4. Debug.Log ("Player[" + no + "] game status is changed: " + status.Count);
    5. if(attrNames.Contains("position")){
    6. float[] position = status ["position"] as float[];
    7. mOtherPlayers [no].GetComponent<Player2Movement> ().MoveTo (position [0], position [1]) ;
    8. }
    9. if(attrNames.Contains("rotation")){
    10. float y = (float)(status ["rotation"]);
    11. mOtherPlayers [no].GetComponent<Player2Movement> ().TurnTo (y) ;
    12. }
    13. if(attrNames.Contains("hp")){
    14. int hp = (int)(status ["hp"]);
    15. mOtherPlayers [no].GetComponent<Player2Health> ().currentHealth = hp;
    16. }
    17. }
    18. //对收到transfer接口的信息的处理
    19. void OnTransfer (int fromNo, byte[] data)
    20. {
    21. Debug.Log ("Get transfer data flag = " + data[0] + " & len = " + data.Length + " & from: " + fromNo);
    22. switch(data[0]){
    23. case 1:
    24. ReceiveFireEvent (fromNo, data);//开火事件
    25. break;
    26. case 2:
    27. ReceiveDamageEvent (fromNo, data);//击中怪物事件
    28. break;
    29. }
    30. Debug.Log ("Player[" + fromNo + "] transfer: " + data [0] + ", len = " + data.Length);
    31. }
    32. //对收到云端通知的处理
    33. void OnCloudNotifyJson(string jsonStr){
    34. Debug.Log ("Handle cloud notify: " + jsonStr);
    35. JSONNode json = JSON.Parse (jsonStr);
    36. if (json == null) {
    37. return;
    38. }
    39. string a = json ["action"];
    40. if (a == null || a.Length == 0) {
    41. return;
    42. }
    43. if ("gameover".Equals (a)) {
    44. // 游戏结束,3秒后回到房间
    45. Invoke ("BackToRoom", 3);
    46. }
    47. }
  8. 在 BGS官网 登录管理后台,创建游戏,修改服务器运行配置,包括:每秒帧率(默认60Hz)、房间最多玩家数 (2个或以上);

  9. 修改 玩家属性配置,设置各个属性的名称、类型、长度、值域、由云端/客户端编辑、其它玩家是否可见等。我这里仅有hp、position和rotation。
  1. "player": { // 玩家的相关信息
  2. "attributes": { // 玩家在游戏内的属性,下面的都是示例,实际情况由开发者自定义
  3. "hp": { // 玩家的HP
  4. "type": "int", // HP属性类型为数字
  5. "max": 101 // HP的上限,int类型的属性,都可以设置其max,设置得越紧密,运行效率越高
  6. },
  7. "position": {
  8. "type": "float[]",
  9. "count": 2,
  10. "editable": true,
  11. "export": true
  12. },
  13. "rotation": {
  14. "type": "float",
  15. "editable": true,
  16. "export": true
  17. }
  18. }
  19. }

10 . 打开 Eclipse 或 Android Studio,创建Java项目,导入 BmobGame_JavaCloud_vx.x.x_xxxxxx.jar,并创建 Player.java 和 Room.java,分别继承自 PlayerBase.class 和 RoomBase.class 后,编写游戏逻辑代码。

Player.java :

  1. package cn.bmob.gamesdk.server.been;
  2. import cn.bmob.gamesdk.server.api.BmobGameSDKHook;
  3. import cn.bmob.gamesdk.server.api.JSON;
  4. import cn.bmob.gamesdk.server.api.PlayerBase;
  5. public class Player extends PlayerBase {
  6. @BmobGameSDKHook
  7. public native int getHp();
  8. @BmobGameSDKHook
  9. public strictfp void onUpdate_Hp() {
  10. for (Player p : roommates)
  11. if (p.getHp() != 0)
  12. return;
  13. gameOver();
  14. }
  15. private final void gameOver() {
  16. sendToAll(JSON.toJson("action", "gameover").toString().getBytes());
  17. room.dispatchGameOver();
  18. }
  19. }

Room.java :

  1. package cn.bmob.gamesdk.server.been;
  2. import cn.bmob.gamesdk.server.api.RoomBase;
  3. public class Room extends RoomBase{
  4. }

11 . 打包运行游戏,就可以多人同时在线对战啦~

运行效果

Demo测试运行视频 (B站无广告传送门)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注