进度展示
前言
Buff系统打算放到最后去做,这一章将会先把服务端的功能实现(主要还是因为一开始写没规划好,客户端代码和服务端代码没有分离,所以把较大的Buff系统模块放到最后写,这样就可以在写Buff系统的时候养成前后端代码分离的思维)
进度(完成的会在后面打“√”)
- 战斗回放 √
- 技能配置优化 √
- 技能配置导出 √
- 战斗逻辑移植服务端 √
- 同步双端战斗逻辑 √
- 战斗逻辑优化 √
- WebSocket服务端 √
- Protobuff导入和编写 √
- WebSocket客户端 √
- 客户端消息发送 √
- 服务端消息处理 √
- 客户端消息处理 √
- 英雄位置选择和交换 √
- 战斗开始请求和响应处理 √
- 联调双端战斗 √
- 双端重新开始战斗 √
- 双端战斗跳过实现 √
遇到的问题
服务端程序集的导入
由于对程序集的理解还不够,在这个过程中遇到一点问题,之后会整理和深入理解一下程序集相关的东西。
(笔记撰写中 .Net的程序集学习)
前后端代码的分离与共享
在一开始做的时候,还不太明白要怎么做前后端代码的分离和共享,这也是这一章需要理清楚的问题。
答:因为前端做表现上的逻辑或者引用,后端是用不上的,一开始没做分离,后面整理真是差点把我的命要了!经过一番捣鼓,现在已经可以前端做完直接复制黏贴过去了,感动ing…..
前后端的消息处理流程
在前后端消息的处理上,可以做一张流程图以及将重点的地方写写助于理解
客户端发送消息到服务器的流程
服务器回包给客户端的就不写了,流程大致一样。唯一不同的是,客户端用的是事件转发的方式。
也就是模块对消息添加监听,当消息转发中心收到网络传过来的消息后,分给所有监听的模块,由模块自己去处理字节数组。当然,这样的方式其实蛮麻烦,之前看Fantasy是用模板来生成相应的Protobuf数据类和枚举,那样做的话,模块就可以直接获取到消息对象了吧,就不用自己转了。
数据存放的位置
因为现在数据已经变成了有好几个地方去获取,配置,消息,测试数据,因而需要将这些进行整理,同时要想清楚数据的生命周期,该存放在哪个地方。
答:暂时就是有一个配置加载中心在一开始存放所有从json读取的数据并加载到内存中,其他地方可通过这个单例类获取到重写的Clone()的数据。然后网络层在处理完消息后,会放回到数据层里,接着用的时候就是用数据层的数据。
小技巧
Rider设置项目编译指令
2d下射线选中物体
因为做的是2d横版的卡牌放置,在选择英雄交换位置的时候,要用2d射线去获取,代码如下
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Ray2D ray = new Ray2D(mousePosition, Vector2.zero);
// 处理检测结果
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);
if (hit.collider != null)
{
// 选中了物体
GameObject selectedObject = hit.collider.gameObject;
Debug.Log("Selected Object: " + selectedObject.name);
}
本地测试消息
客户端测试消息的时候,完全可以先在客户端本地模拟一个网络消息收发的过程,这样就可以在客户端编写消息,然后写完之后把新增的消息类放到服务端就好了。(注意保持代码一致)
Json转ScriptObject
在改变文件路径的时候,发现改完之后ScriptObject丢失了,吓了一跳,查了一下,可以用Json来创建。
[MenuItem("Utility/Convert/Json2ScriptObject")]
static void ConvertToScriptObject()
{
LogManager.Log(Application.dataPath + "/GameData/");
string text = File.ReadAllText(Application.dataPath + "/GameData/Json/SkillJsonCfg.json");
List<BattleSkillConfig> asset = JsonConvert.DeserializeObject<List<BattleSkillConfig>>(text);
for (int i = 0; i < asset.Count; i++)
{
AssetDatabase.CreateAsset(asset[i], AssetPathConfig.SkillConfigPath + asset[i].skillId + ".asset");
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
配置数据Clone
从配置中加载到内存的数据,在取的时候,应该是返回一份新的出去,因而要用深拷贝去做,顺带刚好看到论坛有同学弄过这个问题,抄…观摩学习了一下。大家也可以去看看,写得蛮好的 C#开发中一些对数据克隆的想法
public class HeroData : ICloneable
{
public HeroData()
{
}
public int id;
public string name;
public string assetName;
public int type;
public int[] skillIdArr;
public string skillDes;
public int hp;
public int atk;
public int agl;
public int def;
public int takeDamageRage;
public int maxRage;
public object Clone()
{
//反射获取类型
Type t = GetType();
//获取所有成员变量
FieldInfo[] fieldInfos = t.GetFields();
//实例化一个新对象
object result = Activator.CreateInstance(t);
//遍历所有对象一一赋值
foreach (FieldInfo field in fieldInfos)
{
field.SetValue(result, GetValueByType(field.GetValue(this)));
}
return result;
//根据类型不同返回不同的克隆对象
object GetValueByType(object objRe)
{
if (objRe == null) return null;
if (objRe is ICloneable clone)
{
object cloneObj = clone.Clone();
return cloneObj;
}
else
{
return objRe;
}
}
}
}
状态
完结撒花🎉