使用Multiplayer Networking做一个简单的多人游戏例子-1/3

使用Multiplayer Networking做一个简单的多人游戏例子-2/3

使用Multiplayer Networking做一个简单的多人游戏例子-3/3

上一篇中血条还没有同步到所有客户端,下面添加血条同步。主要用到[SyncVar]同步变量。

13. 网络同步血条

  • 打开Health脚本
  • 添加命名空间UnityEngine.Networking
using UnityEngine.Networking;
  • 添加继承关系为NetworkBehaviour
public class Health : NetworkBehaviour
  • 为currentHealth添加[SyncVar]
[SyncVar]
public int currentHealth = maxHealth;
  • 在TakeDamage函数中增加仅在Server端执行
if (!isServer)
{
    return;
}

最终Health脚本如下:

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;

public class Health : NetworkBehaviour {

    public const int maxHealth = 100;

    [SyncVar]
    public int currentHealth = maxHealth;
    public RectTransform healthBar;

    public void TakeDamage(int amount)
    {
        if (!isServer)
        {
            return;
        }

        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            currentHealth = 0;
            Debug.Log("Dead!");
        }

        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }
}

此时运行测试,你会发现只有客户端的血条会变化,所以我们需要修改血条变化方法

  • 打开Health脚本
  • 将血条修改的代码移到新函数OnChangeHealth中
void OnChangeHealth (int currentHealth)
{
    healthBar.sizeDelta = new Vector2(health, currentHealth.sizeDelta.y);
}
  • 修改currentHealth的SyncVar属性
[SyncVar(hook = "OnChangeHealth")]

最终Health代码:

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;

public class Health : NetworkBehaviour {

    public const int maxHealth = 100;

    [SyncVar(hook = "OnChangeHealth")]
    public int currentHealth = maxHealth;

    public RectTransform healthBar;

    public void TakeDamage(int amount)
    {
        if (!isServer)
            return;

        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            currentHealth = 0;
            Debug.Log("Dead!");
        }
    }

    void OnChangeHealth (int health)
    {
        healthBar.sizeDelta = new Vector2(health, healthBar.sizeDelta.y);
    }
}

14. 死亡和再出生

这里会用到[ClientRpc],让函数在服务端调用,执行却在客户端

  • 打开Health脚本
  • 添加出生函数Respawn
[ClientRpc]
void RpcRespawn()
{
    if (isLocalPlayer)
    {
        // move back to zero location
        transform.position = Vector3.zero;
    }
}
  • 修改TakeDamage函数中,currentHealth = maxHealth;
  • 在服务端执行RpcRespawn();

    最终Health代码如下:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;

public class Health : NetworkBehaviour {

    public const int maxHealth = 100;

    [SyncVar(hook = "OnChangeHealth")]
    public int currentHealth = maxHealth;

    public RectTransform healthBar;

    public void TakeDamage(int amount)
    {
        if (!isServer)
            return;

        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            currentHealth = maxHealth;

            // called on the Server, but invoked on the Clients
            RpcRespawn();
        }
    }

    void OnChangeHealth (int currentHealth )
    {
        healthBar.sizeDelta = new Vector2(currentHealth , healthBar.sizeDelta.y);
    }

    [ClientRpc]
    void RpcRespawn()
    {
        if (isLocalPlayer)
        {
            // move back to zero location
            transform.position = Vector3.zero;
        }
    }
}
  • 保存脚本
  • Build新版本,测试

15. 增加敌人

增加敌人发射器用于创建系统玩家

  • 创建一个empty GameObject
  • 修改名称为“Enemy Spawner”
  • 保持Enemy Spawner选中
  • 添加组件Network > NetworkIdentity
  • 勾选NetworkIdentity中the Server Only选中,仅在服务端中执行
  • 给Enemy Spawner添加新脚本“EnemySpawner”

    EnemySpawner内容如下:
using UnityEngine;
using UnityEngine.Networking;

public class EnemySpawner : NetworkBehaviour {

    public GameObject enemyPrefab;
    public int numberOfEnemies;

    public override void OnStartServer()
    {
        for (int i=0; i < numberOfEnemies; i++)
        {
            var spawnPosition = new Vector3(
                Random.Range(-8.0f, 8.0f),
                0.0f,
                Random.Range(-8.0f, 8.0f));

            var spawnRotation = Quaternion.Euler(
                0.0f,
                Random.Range(0,180),
                0.0f);

            var enemy = (GameObject)Instantiate(enemyPrefab, spawnPosition, spawnRotation);
            NetworkServer.Spawn(enemy);
        }
    }
}
  • 保存脚本
  • 回到Unity

下面开始利用Player Prefab创建Enemy

  • 将Player prefab拖到场景中
  • 修改Player名称为“Enemy”
  • 将Enemy拖到Project面板中创建一个新的Enemy prefab预制体
  • 选中Enemy
  • 删除Enemy中的Gun,点击继续
  • 选中Enemy
  • 删除Bullet Spawn
  • 移除PlayerController组件
  • 修改Enemy材质为Black Material
  • 修改Enemy Visor的材质为Default-Material
  • Apply Enemy Prefab
  • 删除场景中的Enemy Prefab
  • 保存场景

将 Enemy prefab 注册到NetworkManager中

  • 选中NetworkManager
  • 展开Spawn Info foldout
  • Registered Spawnable Prefabs点击+号
  • 将Enemy prefab添加到Registered Spawnable Prefabs list
  • 选中场景中的Enemy Spawner
  • 将Enemy prefab拖到Enemy prefab输入框中
  • 设置Number Of Enemies为4

  • 保存工程

build新版本,测试

16. 销毁死亡Enemy

  • 打开Health脚本
  • 添加destroyOnDeath变量
  • TakeDamage中判断销毁或者respawning

    代码如下:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;

public class Health : NetworkBehaviour {

    public const int maxHealth = 100;

    public bool destroyOnDeath;

    [SyncVar(hook = "OnChangeHealth")]
    public int currentHealth = maxHealth;

    public RectTransform healthBar;

    public void TakeDamage(int amount)
    {
        if (!isServer)
            return;

        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            if (destroyOnDeath)
            {
                Destroy(gameObject);
            }
            else
            {
                currentHealth = maxHealth;

                // called on the Server, will be invoked on the Clients
                RpcRespawn();
            }
        }
    }

    void OnChangeHealth (int currentHealth)
    {
        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }

    [ClientRpc]
    void RpcRespawn()
    {        if (isLocalPlayer)
        {
            // Set the player’s position to origin
            transform.position = Vector3.zero;
        }
    }
}
  • 保存脚本,回到Unity
  • 选中Enemy prefab
  • 将Health中Destroy On Death 属性勾选

增加出身点位置

由于Player每次出生位置一样,我们用NetworkStartPosition来增加一个出身点

  • 创建一个empty GameObject
  • 修改名称为“Spawn Position 1”
  • 选中Spawn Position 1
  • 增加组件Network > NetworkStartPosition
  • 设置Transform Position (3, 0, 0)
  • 复制一份Spawn Position 1 GameObject
  • 将复制这份修改名称为“Spawn Position 2”
  • 选中Spawn Position 2
  • 设置Transform Position (-3, 0, 0)
  • 选中Network Manager
  • 展开Spawn Info foldout
  • 修改Player Spawn Method 为 Round Robin

设置出生点位置

  • 打开Health脚本
  • 增加NetworkStartPosition变量
private NetworkStartPosition[] spawnPoints;
  • 增加Start方法
void Start ()
{
    if (isLocalPlayer)
    {
        spawnPoints = FindObjectsOfType<NetworkStartPosition>();
    }
}
  • 修改RpcRespawn中出生点坐标

    最终Health代码如下:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;

public class Health : NetworkBehaviour {

    public const int maxHealth = 100;
    public bool destroyOnDeath;

    [SyncVar(hook = "OnChangeHealth")]
    public int currentHealth = maxHealth;

    public RectTransform healthBar;

    private NetworkStartPosition[] spawnPoints;

    void Start ()
    {
        if (isLocalPlayer)
        {
            spawnPoints = FindObjectsOfType<NetworkStartPosition>();
        }
    }

    public void TakeDamage(int amount)
    {
        if (!isServer)
            return;

        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            if (destroyOnDeath)
            {
                Destroy(gameObject);
            }
            else
            {
                currentHealth = maxHealth;

                // called on the Server, invoked on the Clients
                RpcRespawn();
            }
        }
    }

    void OnChangeHealth (int currentHealth )
    {
        healthBar.sizeDelta = new Vector2(currentHealth , healthBar.sizeDelta.y);
    }

    [ClientRpc]
    void RpcRespawn()
    {
        if (isLocalPlayer)
        {
            // Set the spawn point to origin as a default value
            Vector3 spawnPoint = Vector3.zero;

            // If there is a spawn point array and the array is not empty, pick one at random
            if (spawnPoints != null && spawnPoints.Length > 0)
            {
                spawnPoint = spawnPoints[Random.Range(0, spawnPoints.Length)].transform.position;
            }

            // Set the player’s position to the chosen spawn point
            transform.position = spawnPoint;
        }
    }
}

Download sample code on github

OK,以上就是全部内容。关于详细的使用请看 Networking Overview

使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)的更多相关文章

  1. 使用Multiplayer Networking做一个简单的多人游戏例子-2/3(Unity3D开发之二十六)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/51007512 ...

  2. 使用Multiplayer Networking做一个简单的多人游戏例子-1/3(Unity3D开发之二十五)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/51006463 ...

  3. 使用Multiplayer Networking做一个简单的多人游戏例子-1/2

    原文地址: http://blog.csdn.net/cocos2der/article/details/51006463 本文主要讲述了如何使用Multiplayer Networking开发多人游 ...

  4. 使用Multiplayer Networking做一个简单的多人游戏例子-1/2(换一种方法)

    SynMove.cs using UnityEngine; using System.Collections; using UnityEngine.Networking; public class S ...

  5. 【Bugly干货分享】一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎

    Bugly 技术干货系列内容主要涉及移动开发方向,是由Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 前言 好吧,说是“粒子引擎”还是大言不 ...

  6. 使用React并做一个简单的to-do-list

    1. 前言 说到React,我从一年之前就开始试着了解并且看了相关的入门教程,而且还买过一本<React:引领未来的用户界面开发框架 >拜读.React的轻量组件化的思想及其virtual ...

  7. 【 D3.js 入门系列 --- 3 】 做一个简单的图表!

    前面说了几节,都是对文字进行处理,这一节中将用 D3.js 做一个简单的柱形图. 做柱形图有很多种方法,比如用 HTML 的 div 标签,或用 svg . 推荐用 SVG 来做各种图形.SVG 意为 ...

  8. 一起用HTML5 canvas做一个简单又骚气的粒子引擎

    前言 好吧,说是"粒子引擎"还是大言不惭而标题党了,离真正的粒子引擎还有点远.废话少说,先看demo 本文将教会你做一个简单的canvas粒子制造器(下称引擎). 世界观 这个简单 ...

  9. Jmeter初步使用二--使用jmeter做一个简单的性能测试

    经过上一次的初步使用,我们懂得了Jmeter的安装与初步使用的方法.现在,我们使用Jmeter做一个简单的性能测试.该次测试,提交的参数不做参数化处理,Jmeter各元件使用将在介绍在下一博文开始介绍 ...

随机推荐

  1. AsyncTask函数化的封装,AsyncTask函数式的调用

    AsyncTask在本专栏已经做过详细的解析,但是AsyncTask函数式的调用这个概念对大多数人来说比较陌生.其实本质就是自己封装AsyncTask,让暴露的方法,看不到一点AsyncTask的身影 ...

  2. OpenResty修改Nginx默认autoindex页面

    Nginx的autoindex 命令可以自动列出目录下的文件,一些网站用这个功能做文件下载,但是Nginx又没有提供这个页面的 自定义的功能,后来看到别人提及 ngx_openresty,才想到 bo ...

  3. 常用的DDL语句

    create database mydb1; 创建一个名称为mydb1的数据库. use db_name; 切换数据库 ; show databases; 查看所有的数据库: select datab ...

  4. Android必知必会-长按返回健退出

    背景 平常比较常见的都是一定时间间隔内按两次返回键来退出应用,并且第一次点击会有相应的提示,网上资料比较多,这里写一下,长按返回键退出. 实现 实现的方案常用的有两个: 重写dispatchKeyEv ...

  5. Cocoa中层(layer)坐标系的极简理解

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) Cocoa层的坐标系一直理解的不清晰,现在把它整理总结一下: ...

  6. [Mysql]mysql windows下配置文件

    环境是win7 mysql5.6版本 测试下配置文件是否可用(之前没用过windows下的msyql配置) 修改配置前查询下: mysql> show variables like '%max_ ...

  7. Java学习之栈和堆的区别

    在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配. 当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配 ...

  8. Android简易实战教程--第十九话《手把手教您监听EditText文本变化,实现抖动和震动的效果》

    昨晚写博客太仓促,代码结构有问题,早上测试发现没法监听文本变化!今日更改一下.真心见谅啦,哈哈!主活动的代码已经改好了,看截图这次的确实现了文本监听变化情况. 监听文本输入情况,仅仅限于土司略显low ...

  9. 解决Xshell显示中文乱码的问题

    执行echo $LANG命令输出的是当前的编码方式,执行locale命令得到系统中所有可用的编码方式.要让Xshell不显示乱码,则要将编码方式改为UTF-8. 在Xshell中[file]-> ...

  10. 最简单的基于DirectShow的示例:视频播放器

    ===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...