NetworkManager网络通讯_Example(一)
---恢复内容开始---
用户手册,范例精讲。
用户手册上给出了一个简单的范例,并指出可以以此为基础进行相开发,再次对范例进行精讲。(NetworkManager对使用unity的轻量级游戏开发有很大帮助,大大解决了开发时间,但是对于吃鸡、农药等游戏估计其整个消息传递机制还是自定义的(此处为猜测))。不做赘述,立马开始(完整脚本均在文末):
1)挂在NetworkManager
如果用过很多插件的,会知道,在使用一个插件之前肯定要挂载一个manager或者controller,NetworkManager也一样,需要关在NetworkManager进行组建数据初始化等。所以新建游戏物体,并添加NetworkManager组件并勾选Dont Destroy on Load。参数中除了network address和port以外,需要对Spawn Info进行一下简单解释。Spawn表示网路范围内产生的游戏物体(根据字面意思以及unity测试自己定义的),比如吃鸡游戏,自己控制人物在别人的场景中也存在,这就是一个Spawn。Player prefab玩家预制,即游戏玩家控制的角色,如果是聊天软件则可以是聊天窗口。RegisteredSpawnablePrefabs表示对需要对网络范围内产生的游戏物体进行注册,如子弹,你打出的子弹需要对方也可以收到,就需要在网络范围内(即所有玩游戏的人场景)产生,他跟Player prefab玩家预制本质上是一样的。
2)挂在NetworkManagerHUD
在NetworkManager所在的游戏物体挂在此组件,此组件为unity自定义的简单UI,运行时会有四个选项:
LANHost:局域网范围内,本机为主机(服务端),同时本机也为客户端。
LANServer:局域网范围内,本机只为主机。
LANClient:局域网范围内,本机为客户端。
Enable。。:(用户手册解释为网络端(外网),并未进行相关测试。点进去就可以进行相关外网连接设置)括号内的解释不严谨,会在下一篇中详细解释
在此说明一下:LANHost与LANServer区别在于本身是否存在一个客户端,这个对于玩儿过CS1.5/1.6的人来说好理解,因为局域网需要一个人见主机,其他人加入,同时它本身也要参与游戏,目前的FPS游戏均为局域网范围外,所以本身只是客户端。
3)玩家生成
添加一个3D游戏物体capsule,并生成预制体,作为一个间的游戏玩家。并拖到NetWorkManager中的PlayerPrefab槽中。对此游戏物体添加NetworkIdentity组件,此组件用来标识游戏玩家身份,只要是场景中每个客户端在交互中实时生成的并可以被广泛看到的均需要此组件。此组件有两个参数,一个人ServerOnly一个是LocalPlayer权限,表示此游戏物是本地有控制权限还是服务端有控制权限(因为服务端和客户端会均存在一个相同的游戏物体,理论上都可以进行控制,所以要对此进行一下区分,后续会讲到怎么处理)。如果需要实时更新位置信息的,则还需要NetWorkTransform组件,来试试更新位置。
4)添加脚本PlayerMove(控制移动开火等)
完整脚本在最后端。
直接添加脚本并在脚本中添加如下代码
float x = Input.GetAxis("Horizontal") * moveSpeed;
float z = Input.GetAxis("Vertical") * moveSpeed;
//transform.Translate(new Vector3(x, 0, z));
GetComponent<CharacterController>().Move(new Vector3(x, , z));
注:原程序用的transform.translate,本文通过添加角色控制器来移动
打包一个程序,运行选择客户端,editor运行选择LanHost。会发现两个场景中均存在两个游戏物体,移动游戏物体则两个游戏物体均移动。而我们需要的是只移动本客户端控制的游戏物体。所以需要作如下修改:
把MonoBehaviour换成NetworkBehaviour(后者继承在前者),然后通过NetworkBehaviour中的isLocalPlayer参数来来判断是否为本地运行,此部分代码如下:
private void Update()
{
if (isLocalPlayer)
{
PlayerMove();
}
} private void PlayerMove()
{
float x = Input.GetAxis("Horizontal") * moveSpeed;
float z = Input.GetAxis("Vertical") * moveSpeed;
//transform.Translate(new Vector3(x, 0, z));
GetComponent<CharacterController>().Move(new Vector3(x, , z));
}
重新运行即为我们想要的效果。
下面为添加子弹:
新建一个球形游戏物体,调整一下初始位置和大小,添加NetworkIdentity和NetWorkTransform组件,并生成预制体,把此预制体拖动到NetworkManager中的RegisteredSpawnablePrefabs中进行注册。当按下F键时表示开火,在update中添加如下代码
if (Input.GetKeyDown(KeyCode.F))
{
GameObject playerBullet = Instantiate(bullet, (*transform.forward + transform.position), Quaternion.identity);
playerBullet.GetComponent<Rigidbody>().velocity = playerBullet.transform.forward * bulletSpeed;
}
运行测试会发现,客户端的两个游戏物体均发射子弹,而且服务端无子弹生成,若增加第二个客户端,则第二个客户端也不会生成子弹。代码放在isLocalPlayer触发的模块下可解决均发射子弹的问题。第二个问题则是client产生游戏物体不会在服务端产生也不会在其他client端产生。如果自己编写消息机制,则需要发送消息告诉服务端和其他client,产生子弹,但是
但是Unet为我们提供好了解决方案,即通过[Command](表示此方法客户端调用,服务端执行,对应的方法开头为Cmd;对应的[ClientRpc]表示服务端调用,客户端执行)让服务端去执行发射子弹命令,并通过NetworkServer.Spawn方法在每个客户端生成
代码如下:
[Command]
private void CmdFire()
{
GameObject playerBullet = Instantiate(bullet, (4*transform.forward + transform.position), Quaternion.identity);
playerBullet.GetComponent<Rigidbody>().velocity = playerBullet.transform.forward * bulletSpeed;
NetworkServer.Spawn(playerBullet);
Destroy(playerBullet, 2f);
}
5)添加血量控制
在Player预制上添加MyCombat脚本以及HealthBar脚本(显示血量)。其中MyCombat中包含两个公共变量maxHealth最大生命值,currentHealth当前生命值。
HealthBar通过最大生命值和当前生命值通过GUI来显示相关数值。HealthBar见本文最后。MyCombat脚本需要一个公共方法,当受到子弹碰撞时减少血量,并当减少到一定数值是死亡或者重新生成,方法如下
public void DecreaseHealth(float delta)
{
currentHealth -= delta; if(currentHealth <= ) { currentHealth = ; } if(currentHealth==)
{
if (isEnemy)
{
Destroy(gameObject);
}
else
{
RpcReset();
currentHealth = maxHealth;
}
}
}
同时我们还需要给子弹添加碰撞体,并添加碰撞事件,完整代码如下:
using UnityEngine; public class MyBullet : MonoBehaviour
{
void OnCollisionEnter(Collision collision)
{
MyCombat combat = collision.collider.gameObject.GetComponent<MyCombat>();
Destroy(gameObject); if(combat != null)
{
combat.DecreaseHealth();
}
}
}
如果我们需要所有数据均在服务端执行则需要如下处理。把MyCombat继承的MonoBehaviour改为NetworkBehaviour,并让DecreaseHealth方法在服务端执行,通过[SyncVar](同步变量,即把服务端的变量值同步到各个客户端)把当前血量同步到各个客户端,同时当期血量低于0时,让player回到初始位置,代码如下
public void DecreaseHealth(float delta)
{
if (!isServer) return; currentHealth -= delta; if(currentHealth <= ) { currentHealth = ; } if(currentHealth==)
{
if (isEnemy)
{
Destroy(gameObject);
}
else
{
RpcReset();
currentHealth = maxHealth;
}
}
} [ClientRpc]//服务端调用,client执行,运动(位置)权限在本地,服务端更改位置无法同步到每个客户端,只有本地更改才可以
private void RpcReset()
{
transform.position = new Vector3(0, 0, 0);
}
6)其他
官方例子中还给出了生成初始敌人的代码,以及客户端控制的Player更改颜色的方法。这两种处理比较简单,NetworkBehaviour中的方法,前者在服务端启动服务时调用,后者在启动游戏玩家时调用,代码分别如下
using UnityEngine;
using UnityEngine.Networking; public class MyEnemySpawner : NetworkBehaviour {
public GameObject enemy;
public int enemyCount = ; public override void OnStartServer()
{
for (int i = ; i < enemyCount; i++)
{
GameObject en = Instantiate(enemy);
en.transform.position = new Vector3(Random.Range(-, ), , Random.Range(-, ));
NetworkServer.Spawn(en);
}
}
}
public override void OnStartLocalPlayer()
{
GetComponent<MeshRenderer>().material.color = Color.red;
}
完整代码:
using UnityEngine.Networking;
using UnityEngine; public class MyPlayerMove : NetworkBehaviour {
public float moveSpeed = 2f;
public float bulletSpeed = 50f;
public GameObject bullet; public override void OnStartLocalPlayer()
{
GetComponent<MeshRenderer>().material.color = Color.red;
} private void Update()
{
if (isLocalPlayer)
{
PlayerMove(); if (Input.GetKeyDown(KeyCode.F))
{
CmdFire();
}
}
} private void PlayerMove()
{
float x = Input.GetAxis("Horizontal") * moveSpeed;
float z = Input.GetAxis("Vertical") * moveSpeed;
//transform.Translate(new Vector3(x, 0, z));
GetComponent<CharacterController>().Move(new Vector3(x, , z));
} [Command]
private void CmdFire()
{
GameObject playerBullet = Instantiate(bullet, (*transform.forward + transform.position), Quaternion.identity);
playerBullet.GetComponent<Rigidbody>().velocity = playerBullet.transform.forward * bulletSpeed;
NetworkServer.Spawn(playerBullet);
Destroy(playerBullet, 2f);
}
}
using UnityEngine;
using UnityEngine.Networking; public class MyCombat : NetworkBehaviour {
public bool isEnemy = false; [SyncVar]
public float currentHealth = maxHealth; public static float maxHealth = ; public void DecreaseHealth(float delta)
{
if (!isServer) return; currentHealth -= delta; if(currentHealth <= ) { currentHealth = ; } if(currentHealth==)
{
if (isEnemy)
{
Destroy(gameObject);
}
else
{
RpcReset();
currentHealth = maxHealth;
}
}
} [ClientRpc]
private void RpcReset()
{
transform.position = new Vector3(, , );
}
}
using UnityEngine; public class HealthBar : MonoBehaviour
{
GUIStyle healthStyle;
GUIStyle backStyle;
MyCombat combat; void Awake()
{
combat = GetComponent<MyCombat>();
} void OnGUI()
{
InitStyles(); // Draw a Health Bar
// 绘制一个血条 Vector3 pos = Camera.main.WorldToScreenPoint(transform.position); // draw health bar background
// 绘制血条背景
GUI.color = Color.grey;
GUI.backgroundColor = Color.grey;
GUI.Box(new Rect(pos.x - , Screen.height - pos.y + , Combat.maxHealth / , ), ".", backStyle); // draw health bar amount
// 绘制当前血量
GUI.color = Color.green;
GUI.backgroundColor = Color.green;
GUI.Box(new Rect(pos.x - , Screen.height - pos.y + , combat.currentHealth / , ), ".", healthStyle);
} void InitStyles()
{
if (healthStyle == null)
{
healthStyle = new GUIStyle(GUI.skin.box);
healthStyle.normal.background = MakeTex(, , new Color(0f, 1f, 0f, 1.0f));
} if (backStyle == null)
{
backStyle = new GUIStyle(GUI.skin.box);
backStyle.normal.background = MakeTex(, , new Color(0f, 0f, 0f, 1.0f));
}
} Texture2D MakeTex(int width, int height, Color col)
{
Color[] pix = new Color[width * height];
for (int i = ; i < pix.Length; ++i)
{
pix[i] = col;
}
Texture2D result = new Texture2D(width, height);
result.SetPixels(pix);
result.Apply();
return result;
}
}
using UnityEngine; public class MyBullet : MonoBehaviour
{
void OnCollisionEnter(Collision collision)
{
MyCombat combat = collision.collider.gameObject.GetComponent<MyCombat>();
Destroy(gameObject); if(combat != null)
{
combat.DecreaseHealth();
}
}
}
using UnityEngine;
using UnityEngine.Networking; public class MyEnemySpawner : NetworkBehaviour {
public GameObject enemy;
public int enemyCount = ; public override void OnStartServer()
{
for (int i = ; i < enemyCount; i++)
{
GameObject en = Instantiate(enemy);
en.transform.position = new Vector3(Random.Range(-, ), , Random.Range(-, ));
NetworkServer.Spawn(en);
}
}
}
---恢复内容结束---
NetworkManager网络通讯_Example(一)的更多相关文章
- NetworkManager网络通讯_NetworkManager(二)
本文主要来实现一下自定UI(实现HUD的功能),并对Network Manger进行深入的讲解. 1)自定义manager 创建脚本CustomerUnetManger,并继承自NetworkMang ...
- NetworkManager网络通讯_破产版NetworkManager(五)
根据对NetWorkServer 以及NetworkClient的理解,编写一个简易版的NetWork Manager.惯例全部代码放在最后 (一)NetWorkServer与NetworkClien ...
- NetworkManager网络通讯_问题汇总(四)
此篇来填坑,有些坑是unet自身问题,而大部分则是理解不准确造成的(或者unity定义太复杂) 问题一: isLocalPlayer 值一直是false 出现场景:NetworkLobbyPlayer ...
- NetworkManager网络通讯_NetworkLobbyManager(三)
此部分可以先建立游戏大厅,然后进入游戏,此处坑甚多耗费大量时间.国内百度出来的基本没靠谱的,一些专栏作家大V也不过是基本翻译了一下用户手册(坑啊),只能通过看youtube视频以及不停的翻阅用户手册解 ...
- NetworkManager网络通讯_networkReader/Writer(六)
unet客户端和服务端进行消息发送时可以采用上一节中方法,也可以直接用networkReader/Writer类进行发送 (一)服务端/客户端注册消息 ; m_Server.RegisterHandl ...
- dicom网络通讯入门(3)
接下来可以进行消息传递了 ,也就是dimse ,再来复习下 什么是dimse .n-set n-create c-echo 这些都是dimse 他们都是属于一种结构的pdu 那就是tf-pdu(传 ...
- dicom网络通讯入门(2)
第二篇,前面都是闲扯 其实正文现在才开始,这次是把压箱底的东西都拿出来了. 首先我们今天要干的事是实现一个echo响应测试工具 也就是echo 的scu,不是实现打印作业管理么.同学我告诉你还早着呢. ...
- 《连载 | 物联网框架ServerSuperIO教程》-4.如开发一套设备驱动,同时支持串口和网络通讯。附:将来支持Windows 10 IOT
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
- Windows 网络通讯开发
Windows 网络通讯开发 一.Windows网络开发API 由于C++标准库中没有网络库,所以进行网络开发的时候要调用系统API.Windows通讯开发API包括以下几个基本函数及成员类型: 1. ...
随机推荐
- 一个selenium简单案例自动添加数据
//本来想着用execl来录入数据的,但是为了尽快完成所以直接搞了个数组 package aldtest; import org.openqa.selenium.*; import org.openq ...
- 【系统设计】分布式唯一ID生成方案总结
目录 分布式系统中唯一ID生成方案 1. 唯一ID简介 2. 全局ID常见生成方案 2.1 UUID生成 2.2 数据库生成 2.3 Redis生成 2.4 利用zookeeper生成 2.5 雪花算 ...
- Mysql INSTR函数
在Mysql中,可以使用INSTR(str,substr)函数,用于在一个字符串(str)中搜索指定的字符(substr),返回找到指定的字符的第一个位置(index),index是从1开始计算,如果 ...
- 阿里云短信服务开发报错Java.lang.NoClassDefFoundError:com/aliyuncs/exceptions/ClientException
手机app获取短信验证码功能时候,遇到的问题.使用的是阿里云的短信服务,下载平台demo时运行不报错,但是在service层调用的时候报错 Java.lang.NoClassDefFoundError ...
- DevExpress GridControl导出ExportToXls 数字类型显示成货币格式
用Dev开发很习惯直接用自带控件导出Excel,现在很少使用原生的Excel API去操作了.除非需要详细的控制. 但别人家封装好的就得按人家的规则的.在使用GridControl导出Excel时发现 ...
- vue解决刷新时闪烁
原文地址:原文地址 1.在vue容器的div里面加上 v-cloak <div id="app" v-cloak> 2.样式文件中加上 <style type=& ...
- Transactional事务提交后触发异步方法
一.问题复现 1.场景 2个service方法, 方法A中调用方法B. 方法A 是核心业务方法,涉及多张表数据变更,为了保持数据一致,用spring事务注解:@Transactional(rollba ...
- Cocos Creator一步一步实现重力球游戏
『 游戏玩法 』 通过手机陀螺仪,调整手机,让球从上一层的间隔中落到下一层,楼层会不断上涨,如果球碰到上方或者下方的火焰,游戏结束. 『 游戏预览 』 『 开发工具 』 1. CocosCreat ...
- springboot redis-cache 自动刷新缓存
这篇文章是对上一篇 spring-data-redis-cache 的使用 的一个补充,上文说到 spring-data-redis-cache 虽然比较强悍,但还是有些不足的,它是一个通用的解决方案 ...
- Failure to transfer org.springframework:spring-jcl:jar:5.0.7.RELEASE from
错误信息: Failure to transfer org.springframework.boot:spring-boot-maven-plugin:pom:1.5.4.RELEASE from h ...