书籍作者: 罗培羽 著
ISBN:9787111612179
推荐程度: 有编程经验的,强烈推荐
在学习 Unity 3D 的过程中,从图书馆借来了此书。该书总体由浅入深,步骤清晰,文字简练,在阅读学习过程中,发现存在少许错误,特此记录。
书中大部分工程使用的 Unity 版本为 2017.3.0f3,本人使用的 Unity 版本为 2019.4.31f1c1 。经过代码实践,书中大部分函数均与 2019 一致,存在不同时,会在本文列出。
第 1 章 网络游戏的开端:Echo
- 第 9 页代码
Echo.cs
内的socket.connect
拼写缺失字母e
。
第 3 章 实践出真知:大乱斗游戏
-
第 68 页 Main 代码的编写,应将前几页的 NetManager 类的 Update 调用,放置在 Main 的
Update
函数内调用。否则,OnEnter 条件无法触发。 -
第 70 页 OnEnter 函数代码的编写,注意 otherHumans 在使用前应进行初始化。例如:
// 声明时初始化
public Dictionary<string, BaseHuman> otherHumans = new Dictionary<string, BaseHuman>();
// 或者 在 Awake 里初始化
private void Awake()
{
otherHumans = new Dictionary<string, BaseHuman>();
}
- 第 89 页 Hit 协议,此处描述的协议并非最终代码使用的协议,具体协议应参考第 91 页的代码。格式为:
Hit|<攻击发动者>,<伤害承担者>
。
第 4 章 正确收发数据流
-
第 96 页的图 4-6 半包现象,其中第二个文字描述错误,应为
接收端 Receive
而非发送端 Receive
。 -
第 105 页的
OnReceiveData
函数代码,其中在处理readBuff
获取消息时,读取了buffCount
的字节长度,此处应使用bodyLength
更合适, 代码如下:
string s = System.Text.Encoding.UTF8.GetString(readBuff, 2, bodyLength);
-
第 119 页在描述 Queue 的使用方法时,为了保证队列使用方法的一致性,可将
First()
替换为Peek()
。First()
是需要引入System.Linq
命名控件的,而Peek()
与Enqueue()
、Dequeue()
均属于队列类的基本操作。两者使用效果一致。 -
第 126 页读写功能小节,其中
Read
方法的代码里对数组的复制,此处源数组的读取下标应为 readIdx 而不应该为 0 ,否则当 bytes 数组的 length > 8 时, 数组不移动而下次读取仍然从 0 开始读取,这导致数据错误。代码如下:
public int Read(byte[] bs, int offset, int count){
count = Math.Min(count, length);
// 此处应修改为 readIdx
Array.Copy(bytes, readIdx, bs, offset, count);
readIdx += count;
CheckAndMoveBytes();
return count;
}
- 第 131 页将 ByteArray 应用到异步程序的代码, 此处存在代码错误。作者的想法应该是读取
bodyLength
后,当数组内接收字节长度为一个完整的 package 则进行解析。原代码如下:
if ( readBuff.length <= 2 )
return;
int readIdx = readBuff.readIdx;
byte[] bytes = readBuff.bytes;
Int16 bodyLength = (Int16) ((bytes[readIdx + 1] << 8) | bytes[readIdx]);
// 此处存在错误, length 包括了长度的两个字节,实际应为 readBuff.length < 2 + bodyLength
if (readBuff.length < bodyLength)
return;
readBuff.readIdx += 2;
结合书中对 ByteArray 的方法描述,应修改为以下代码:
if ( readBuff.length <= 2 )
return;
// 注意 ReadInt16 函数内,会对 readIdx 执行 + 2 操作
Int16 bodyLength = readBuff.ReadInt16();
// 由于 length 指的是 writeIdx - readIdx , 所以可直接与 bodyLength 比较
if (readBuff.length < bodyLength)
{
// 如果字节数达不到长度,则需要继续等待接收,并将 readIdx 还原为读取长度之前
readBuff.readIdx -= 2;
return;
}
第 7 章 通用服务端框架
-
第 204 页心跳机制,这里使用了 lastPingTime 记录最近一次接收 Ping 指令的时间。按照书中的代码的话,需要在客户端接入的时候初始化一次这个变量。否则,在第 206 页的
CheckPing
方法中,判断一直生效,因为lastPingTime
初始化为 0 。 -
第 221 页
IsAccountExist
的代码中,try...catch
捕获的异常输出描述有误,代码如下:
...
catch(Exception e){
// 书中描述为 IsSafeString
Console.WriteLine("[ 数据库 ] IsAccountExist err, " + e.Message);
return false;
}
第 8 章 完整大项目《坦克大战》
-
第 249 页移动控制的代码,其中
MoveUpdate
函数最后控制 GameObject 移动时,无需重复嵌套 transform ,如transform.transform.position += s;
只写前面的 transform 即可。 -
第 258 页编写跟随摄像头脚本代码,在
LateUpdate
函数中部分代码缺失,但文字描述有提及。文中丢失Vector3.MoveTowards
方法。如下:
// 原文代码
void LateUpdate(){
// ... 前面代码省略,此处有误
// 应为 Vector3.MoveTowards(cameraPos, targetPos, Time.deltaTime * speed);
cameraPos = (cameraPos, targetPos, Time.deltaTime * speed);
}
第 9 章 UI界面模块
-
第 281 页界面层级管理的描述中,
定义枚举类型 Layer,分别有 Layer.Panel 和 Layer.Panel 两项。
这里笔误,结合上下文应该是分别有 Layer.Panel 和 Layer.Tip 两项。
。 -
第 282 页的
Init
函数代码内,有一行代码有误layer.Add(Layer.Tip, tip);
。 -
第 288 页的登录面板部件说明表格中,出现笔误,密码文字标签应为
PwText
而不是PwTest
。 -
第 289 页的登录面板类的段落文字描述中,新建文件夹统一放置面板脚本文件,命名出现笔误, 应为
module/Login
而不是mudule/Login
。 -
第 298 页
OnShow
函数 代码笔误,根据文字描述,本节代码是关于注册窗口RegisterPanel
的,而在OnShow
函数内监听的消息是登录MsgLogin
,此处应改为MsgRegister
。下方的OnClose
函数一样需修改, 不过前面小节编写的LoginPanel
代码应做相同处理,作者前面没有加上。代码如下:
public override void OnShow(params object[] args){
// 前面代码省略...
NetManager.AddMsgListener("MsgRegister", OnMsgRegister);
}
public override void OnShow(params object[] args){
// 前面代码省略...
NetManager.RemoveMsgListener("MsgRegister", OnMsgRegister);
}
第 10 章 游戏大厅和房间
- 第 313 页的操作栏部件说明表格中,出现笔误,刷新按钮应为
RefreshButton
而不是ReflashButton
。
第 11 章 战斗和胜负判定
-
第 376 页
ResetPlayers
函数内,设定 Player 的生命值在设置出生点时直接赋值,无需再写一个循环赋值。 -
第 379 页胜负决断函数,代码里函数名拼写有误,应为
Judgement
。
网友评论