【零基础教学】Unet局域网联机的实现——最基础的Unity联网实现方式(2)
第二部分——主角的创建
学习前说明:
项目源码:链接:https://pan.baidu.com/s/1g78L9QODXdRjoVcm-odRSg 密码:0pzo
源码引用自Siki老师的Unet基础系列教程,文章主要以解释为主,后期会添加一些Siki老师源码以外的新东西,敬请期待。
文中用红色显示的内容为我自己命名的关键名词,例如场景名、代码名、阐述代码或方法所实现的功能 等等
文中用蓝色显示的内容为UI元素、组件名或是变量名,例如Button、InputField、NetworkManager 等等
文中用紫色显示的内容为当前所解释的代码部分。例如:
这里使用一个Console命令完成输出语句
public void ShowMessage()
{
Console.WriteLine("Hello World.");
}
本文中仅讨论Unet用于局域网的情况
如有互联网联网需求,请自行查询Unity与Socket协议结合相关的文章,不推荐使用Unet完成
本文主要服务于渤海大学交互式虚拟现实开发基地,为求尽可能的细致和易懂,可能有些地方写的过于冗杂,不喜勿喷
需求分析
1.W/S键能够完成向前/向后的移动
2.左右键能够完成朝向的旋转
3.空格键能够完成开枪的操作,每发子弹造成10点的伤害
4.头上显示血量,每个人基础血量为100
常用内容(void)
1.继承自NetworkBehaviour而非MonoBehaviour
2.初始化放在OnStartLocalPlayer中而不是Start中
3.Update中放入 if(isLocalPlayer==false) return; 检查是不是本机
4.[SyncVar]修饰的变量,一般用于可以和服务端或是其他客户端交互发生变化的变量。强调具有影响性。比如我的生命值(可被敌人伤害减少),我的攻击力(可以影响伤害敌人的效果),我的级别(可以影响我的伤害值、体型)等等
5.[SyncVar=hook"方法名"]修饰的变量,用于在变量变化后调用的方法。强调依赖性。比如升级时弹出技能升级面板、血量为0后屏幕变灰等等
6.[Command]修饰的方法,一般用于客户端所能操控的物体。强调主动性。比如我主动开枪,我主动移动,我主动生成随从等等
7.[ClientRpc]修饰的方法,一般用于客户端受到的效果。强调被动性。比我我被敌人命中,我被杀死,我重生(被恢复满状态并被移动到指定位置)等等。一般配合 if(isServer==false)return; 使用
8.NetworkServer.Spawn()方法,一般用于生成附属于自己的物体。比如小兵(可以帮自己打敌人),子弹(造成伤害给自己加经验)、棋子(能够判断自己胜利)等等
9.NetworkTransform组件,一般用于当前控制角色的位移,比如主角的移动,汽车的移动。但是仅能实现当前Transform的变换,不会影响到子物体。比如汽车开车时,其他客户端仅能看到车整体的移动,看不到车轮的移动。
实例分析
创建角色
1.创建主角对象,这里使用了Capsule、Cube和Cylinder分别代表身体、眼镜和枪搭成一个简单的人物,注意Cube的Z轴要为正值,这样才能保证游戏对象是面向“前方”的,眼镜是戴在眼镜上而不是后脑勺。
2.创建一个Canvas,并将它的Render Mode修改为World级别,将Scale调至(0.01,0.01,0.01),并通过修改Position以调整至主角的头上方。
3.为主角添加一个Slider以表示血条,将其中的Fill Area和Handle Slide Area删去,仅保留Background,
========》
4.修改血条的背景色为红色,填充色为绿色,满血时就是绿色,每掉一点血,就会露出一点红色。
血条面向摄像机
新建脚本LookAtCamera,并挂在Player下的Canvas上。
Camera.main 代表带有MainCamera 标签的Camera组件,一般一个场景中只有一个。
transform.LookAt(目标transform),使物体的正方向中心始终面对这个点,也就是“看向”目标点。
public class LookAtCamera : MonoBehaviour {// Update is called once per framevoid Update () {transform.LookAt(Camera.main.transform);}
}
角色控制
1.新建一个脚本Player Controller挂在Player下,并继承自NetworkBehaviour。当继承自NetworkBehaviour时,自动添加一个组件Network Identity。
这是角色控制的完整代码。
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
/// <summary>
/// NetworkBehaviour是Unet联网过程中,所有可操控对象必须继承的类
/// 他提供了额外的生命周期函数及联网相关的方法与属性、特性
/// NetworkBehaviour本身是继承自MonoBehaviour
/// </summary>
public class PlayerController : NetworkBehaviour
{public GameObject bulletPrefab;public Transform bulletSpawn;// Update is called once per framevoid Update(){if (isLocalPlayer == false)//isLocalPlayer用于判断是否是当前客户端进行操作,以防止误操作别人的角色{return;//如果不是当前对象,则不执行下述代码}float h = Input.GetAxis("Horizontal");float v = Input.GetAxis("Vertical");//transform.Rotate用于控制物体的旋转,//transform.Rotate(旋转的轴向*单位时间内旋转的角度*旋转的速度)//当前为匀速转动transform.Rotate(Vector3.up * (h * 120) * Time.deltaTime);//transform.Translate用于控制物体的位移//transform.Translate(位移的方向*单位时间内位移的距离*位移的速度)//当前为匀速位移transform.Translate(Vector3.forward * v * 3 * Time.deltaTime);//点击空格时,开火if (Input.GetKeyDown(KeyCode.Space)){CmdFire();}}/// <summary>/// 当客户端第一次进入游戏场景时调用/// </summary>public override void OnStartLocalPlayer(){//将自己变蓝以区别敌我GetComponent<MeshRenderer>().material.color = Color.blue;}// 凡是希望从一个客户端发出,在各个客户端都能看到的效果,例如开火,都需要使用Command特性,同时方法名需要以Cmd开头[Command]void CmdFire()//实际上这个方法在server里面调用,我们只是发出请求,需要记住,目前不要求理解{//生成个子弹GameObject bullet = Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation) as GameObject;//子弹向前飞bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 10;//两秒后自动消失Destroy(bullet, 2);//关键部分:将当前的物体(bullet)分发到各个客户端,这样其他的客户端才能看到子弹NetworkServer.Spawn(bullet);}}
几个地方需要注意(从上至下)
在Update开始时,必须先检查isLocalPlayer是不是为false,也就是保证自己只能控制自己的角色,否则一个人点击后,所有人都在动
if (isLocalPlayer == false)
{
return;
}
OnStartLocalPlayer会在此客户端加入场景时调用,一般我们不在联网的对象上使用Awake(),Start()方法,可以认为OnStartLocalPlayer就是用于替代Start()方法的,同样的,这个方法也是第一步调用,且只能够调用一次。
此方法需重载(override)
public override void OnStartLocalPlayer()
{
GetComponent<MeshRenderer>().material.color = Color.blue;
}
- [Command]特性--用于从客户端发出指令,在服务器端执行。
同时,它修饰的方法,方法名前面要加上Cmd前缀。凡是你希望对其他客户端的操作,无论是攻击其他客户端还是自己生成一个新物体(自己生成新物体,也可以理解为通知其他客户端自己有这个物体),都需要这样编写。
[Command]
void CmdFire()
{//function
}
NetworkServer.Spawn() -- 将物体显示在各个客户端上。正常的小兵、子弹等等这种附属关系的物体都需要以下的步骤
在Lobby场景中,先完成物体的注册。只有注册了的物体才能够使用NetworkServer.Spawn()
使用Instantiate方法实例化对象
[Command]
void CmdFire(){
GameObject bullet = Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation) as GameObject;}
执行对象所需执行的操作
[Command]
void CmdFire()
{
GameObject bullet = Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation) as GameObject;
bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 10;
Destroy(bullet, 2);
}
使用NetworkServer.Spawn()在各个客户端上显示出来这个物体。他同时会显示出这个物体的状态(位移、旋转、缩放等)
[Command]
void CmdFire()
{
GameObject bullet = Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation) as GameObject;
bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 10;
Destroy(bullet, 2);
NetworkServer.Spawn(bullet);
}
在其他客户端上同步位移
在Player身上加上一个组件——NetworkTransform
选择Transform Sync Mode(Transform同步方式)
添加血条
新建一个代码Health,并将它挂在Player身上
public const int maxHealth = 100;[SyncVar(hook="OnChangeHealth") ]public int currentHealth = maxHealth;public Slider healthSlider;public bool destroyOnDeath = false;private NetworkStartPosition[] spawnPoints;void Start(){if (isLocalPlayer){spawnPoints = FindObjectsOfType<NetworkStartPosition>();}}public void TakeDamage(int damage){if (isServer == false) return;// 血量的处理只在服务器端执行currentHealth -= damage;if (currentHealth <= 0){if (destroyOnDeath){Destroy(this.gameObject); return;}currentHealth = maxHealth;Debug.Log("Dead");RpcRespawn();}}void OnChangeHealth(int health){healthSlider.value = health / (float)maxHealth;}[ClientRpc]void RpcRespawn(){if (isLocalPlayer == false) return;Vector3 spawnPosition = Vector3.zero;if (spawnPoints != null && spawnPoints.Length > 0){spawnPosition = spawnPoints[Random.Range(0, spawnPoints.Length)].transform.position;}transform.position = spawnPosition;}
几个地方需要注意(从上至下)
- [SyncVar]--同步变量,这是一个特性,代表这个变量可以受到其他客户端的影响。
[SyncVar(hook="方法名")],是它的一个派生特性,代表当这个变量发生变化时,将调用一次hook中提到的方法,如果有参数,默认将此变量作为参数代入方法中。
特性必须紧挨着修饰的变量或方法
[SyncVar(hook="OnChangeHealth") ]
public int currentHealth = maxHealth;
当变量发生变化时,调用方法
[SyncVar(hook="OnChangeHealth") ]
public int currentHealth = maxHealth;//...
void OnChangeHealth(int health)
{
healthSlider.value = health / (float)maxHealth;
}
- isServer--服务端判断,与isLocalPlayer相对,可以判断当前是否为服务端。
我们当前的方式是让当前主机作为服务端的同时操控一个客户端,其他主机仅操控一个客户端
public void TakeDamage(int damage)
{if (isServer == false) return;
//....
}
当血量已经为0的时候,让你复活,如果是系统生成的敌人,为他勾上destroyOnDeath,这样当他血量为0,他就会消失了
public void TakeDamage(int damage)
{if (isServer == false) return
currentHealth -= damage;
if (currentHealth <= 0)
{
if (destroyOnDeath)//当系统敌人血量为0
{
Destroy(this.gameObject); return;
}currentHealth = maxHealth;
Debug.Log("Dead");
RpcRespawn();//重生
}}
- [ClientRpc]--从服务端发起,在客户端调用。
这个特性与[Command]相对,它适合用于被动的效果。比如重生(被杀死后移动到某一位置),传送(被移动)等等
同样的,它修饰的方法名前面需要加上Rpc
一般这个方法配合if (isLocalPlayer == false)使用
[ClientRpc]
void RpcRespawn()
{
if (isLocalPlayer == false) return;//...
}
为子弹添加效果
新建代码Bullet,并挂在子弹的身上,将它保存为Prefab
using UnityEngine;
using System.Collections;public class Bullet : MonoBehaviour {void OnCollisionEnter(Collision col){GameObject hit = col.gameObject;Health health = hit.GetComponent<Health>();if (health != null){health.TakeDamage(10);}Destroy(this.gameObject);}}
- 利用碰撞检测
void OnCollisionEnter(Collision col)
{//function
}
- 试图获得物体身上的Health(血条)
void OnCollisionEnter(Collision col)
{
GameObject hit = col.gameObject;
Health health = hit.GetComponent<Health>();
}
- 当取得到物体身上的Health组件,就说这这个物体代表敌人,对他造成伤害
void OnCollisionEnter(Collision col)
{
GameObject hit = col.gameObject;
Health health = hit.GetComponent<Health>();if (health != null)
{
health.TakeDamage(10);
}
}
- 子弹撞墙或者撞到地面都可以让他消失
void OnCollisionEnter(Collision col)
{
GameObject hit = col.gameObject;
Health health = hit.GetComponent<Health>();if (health != null)
{
health.TakeDamage(10);
}
Destroy(this.gameObject);
}
最后处理
将此Player制作为Prefab,并在Main场景中删去
Player会在客户端加载的时候自动生成
就这样吧,掰掰。
【零基础教学】Unet局域网联机的实现——最基础的Unity联网实现方式(2)相关推荐
- 计算机应用基础教学体会与建议,试论对计算机应用基础教学的几点体会
阿拉腾乌拉 摘 要:该文结合在计算机应用基础教学中的教学实践,从几个方面总结了学生在学习计算机过程中易犯的错误. 关键词:计算机:应用:基础:Word:Excel 中图分类号:G712 文献标识码:B ...
- 计算机基础原理习题,《计算机基础教学资料》第1章计算机基础原理习题.doc
第1章计算机基础原理习题 一.单选题 计算机最主要的工作特点是______. A)速度快.能存储.体积小B)速度快.价格低.程序控制 C)速度快.能存储.程序控制D)价格低.功能全.体积小 下列各无符 ...
- 计算机应用基础教学设计信息化,基于《大学计算机应用基础》的高校计算机基础课程的信息化教学设计与实践研究...
基于<大学计算机应用基础>的高校计算机基础课程的信息化教学设计与实践研究 随着信息技术的发展,教育信息化改革得以深化,而信息化教学设计作为高校教学活动的一大核心,发挥着十分重要的 (本文共 ...
- 计算是计算机科学独有的方法,大学计算机基础教学中的计算思维培养.doc
大学计算机基础教学中的计算思维培养 龚沛曾 杨志强 ? 2012-06-26 08:58:58 来源:<中国大学教学>2012年第05期 摘要:首先仔细地分析了计算思维的定义,提出了计算思 ...
- 大学计算机基础毕业论文操作步骤,大学计算机基础教学论文
毕业论文应反映出作者能够准确地掌握所学的专业基础知识,基本学会综合运用所学知识进行科学研究的方法,对所研究的题目有一定的心得体会,论文题目的范围不宜过宽,一般选择本学科某一重要问题的一个侧面.范文小编 ...
- 计算机基础教学模式,计算机基础行动导向教学模式的应用
[摘要]为了培养更加适应社会需求和发展的现代化人才,加强计算机基础课堂的教学势在必行.行动导向教学法是体现"以人为本"教育思想的重要方法,教师在教学过程中重点强调学生的参与性.实践 ...
- 大一计算机思维知识点,大学计算机基础教学中计算思维的培养途径
摘要:从几个侧面思考和探究在大学计算机基础教学中培养学生计算思维的途径,提出通过整合教学内容,将不同的知识点归纳为不同的知识单元:通过实验教学,提升学生运用计算思维处理问题的能力. 关键词:计算思维: ...
- Unity + Mirror实现原创卡牌游戏局域网联机
资源下载地址 局域网联机插件 Mirror:Mirror | 网络 | Unity Asset Store 本地客户端测试多人游戏(不用打包)插件 : ParrelSync Mirror官方文档:Ge ...
- 大学计算机基础毕业论文操作步骤,大学计算机基础教学论文论文
毕业论文应反映出作者能够准确地掌握所学的专业基础知识,基本学会综合运用所学知识进行科学研究的方法,对所研究的题目有一定的心得体会,论文题目的范围不宜过宽,一般选择本学科某一重要问题的一个侧面.范文小编 ...
最新文章
- 基于iview 封装一个vue 表格分页组件
- 记录 spring 使用@Value获取properties文件中的属性值
- LeetCode解题的常见模式套路
- 计算机网络物理层知识要点:通信基础、传输介质和设备
- 类型缩放Google map 地图类型
- 印发 指南 通知_通知设计的综合指南
- 【数学】Element Swapping
- Atlantis HDU - 1542 (扫描线,线段树)
- 记一次线上Zabbix对Redis监控实录
- 15-07-22 数据库--存储过程、触发器
- 定时器_定时器设计的门铃
- Spring Boot 自定义注解实现AOP切面织入
- matlab求一个矩阵的逆矩阵的命令,如何用MATLAB求逆矩阵
- ios逆向工具theos tweak make编译错误集合
- QGraphicsItem
- Prometheus常用函数
- 24微信小程序开发2
- 重装战姬服务器正在维护,《重装战姬》4月23日更新维护公告
- 乐视x820android最新版本,乐视MAX2|MIUI10|安卓8.1|最终完美版|极速_最新最全的乐Max2ROM刷机包下载、刷机教程_...
- 2018-11-26-win10-UWP-Controls-by-function