猴子原创,欢迎转载。转载请注明: 转载自Cocos2Der-CSDN,谢谢!

原文地址: http://blog.csdn.net/cocos2der/article/details/51007512

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

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

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

7. 在网络中控制Player移动

上一篇中,玩家操作移动会同时控制同屏内的所有Player,且只有自己的屏幕生效。因为咱们还没有同步Transform信息。

下面我们通过UnityEngine.Networking组件来实现玩家控制各自Player

  • 打开PlayerController脚本
  • 添加命名空间UnityEngine.Networking
using UnityEngine.Networking;
  • 修改MonoBehaviour为NetworkBehaviour
public class PlayerController : NetworkBehaviour
  • 在Update函数中添加如下方法
if (!isLocalPlayer)
{
    return;
}

最后你的PlayerController内容如下:

using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    void Update()
    {
        if (!isLocalPlayer)
        {
            return;
        }

        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
        var z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;

        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);
    }
}
  • 保存脚本
  • 回到Unity中
  • 选中Player prefab在Project面板中
  • 保持Player prefab为选中状态
  • 添加组件Network > NetworkTransform
  • 保存工程

NetworkTransform用于在网络中同步所有Client信息。加上isLocalPlayer判断,只让当前客户端操作。

8. 测试网络中多玩家移动

  • 同样Build一个Mac standalone application作为主机运行
  • 点击LAN Host作为主机开始游戏
  • 运行Unity,点击LAN Client作为另一个客户端加入游戏
  • 点击各自的WASD移动各自Player

9. 区分各自的Player

上面中两个Player外观一致,我们修改自己Player的颜色

  • 打开PlayerController脚本
  • 添加OnStartLocalPlayer方法
public override void OnStartLocalPlayer()
{
    GetComponent<MeshRenderer>().material.color = Color.blue;
}

最终PlayerController:

using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    void Update()
    {
        if (!isLocalPlayer)
        {
            return;
        }

        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
        var z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;

        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);
    }

    public override void OnStartLocalPlayer()
    {
        GetComponent<MeshRenderer>().material.color = Color.blue;
    }
}
  • Build新的Mac版本,测试效果

  • 关闭Mac版本,停止运行Unity,回到编辑状态

10. 给Player添加射击武器

创建子弹

  • 创建一个Sphere GameObject
  • 修改名称为“Bullet”
  • 选中Bullet对象
  • 修改Transform (0.2, 0.2, 0.2)
  • 添加组件Physics > Rigidbody
  • 在Rigidbody属性中取消Use Gravity
  • 拖拽Bullet到Project面板中,制作为Prefab
  • 删除场景中Bullet
  • 保存场景

下面修改PlayerController添加发射子弹

  • 打开PlayerController脚本
  • 添加public变量bulletPrefab
public GameObject bulletPrefab;
  • 添加子弹local发射点
public Transform bulletSpawn;
  • 在Update中加入输入源
if (Input.GetKeyDown(KeyCode.Space))
{
    Fire();
}
  • 添加Fire方法
void Fire()
{
    // Create the Bullet from the Bullet Prefab
    var bullet = (GameObject)Instantiate (
        bulletPrefab,
        bulletSpawn.position,
        bulletSpawn.rotation);

    // Add velocity to the bullet
    bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 6;

    // Destroy the bullet after 2 seconds
    Destroy(bullet, 2.0f);
}

最终PlayerController如下:

using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    public GameObject bulletPrefab;
    public Transform bulletSpawn;

    void Update()
    {
        if (!isLocalPlayer)
        {
            return;
        }

        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
        var z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;

        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);

        if (Input.GetKeyDown(KeyCode.Space))
        {
            Fire();
        }
    }

    void Fire()
    {
        // Create the Bullet from the Bullet Prefab
        var bullet = (GameObject)Instantiate(
            bulletPrefab,
            bulletSpawn.position,
            bulletSpawn.rotation);

        // Add velocity to the bullet
        bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 6;

        // Destroy the bullet after 2 seconds
        Destroy(bullet, 2.0f);
    }

    public override void OnStartLocalPlayer ()
    {
        GetComponent<MeshRenderer>().material.color = Color.blue;
    }
}
  • 保存脚本
  • 回到Unity

下面开始针对变化过的PlayerController修改Player Prefab

  • 将Player Prefab拖拽到场景中
  • 保持Player prefab选中
  • 创建一个Cylinder圆柱体作为其Child
  • 修改Cylinder名称为“Gun”
  • 保持Gun被选中
  • 移除Capsule Collider组件
  • 设置Transform Position (0.5, 0.0, 0.5)
  • 设置Transform Rotation (90.0, 0.0, 0.0)
  • 设置Transform Scale (0.25, 0.5, 0.25)
  • 设置Material为Black Material

最后Player效果:

  • 保持Player选中状态
  • 创建一个empty GameObject作为Child
  • 修改empty GameObject名称为“Bullet Spawn”
  • 设置Bullet Spawn Position (0.5, 0.0, 1.0)

主要是将子弹发射点Bullet Spawn设置到枪口处

  • 保持Player Prefab选中
  • 将Bullet prefab拖到PlayerController中的Bullet Prefab 框
  • 将Player的Child Bullet Spawn拖到PlayerController中的Bullet Spawn 框
  • 保存工程
  • Build新的Mac版本,并测试

你会发现空格键可以在各自场景中发射子弹,但是子弹没有出现在对方场景中。

  • 关闭Mac版本
  • 停止Unity,回到编辑模式

11. 增加多人射击

下面我们会将Bullet prefab注册到NetworkManager

  • 选中Bullet prefab在Project面板中
  • 保存Bullet prefab选中
  • 添加组件Network > NetworkIdentity
  • 添加组件Network > NetworkTransform
  • 设置NetworkTransform中Network Send Rate为0

子弹不会中途改变方向,所以我们不需要每帧更新位置,每个客户端自己计算Bullet坐标信息,所以将Network Send Rate设置为0,网络不需要同步坐标信息。

  • 选中NetworkManager在Hierarchy面板中
  • 保持NetworkManager选中
  • 展开Spawn Info
  • 点击Registered Spawnable Prefabs右下角+
  • 将Bullet Prefab加入到Registered Spawnable Prefabs中

  • 打开PlayerController脚本

注意[Command]可以声明一个函数可以本客户端调用,但是会在服务端(主机)执行。

  • 添加[Command]给Fire函数
  • 修改Fire函数名称为“CmdFire”
[Command]
void CmdFire()
  • Update函数中修改调用为CmdFire
CmdFire();
  • 在CmdFire函数中添加NetworkServer.Spawn方法来创建bullet
NetworkServer.Spawn(bullet);

最终PlayerController如下:

using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    public GameObject bulletPrefab;
    public Transform bulletSpawn;

    void Update()
    {
        if (!isLocalPlayer)
        {
            return;
        }

        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
        var z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;

        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);

        if (Input.GetKeyDown(KeyCode.Space))
        {
            CmdFire();
        }
    }

    // This [Command] code is called on the Client …
    // … but it is run on the Server!
    [Command]
    void CmdFire()
    {
        // Create the Bullet from the Bullet Prefab
        var bullet = (GameObject)Instantiate(
            bulletPrefab,
            bulletSpawn.position,
            bulletSpawn.rotation);

        // Add velocity to the bullet
        bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 6;

        // Spawn the bullet on the Clients
        NetworkServer.Spawn(bullet);

        // Destroy the bullet after 2 seconds
        Destroy(bullet, 2.0f);
    }

    public override void OnStartLocalPlayer ()
    {
        GetComponent<MeshRenderer>().material.color = Color.blue;
    }
}
  • 保存脚本
  • 回到Unity
  • Build新版本Mac,测试

此时应该可以看到子弹同步到了每个玩家场景中

  • 关闭Mac版本
  • 停止运行Unity,回到编辑模式

添加玩家生命值

  • 给Bullet Prefab添加一个新脚本“Bullet”
  • 打开Bullet脚本
  • 添加碰撞函数
using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour {

    void OnCollisionEnter(Collision collision)
    {
        Destroy(gameObject);
    }
}

此时子弹碰撞到Player之后自动销毁

添加玩家生命值

- 给Player prefab添加一个新脚本“Health”

Health脚本如下:

using UnityEngine;

public class Health : MonoBehaviour
{
    public const int maxHealth = 100;
    public int currentHealth = maxHealth;

   public void TakeDamage(int amount)
    {
        currentHealth -= amount;
        if (currentHealth <= 0)
        {
            currentHealth = 0;
            Debug.Log("Dead!");
        }
    }
}
  • 保存脚本

在Bullet中增加击中受伤

  • 修改Bullet中的OnCollisionEnter函数
using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour {

    void OnCollisionEnter(Collision collision)
    {
        var hit = collision.gameObject;
        var health = hit.GetComponent<Health>();
        if (health  != null)
        {
            health.TakeDamage(10);
        }

        Destroy(gameObject);
    }
}

添加一个简易的玩家头顶血条

  • 在场中中创建一个UI Image
  • 修改Canvas名称为“Healthbar Canvas”
  • 修改Image名称为“Background”
  • 保存Background选中
  • 设置RectTransform Width 100
  • 设置RectTransform Height 10
  • 修改Source Image 为 built-in InputFieldBackground
  • 修改 Image Color 为红色
  • 不要修改Background的中心点和锚点
  • 复制一份Background
  • 修改复制出来的Background名称为Foreground
  • 将Foreground设置为Background的Child
  • 将Player prefab拖到场景中
  • 将Healthbar Canvas拖到Player中作为Child

    整个Player结构如下:

  • 选中Foreground

  • 设置Foreground Image为绿色
  • 将Foreground 中心点和锚点修改为Middle Left(用于血条从左到右填充)

  • 选中Healthbar Canvas

  • 点击RectTransform设置按钮(小齿轮)中的Reset
  • 设置RectTransform Scale (0.01, 0.01, 0.01)
  • 设置RectTransform Position (0.0, 1.5, 0.0)
  • 选中Player,点击apply,保存Player Prefab
  • 保存场景

修改Health脚本,控制血条

最终Health脚本如下:

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

public class Health : MonoBehaviour {

    public const int maxHealth = 100;
    public int currentHealth = maxHealth;
    public RectTransform healthBar;

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

        healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    }
}
  • 保存脚本
  • 回到Unity
  • 保持Player选中
  • 将Foreground拖到Healthbar输入框中

  • apply Player prefab
  • 删除场景中的Player
  • 保存场景

最后,修改Healthbar永远朝向主摄像机

- 给Player prefab中的Healthbar Canvas添加新脚本“Billboard”

Billboard脚本如下:

using UnityEngine;
using System.Collections;

public class Billboard : MonoBehaviour {

    void Update () {
        transform.LookAt(Camera.main.transform);
    }
}
  • 编译新Mac版本,测试

你会发现血条只在本地变化了,没有同步到所有玩家。

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

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

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

  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. 通过Dapr实现一个简单的基于.net的微服务电商系统(十六)——dapr+sentinel中间件实现服务保护

    dapr目前更新到了1.2版本,在之前4月份的时候来自阿里的开发工程师发起了一个dapr集成Alibaba Sentinel的提案,很快被社区加入到了1.2的里程碑中并且在1.2 release 相关 ...

  6. 通过Dapr实现一个简单的基于.net的微服务电商系统(十九)——分布式事务之Saga模式

    在之前的系列文章中聊过分布式事务的一种实现方案,即通过在集群中暴露actor服务来实现分布式事务的本地原子化.但是actor服务本身有其特殊性,场景上并不通用.所以今天来讲讲分布式事务实现方案之sag ...

  7. 通过Dapr实现一个简单的基于.net的微服务电商系统(十八)——服务保护之多级缓存

    很久没有更新dapr系列了.今天带来的是一个小的组件集成,通过多级缓存框架来实现对服务的缓存保护,依旧是一个简易的演示以及对其设计原理思路的讲解,欢迎大家转发留言和star 目录:一.通过Dapr实现 ...

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

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

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

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

随机推荐

  1. 在Windows cmd中计算行数

    本文主体来自这篇外文文章的翻译.原文中有一个副标题:"如何简单地用Windows自带的FIND在CMD.exe中计算行数" 当我们在命令行环境中工作时,能计算其它工具输出内容的行数 ...

  2. Mac小技巧:强制退出程序的六种方法

    原帖地址: http://www.cnbeta.com/articles/175447.htm 1.使用键盘快捷键强制退出处于活跃状态的Mac程序 快捷键:Command+Option+Shift+E ...

  3. PGM:图模型学习概述

    http://blog.csdn.net/pipisorry/article/details/52571640 动机 前面我们讨论的问题出发点是给定一个图模型.如在独立性和推理讨论中,假定模型--结构 ...

  4. 游戏引擎cocos2d-android使用大全

    做手机游戏需要三个核心的类,即:SurfaceView,SurfaceHolder,Thread.帧数要在30帧左右是最好的. cocos2d游戏引擎 封装好的框架,可直接使用 cocos2d-and ...

  5. Android中的语言和字符串资源

    在任何情况下,从您的应用代码中提取 UI 字符串并将其存放在外部文件中都是个好办法.Android 在每个 Android 项目中都提供一个资源目录,从而简化了这一过程. 如果您是使用 Android ...

  6. 2.Lucene3.6.2包介绍,第一个Lucene案例介绍,查看索引信息的工具lukeall介绍,Luke查看的索引库内容,索引查找过程

     1  Lucen目录介绍 2  lucene-core-3.6.2.jar是lucene开发核心jar包 contrib  目录存放,包含一些扩展jar包 3  案例 建立第一个Lucene项目 ...

  7. Tomcat内核之类加载器工厂

    Java虚拟机利用类加载器将类载入内存,以供使用.在此过程中类加载器要做很多的事情,例如读取字节数组.验证.解析.初始化等.而Java提供的URLClassLoader类能方便地将jar.class或 ...

  8. UNIX网络编程——使用线程的TCP回射服务器程序

    同一进程内的所有线程除了共享全局变量外还共享: (1)进程指令: (2)大多数数据: (3)  打开的文件(即描述符): (4)信号处理函数和信号处置: (5)当前工作目录: (6)用户ID和组ID. ...

  9. 【一天一道LeetCode】#326. Power of Three

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...

  10. Java采用JDBC的方式连接Hive(SparkSQL)

    前两天,由于系统的架构设计的原因,想通过Java直接访问Hive数据库,对于我这个Java以及Hadoop平台的菜鸟来说,的确是困难重重,不过,还好是搞定了.感觉也不是很麻烦.这篇文章,作为一个感想记 ...