使用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. 全废话SQL Server统计信息(1)——统计信息简介

    当心空无一物,它便无边无涯.树在.山在.大地在.岁月在.我在.你还要怎样更好的世界?--张晓风<我在> 为什么要写这个内容? 随着工作经历的积累,越来越感觉到,大量的关系型数据库的性能问题 ...

  2. ActiveMQ + NodeJS + Stomp 极简入门

    前提 安装ActiveMQ和Nodejs 测试步骤 1.执行bin\win32\activemq.bat启动MQ服务 2. 打开http://localhost:8161/admin/topics.j ...

  3. 使用 纯JQuery 进行 表单 验证

    对于JavaScript而言,进行表单数据的验证可谓是很有必要的,而且一般我们都会在网页上先进行一下表单验证,然后服务器端再次进行验证,来确保用户提交数据的准确性.下面就来分享一个JQuery实现的表 ...

  4. linux配置java环境变量 转过几个,这个最详细和靠谱

    一. 解压安装jdk 在shell终端下进入jdk-6u14-linux-i586.bin文件所在目录,之后会在当前目录下生成一个jdk1.6.0_14目录二. 需要配置的环境变量 1. PATH环境 ...

  5. Apache Commons Configuration读取xml配置

    近期项目自己手写一个字符串连接池.因为环境不同有开发版本.测试版本.上线版本.每一个版本用到的数据库也是不一样的.所以需要能灵活的切换数据库连接.当然这个用maven就解决了.Apache Commo ...

  6. 聊聊Condition

    本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>的学习笔记. 上面我们说了Lock,那是对synchronized的一种更为面向对象的替代,在原来的synchronized内部 ...

  7. Android的ToggleButton和Switch以及AnalogColok和DigitalColok的用法-android学习之旅(二十)

    ToggleButton 和Switch简介 ToggleButton 和Switch都是继承了Button,所以他们的属性设置和Button差不多. 分别支持的属性 ToggleButton 的属性 ...

  8. UNIX网络编程——客户/服务器程序设计示范(四)

        TCP预先派生子进程服务器程序,accept使用线程上锁保护 我们使用线程上锁保护accept,因为这种方法不仅适用于同一进程内各线程之间的上锁,而且适用于不同进程之间的上锁.        ...

  9. Spark技术内幕:一个图搞定Spark到底有多少行代码

    Spark1.0.0发布一个多月了,那么它有多少行代码(Line of Code, LOC)? 注:代码统计未包含测试,sample.

  10. 关于新版本,iOS10的相关内容,兼容iOS 10 资料整理笔记

    1.Notification(通知) 自从Notification被引入之后,苹果就不断的更新优化,但这些更新优化只是小打小闹,直至现在iOS 10开始真正的进行大改重构,这让开发者也体会到UserN ...