1、介紹
在《Unity網(wǎng)絡(luò)游戲?qū)崙?zhàn)》書的最后五個(gè)章節(jié)是制作一個(gè)小的多人對戰(zhàn)游戲巴刻,坦克大戰(zhàn)涵紊。這里就把東西都寫在一起傍妒。做一個(gè)總結(jié)。
最后的五個(gè)章節(jié)是實(shí)現(xiàn)一個(gè)坦克大戰(zhàn)游戲摸柄。游戲功能大致如下:
1颤练、玩家注冊游戲賬號(hào),登陸游戲驱负。
2嗦玖、進(jìn)入游戲大廳,玩家可以創(chuàng)建房間跃脊,等待其他玩家加入房間宇挫。
3、玩家進(jìn)入房間之后分配陣營酪术,兩邊陣營人數(shù)相同并且最少有一人就可以開始游戲器瘪。
4、游戲過程很簡單绘雁。地方坦克全滅就取得勝利橡疼。
因?yàn)檎鹿?jié)太多,代碼太長了庐舟。就不一一記錄了欣除。而且小游戲?qū)崿F(xiàn)的功能確實(shí)比較簡單。就只拿一下比較有用代碼說一下挪略。
2历帚、大廳系統(tǒng)
大廳系統(tǒng)是玩家登錄游戲之后進(jìn)入游戲大廳,然后創(chuàng)建房間或加入已經(jīng)存在的房間的操作杠娱。
客戶端實(shí)現(xiàn):
界面制作有大廳界面以及房間界面抹缕。
玩家登陸游戲之后,會(huì)向服務(wù)器發(fā)送MsgGetRoomList協(xié)議墨辛,獲取當(dāng)前存在的房間∨糠客戶端收到回應(yīng)之后睹簇,就清空當(dāng)前的所有RoomItem,根據(jù)服務(wù)器的回送創(chuàng)建新的RoomItem寥闪。
玩家點(diǎn)擊進(jìn)入房間太惠,會(huì)向服務(wù)器發(fā)送MsgGetRoomInfo協(xié)議,獲取當(dāng)前進(jìn)入的房間中所有玩家的信息疲憋,然后生成PlayerInfoItem凿渊。退出房間就向服務(wù)器發(fā)送MsgGetRoomList協(xié)議,顯示房間列表。
客戶端代碼:
主要代碼有:RoomListPanel和RoomPanel
RoomListPanel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RoomListPanel : BasePanel {
//賬號(hào)文本
private Text idText;
//戰(zhàn)績文本
private Text scoreText;
//創(chuàng)建房間按鈕
private Button createButton;
//刷新列表按鈕
private Button reflashButton;
//列表容器
private Transform content;
//房間物體
private GameObject roomObj;
//初始化
public override void OnInit() {
skinPath = "RoomListPanel";
layer = PanelManager.Layer.Panel;
}
//顯示
public override void OnShow(params object[] args) {
//尋找組件
idText = skin.transform.Find("InfoPanel/IdText").GetComponent<Text>();
scoreText = skin.transform.Find("InfoPanel/ScoreText").GetComponent<Text>();
createButton = skin.transform.Find("CtrlPanel/CreateButton").GetComponent<Button>();
reflashButton = skin.transform.Find("CtrlPanel/ReflashButton").GetComponent<Button>();
content = skin.transform.Find("ListPanel/Scroll View/Viewport/Content");
roomObj = skin.transform.Find("Room").gameObject;
//不激活房間
roomObj.SetActive(false);
//顯示id
idText.text = GameMain.id;
//按鈕事件
createButton.onClick.AddListener(OnCreateClick);
reflashButton.onClick.AddListener(OnReflashClick);
//協(xié)議監(jiān)聽
NetManager.AddMsgListener("MsgGetAchieve", OnMsgGetAchieve);
NetManager.AddMsgListener("MsgGetRoomList", OnMsgGetRoomList);
NetManager.AddMsgListener("MsgCreateRoom", OnMsgCreateRoom);
NetManager.AddMsgListener("MsgEnterRoom", OnMsgEnterRoom);
//發(fā)送查詢
MsgGetAchieve msgGetAchieve = new MsgGetAchieve();
NetManager.Send(msgGetAchieve);
MsgGetRoomList msgGetRoomList = new MsgGetRoomList();
NetManager.Send(msgGetRoomList);
}
//關(guān)閉
public override void OnClose() {
//協(xié)議監(jiān)聽
NetManager.RemoveMsgListener("MsgGetAchieve", OnMsgGetAchieve);
NetManager.RemoveMsgListener("MsgGetRoomList", OnMsgGetRoomList);
NetManager.RemoveMsgListener("MsgCreateRoom", OnMsgCreateRoom);
NetManager.RemoveMsgListener("MsgEnterRoom", OnMsgEnterRoom);
}
//收到成績查詢協(xié)議
public void OnMsgGetAchieve (MsgBase msgBase) {
MsgGetAchieve msg = (MsgGetAchieve)msgBase;
scoreText.text = msg.win + "勝 " + msg.lost + "負(fù)";
}
//收到房間列表協(xié)議
public void OnMsgGetRoomList (MsgBase msgBase) {
MsgGetRoomList msg = (MsgGetRoomList)msgBase;
//清除房間列表
for(int i = content.childCount-1; i >= 0 ; i--){
GameObject o = content.GetChild(i).gameObject;
Destroy(o);
}
//重新生成列表
if(msg.rooms == null){
return;
}
for(int i = 0; i < msg.rooms.Length; i++){
GenerateRoom(msg.rooms[i]);
}
}
//創(chuàng)建一個(gè)房間單元
public void GenerateRoom(RoomInfo roomInfo){
//創(chuàng)建物體
GameObject o = Instantiate(roomObj);
o.transform.SetParent(content);
o.SetActive(true);
o.transform.localScale = Vector3.one;
//獲取組件
Transform trans = o.transform;
Text idText = trans.Find("IdText").GetComponent<Text>();
Text countText = trans.Find("CountText").GetComponent<Text>();
Text statusText = trans.Find("StatusText").GetComponent<Text>();
Button btn = trans.Find("JoinButton").GetComponent<Button>();
//填充信息
idText.text = roomInfo.id.ToString();
countText.text = roomInfo.count.ToString();
if(roomInfo.status == 0){
statusText.text = "準(zhǔn)備中";
}
else{
statusText.text = "戰(zhàn)斗中";
}
//按鈕事件
btn.name = idText.text;
btn.onClick.AddListener(delegate(){
OnJoinClick(btn.name);
});
}
//點(diǎn)擊刷新按鈕
public void OnReflashClick(){
MsgGetRoomList msg = new MsgGetRoomList();
NetManager.Send(msg);
}
//點(diǎn)擊加入房間按鈕
public void OnJoinClick(string idString) {
MsgEnterRoom msg = new MsgEnterRoom();
msg.id = int.Parse(idString);
NetManager.Send(msg);
}
//收到進(jìn)入房間協(xié)議
public void OnMsgEnterRoom (MsgBase msgBase) {
MsgEnterRoom msg = (MsgEnterRoom)msgBase;
//成功進(jìn)入房間
if(msg.result == 0){
PanelManager.Open<RoomPanel>();
Close();
}
//進(jìn)入房間失敗
else{
PanelManager.Open<TipPanel>("進(jìn)入房間失敗");
}
}
//點(diǎn)擊新建房間按鈕
public void OnCreateClick() {
MsgCreateRoom msg = new MsgCreateRoom();
NetManager.Send(msg);
}
//收到新建房間協(xié)議
public void OnMsgCreateRoom (MsgBase msgBase) {
MsgCreateRoom msg = (MsgCreateRoom)msgBase;
//成功創(chuàng)建房間
if(msg.result == 0){
PanelManager.Open<TipPanel>("創(chuàng)建成功");
PanelManager.Open<RoomPanel>();
Close();
}
//創(chuàng)建房間失敗
else{
PanelManager.Open<TipPanel>("創(chuàng)建房間失敗");
}
}
}
RoomPanel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class RoomPanel : BasePanel {
//開戰(zhàn)按鈕
private Button startButton;
//退出按鈕
private Button closeButton;
//列表容器
private Transform content;
//玩家信息物體
private GameObject playerObj;
//初始化
public override void OnInit() {
skinPath = "RoomPanel";
layer = PanelManager.Layer.Panel;
}
//顯示
public override void OnShow(params object[] args) {
//尋找組件
startButton = skin.transform.Find("CtrlPanel/StartButton").GetComponent<Button>();
closeButton = skin.transform.Find("CtrlPanel/CloseButton").GetComponent<Button>();
content = skin.transform.Find("ListPanel/Scroll View/Viewport/Content");
playerObj = skin.transform.Find("Player").gameObject;
//不激活玩家信息
playerObj.SetActive(false);
//按鈕事件
startButton.onClick.AddListener(OnStartClick);
closeButton.onClick.AddListener(OnCloseClick);
//協(xié)議監(jiān)聽
NetManager.AddMsgListener("MsgGetRoomInfo", OnMsgGetRoomInfo);
NetManager.AddMsgListener("MsgLeaveRoom", OnMsgLeaveRoom);
NetManager.AddMsgListener("MsgStartBattle", OnMsgStartBattle);
//發(fā)送查詢
MsgGetRoomInfo msg = new MsgGetRoomInfo();
NetManager.Send(msg);
}
//關(guān)閉
public override void OnClose() {
//協(xié)議監(jiān)聽
NetManager.RemoveMsgListener("MsgGetRoomInfo", OnMsgGetRoomInfo);
NetManager.RemoveMsgListener("MsgLeaveRoom", OnMsgLeaveRoom);
NetManager.RemoveMsgListener("MsgStartBattle", OnMsgStartBattle);
}
//收到玩家列表協(xié)議
public void OnMsgGetRoomInfo (MsgBase msgBase) {
MsgGetRoomInfo msg = (MsgGetRoomInfo)msgBase;
//清除玩家列表
for(int i = content.childCount-1; i >= 0 ; i--){
GameObject o = content.GetChild(i).gameObject;
Destroy(o);
}
//重新生成列表
if(msg.players == null){
return;
}
for(int i = 0; i < msg.players.Length; i++){
GeneratePlayerInfo(msg.players[i]);
}
}
//創(chuàng)建一個(gè)玩家信息單元
public void GeneratePlayerInfo(PlayerInfo playerInfo){
//創(chuàng)建物體
GameObject o = Instantiate(playerObj);
o.transform.SetParent(content);
o.SetActive(true);
o.transform.localScale = Vector3.one;
//獲取組件
Transform trans = o.transform;
Text idText = trans.Find("IdText").GetComponent<Text>();
Text campText = trans.Find("CampText").GetComponent<Text>();
Text scoreText = trans.Find("ScoreText").GetComponent<Text>();
//填充信息
idText.text = playerInfo.id;
if(playerInfo.camp == 1){
campText.text = "紅";
}
else{
campText.text = "藍(lán)";
}
if(playerInfo.isOwner == 1){
campText.text = campText.text + " 埃脏!";
}
scoreText.text = playerInfo.win + "勝 " + playerInfo.lost + "負(fù)";
}
//點(diǎn)擊退出按鈕
public void OnCloseClick(){
MsgLeaveRoom msg = new MsgLeaveRoom();
NetManager.Send(msg);
}
//收到退出房間協(xié)議
public void OnMsgLeaveRoom (MsgBase msgBase) {
MsgLeaveRoom msg = (MsgLeaveRoom)msgBase;
//成功退出房間
if(msg.result == 0){
PanelManager.Open<TipPanel>("退出房間");
PanelManager.Open<RoomListPanel>();
Close();
}
//退出房間失敗
else{
PanelManager.Open<TipPanel>("退出房間失敗");
}
}
//點(diǎn)擊開戰(zhàn)按鈕
public void OnStartClick(){
MsgStartBattle msg = new MsgStartBattle();
NetManager.Send(msg);
}
//收到開戰(zhàn)返回
public void OnMsgStartBattle (MsgBase msgBase) {
MsgLeaveRoom msg = (MsgLeaveRoom)msgBase;
//開戰(zhàn)
if(msg.result == 0){
//等待戰(zhàn)斗推送的協(xié)議
}
//開戰(zhàn)失敗
else{
PanelManager.Open<TipPanel>("開戰(zhàn)失斕侣唷!兩隊(duì)至少都需要一名玩家彩掐,只有隊(duì)長可以開始戰(zhàn)斗构舟!");
}
}
}
服務(wù)器主要是實(shí)現(xiàn)Room和RoomManager。這兩個(gè)類在名字上就很清楚它們的功能是什么堵幽,比較簡單狗超。
Room.cs
using System;
using System.Collections.Generic;
public class Room {
//id
public int id = 0;
//最大玩家數(shù)
public int maxPlayer = 6;
//玩家列表
public Dictionary<string, bool> playerIds = new Dictionary<string, bool>();
//房主id
public string ownerId = "";
//狀態(tài)
public enum Status {
PREPARE = 0,
FIGHT = 1 ,
}
public Status status = Status.PREPARE;
//添加玩家
public bool AddPlayer(string id){
//獲取玩家
Player player = PlayerManager.GetPlayer(id);
if(player == null){
Console.WriteLine("room.AddPlayer fail, player is null");
return false;
}
//房間人數(shù)
if(playerIds.Count >= maxPlayer){
Console.WriteLine("room.AddPlayer fail, reach maxPlayer");
return false;
}
//準(zhǔn)備狀態(tài)才能加人
if(status != Status.PREPARE){
Console.WriteLine("room.AddPlayer fail, not PREPARE");
return false;
}
//已經(jīng)在房間里
if(playerIds.ContainsKey(id)){
Console.WriteLine("room.AddPlayer fail, already in this room");
return false;
}
//加入列表
playerIds[id] = true;
//設(shè)置玩家數(shù)據(jù)
player.camp = SwitchCamp();
player.roomId = this.id;
//設(shè)置房主
if(ownerId == ""){
ownerId = player.id;
}
//廣播
Broadcast(ToMsg());
return true;
}
//分配陣營
public int SwitchCamp() {
//計(jì)數(shù)
int count1 = 0;
int count2 = 0;
foreach(string id in playerIds.Keys) {
Player player = PlayerManager.GetPlayer(id);
if(player.camp == 1) {count1++;}
if(player.camp == 2) {count2++;}
}
//選擇
if (count1 <= count2){
return 1;
}
else{
return 2;
}
}
//是不是房主
public bool isOwner(Player player){
return player.id == ownerId;
}
//刪除玩家
public bool RemovePlayer(string id) {
//獲取玩家
Player player = PlayerManager.GetPlayer(id);
if(player == null){
Console.WriteLine("room.RemovePlayer fail, player is null");
return false;
}
//沒有在房間里
if(!playerIds.ContainsKey(id)){
Console.WriteLine("room.RemovePlayer fail, not in this room");
return false;
}
//刪除列表
playerIds.Remove(id);
//設(shè)置玩家數(shù)據(jù)
player.camp = 0;
player.roomId = -1;
//設(shè)置房主
if(ownerId == player.id){
ownerId = SwitchOwner();
}
//房間為空
if(playerIds.Count == 0){
RoomManager.RemoveRoom(this.id);
}
//廣播
Broadcast(ToMsg());
return true;
}
//選擇房主
public string SwitchOwner() {
//選擇第一個(gè)玩家
foreach(string id in playerIds.Keys) {
return id;
}
//房間沒人
return "";
}
//廣播消息
public void Broadcast(MsgBase msg){
foreach(string id in playerIds.Keys) {
Player player = PlayerManager.GetPlayer(id);
player.Send(msg);
}
}
//生成MsgGetRoomInfo協(xié)議
public MsgBase ToMsg(){
MsgGetRoomInfo msg = new MsgGetRoomInfo();
int count = playerIds.Count;
msg.players = new PlayerInfo[count];
//players
int i = 0;
foreach(string id in playerIds.Keys){
Player player = PlayerManager.GetPlayer(id);
PlayerInfo playerInfo = new PlayerInfo();
//賦值
playerInfo.id = player.id;
playerInfo.camp = player.camp;
playerInfo.win = player.data.win;
playerInfo.lost = player.data.lost;
playerInfo.isOwner = 0;
if(isOwner(player)){
playerInfo.isOwner = 1;
}
msg.players[i] = playerInfo;
i++;
}
return msg;
}
}
RoomManager.cs
using System;
using System.Collections.Generic;
public class RoomManager
{
//最大id
private static int maxId = 1;
//房間列表
public static Dictionary<int, Room> rooms = new Dictionary<int, Room>();
//創(chuàng)建房間
public static Room AddRoom(){
maxId++;
Room room = new Room();
room.id = maxId;
rooms.Add(room.id, room);
return room;
}
//刪除房間
public static bool RemoveRoom(int id) {
rooms.Remove(id);
return true;
}
//獲取房間
public static Room GetRoom(int id) {
if(rooms.ContainsKey(id)){
return rooms[id];
}
return null;
}
//生成MsgGetRoomList協(xié)議
public static MsgBase ToMsg(){
MsgGetRoomList msg = new MsgGetRoomList();
int count = rooms.Count;
msg.rooms = new RoomInfo[count];
//rooms
int i = 0;
foreach(Room room in rooms.Values){
RoomInfo roomInfo = new RoomInfo();
//賦值
roomInfo.id = room.id;
roomInfo.count = room.playerIds.Count;
roomInfo.status = (int)room.status;
msg.rooms[i] = roomInfo;
i++;
}
return msg;
}
}
RoomMsgHandle.cs
using System;
public partial class MsgHandler {
//查詢戰(zhàn)績
public static void MsgGetAchieve(ClientState c, MsgBase msgBase){
MsgGetAchieve msg = (MsgGetAchieve)msgBase;
Player player = c.player;
if(player == null) return;
msg.win = player.data.win;
msg.lost = player.data.lost;
player.Send(msg);
}
//請求房間列表
public static void MsgGetRoomList(ClientState c, MsgBase msgBase){
MsgGetRoomList msg = (MsgGetRoomList)msgBase;
Player player = c.player;
if(player == null) return;
player.Send(RoomManager.ToMsg());
}
//創(chuàng)建房間
public static void MsgCreateRoom(ClientState c, MsgBase msgBase){
MsgCreateRoom msg = (MsgCreateRoom)msgBase;
Player player = c.player;
if(player == null) return;
//已經(jīng)在房間里
if(player.roomId >=0 ){
msg.result = 1;
player.Send(msg);
return;
}
//創(chuàng)建
Room room = RoomManager.AddRoom();
room.AddPlayer(player.id);
msg.result = 0;
player.Send(msg);
}
//進(jìn)入房間
public static void MsgEnterRoom(ClientState c, MsgBase msgBase){
MsgEnterRoom msg = (MsgEnterRoom)msgBase;
Player player = c.player;
if(player == null) return;
//已經(jīng)在房間里
if(player.roomId >=0 ){
msg.result = 1;
player.Send(msg);
return;
}
//獲取房間
Room room = RoomManager.GetRoom(msg.id);
if(room == null){
msg.result = 1;
player.Send(msg);
return;
}
//進(jìn)入
if(!room.AddPlayer(player.id)){
msg.result = 1;
player.Send(msg);
return;
}
//返回協(xié)議
msg.result = 0;
player.Send(msg);
}
//獲取房間信息
public static void MsgGetRoomInfo(ClientState c, MsgBase msgBase){
MsgGetRoomInfo msg = (MsgGetRoomInfo)msgBase;
Player player = c.player;
if(player == null) return;
Room room = RoomManager.GetRoom(player.roomId);
if(room == null){
player.Send(msg);
return;
}
player.Send(room.ToMsg());
}
//離開房間
public static void MsgLeaveRoom(ClientState c, MsgBase msgBase){
MsgLeaveRoom msg = (MsgLeaveRoom)msgBase;
Player player = c.player;
if(player == null) return;
Room room = RoomManager.GetRoom(player.roomId);
if(room == null){
msg.result = 1;
player.Send(msg);
return;
}
room.RemovePlayer(player.id);
//返回協(xié)議
msg.result = 0;
player.Send(msg);
}
}
如果是從頭到尾實(shí)現(xiàn)一遍書上的內(nèi)容的話,上面的代碼還是比較簡單的朴下。
3努咐、預(yù)測同步算法
在最后一章中作者介紹了服務(wù)器的同步算法。原理如下:
1、每個(gè)客戶端每隔一段時(shí)間發(fā)送同步“幀”到服務(wù)器职祷。
2澡谭、服務(wù)器更細(xì)玩家的位置、旋轉(zhuǎn)信息并轉(zhuǎn)發(fā)到各個(gè)客戶端免胃。
3、客戶端收到同步信息之后惫撰,使用預(yù)測算法計(jì)算同步玩家的位置羔沙。
預(yù)測的方法也很簡單。因?yàn)樘箍耸莿蛩龠\(yùn)動(dòng)的厨钻。因此預(yù)測位置只需要根據(jù)s = v * t就行了扼雏。
這里直接看代碼就行,比較簡單夯膀。
SyncMsg.cs
//同步坦克信息
public class MsgSyncTank:MsgBase {
public MsgSyncTank() {protoName = "MsgSyncTank";}
//位置诗充、旋轉(zhuǎn)、炮塔旋轉(zhuǎn)
public float x = 0f;
public float y = 0f;
public float z = 0f;
public float ex = 0f;
public float ey = 0f;
public float ez = 0f;
public float turretY = 0f;
//服務(wù)端補(bǔ)充
public string id = ""; //哪個(gè)坦克
}
//開火
public class MsgFire:MsgBase {
public MsgFire() {protoName = "MsgFire";}
//炮彈初始位置诱建、旋轉(zhuǎn)
public float x = 0f;
public float y = 0f;
public float z = 0f;
public float ex = 0f;
public float ey = 0f;
public float ez = 0f;
//服務(wù)端補(bǔ)充
public string id = ""; //哪個(gè)坦克
}
//擊中
public class MsgHit:MsgBase {
public MsgHit() {protoName = "MsgHit";}
//擊中誰
public string targetId = "";
//擊中點(diǎn)
public float x = 0f;
public float y = 0f;
public float z = 0f;
//服務(wù)端補(bǔ)充
public string id = ""; //哪個(gè)坦克
public int hp = 0; //被擊中坦克血量
public int damage = 0; //受到的傷害
}
SyncTank.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SyncTank : BaseTank {
//預(yù)測信息蝴蜓,哪個(gè)時(shí)間到達(dá)哪個(gè)位置
private Vector3 lastPos;
private Vector3 lastRot;
private Vector3 forecastPos;
private Vector3 forecastRot;
private float forecastTime;
//重寫Init
public new void Init(string skinPath){
base.Init(skinPath);
//不受物理運(yùn)動(dòng)影響
rigidBody.constraints = RigidbodyConstraints.FreezeAll;
rigidBody.useGravity = false;
//初始化預(yù)測信息
lastPos = transform.position;
lastRot = transform.eulerAngles;
forecastPos = transform.position;
forecastRot = transform.eulerAngles;
forecastTime = Time.time;
}
new void Update(){
base.Update();
//更新位置
ForecastUpdate();
}
//移動(dòng)同步
public void SyncPos(MsgSyncTank msg){
//預(yù)測位置
Vector3 pos = new Vector3(msg.x, msg.y, msg.z);
Vector3 rot = new Vector3(msg.ex, msg.ey, msg.ez);
forecastPos = pos + 2*(pos - lastPos);
forecastRot = rot + 2*(rot - lastRot);
//更新
lastPos = pos;
lastRot = rot;
forecastTime = Time.time;
//炮塔
Vector3 le = turret.localEulerAngles;
le.y = msg.turretY;
turret.localEulerAngles = le;
}
//更新位置
public void ForecastUpdate(){
//時(shí)間
float t = (Time.time - forecastTime)/CtrlTank.syncInterval;
t = Mathf.Clamp(t, 0f, 1f);
//位置
Vector3 pos = transform.position;
pos = Vector3.Lerp(pos, forecastPos, t);
transform.position = pos;
//旋轉(zhuǎn)
Quaternion quat = transform.rotation;
Quaternion forcastQuat = Quaternion.Euler(forecastRot);
quat = Quaternion.Lerp(quat, forcastQuat, t) ;
transform.rotation = quat;
}
//開火
public void SyncFire(MsgFire msg){
Bullet bullet = Fire();
//更新坐標(biāo)
Vector3 pos = new Vector3(msg.x, msg.y, msg.z);
Vector3 rot = new Vector3(msg.ex, msg.ey, msg.ez);
bullet.transform.position = pos;
bullet.transform.eulerAngles = rot;
}
}
戰(zhàn)斗管理類中加入:
//收到同步協(xié)議
public static void OnMsgSyncTank(MsgBase msgBase){
MsgSyncTank msg = (MsgSyncTank)msgBase;
//不同步自己
if(msg.id == GameMain.id){
return;
}
//查找坦克
SyncTank tank = (SyncTank)GetTank(msg.id);
if(tank == null){
return;
}
//移動(dòng)同步
tank.SyncPos(msg);
}
//收到開火協(xié)議
public static void OnMsgFire(MsgBase msgBase){
MsgFire msg = (MsgFire)msgBase;
//不同步自己
if(msg.id == GameMain.id){
return;
}
//查找坦克
SyncTank tank = (SyncTank)GetTank(msg.id);
if(tank == null){
return;
}
//開火
tank.SyncFire(msg);
}
//收到擊中協(xié)議
public static void OnMsgHit(MsgBase msgBase){
MsgHit msg = (MsgHit)msgBase;
//查找坦克
BaseTank tank = GetTank(msg.targetId);
if(tank == null){
return;
}
//被擊中
tank.Attacked(msg.damage);
}
服務(wù)器主要做的就是轉(zhuǎn)發(fā)功能。并沒有做校驗(yàn)俺猿。相對簡單茎匠。就不贅述了。
4押袍、結(jié)語
原本打算花個(gè)十來天把這本書看完的诵冒。但是沒想到花了差不多一個(gè)多月∫瓴眩《Unity3D網(wǎng)絡(luò)游戲?qū)崙?zhàn)》這本書對于接觸u3d的初學(xué)者還是很友好的汽馋,非常簡單容易上手侮东,代碼也簡單。最有用的應(yīng)該是通用客戶端網(wǎng)絡(luò)框架和通用服務(wù)器框架那兩章豹芯。對于網(wǎng)絡(luò)通訊寫的很好悄雅,清晰易懂。以及最后一章的同步方法告组。在書上學(xué)習(xí)的代碼也能用于工作中煤伟,讓自己上手更快了點(diǎn)。收獲還是很大的木缝。以后也要堅(jiān)持看書便锨,學(xué)習(xí)!