<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>jinshuangyan</title><description>Unity · C# · 学习笔记</description><link>https://jinshuangyan.top/</link><language>zh_CN</language><item><title>解决通信中的粘包和分包问题</title><link>https://jinshuangyan.top/posts/unity-tcp-sticky-packet/</link><guid isPermaLink="true">https://jinshuangyan.top/posts/unity-tcp-sticky-packet/</guid><pubDate>Sun, 11 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;byte[] tempbuffer = new byte[1024 * 1024];   // 缓冲区：收到的字节全攒这里&lt;/p&gt;
&lt;p&gt;int templen;                                 // 写指针：缓冲区有效数据的末尾&lt;/p&gt;
&lt;p&gt;int nowindex;                                // 读指针：解析到哪了&lt;/p&gt;
&lt;p&gt;public void HandResMeg(byte[] buffer, ref int offset)&lt;/p&gt;
&lt;p&gt;{&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ① 收多少存多少：永远追加到已有数据的末尾（templen），不能写 nowindex 位置

buffer.CopyTo(tempbuffer, templen);

templen += buffer.Length;

while (true)

{

    // ② 连 8 字节头都没凑齐 → 等下一批数据

    if (templen - nowindex &amp;lt; 8) break;

    if (templen - nowindex &amp;gt;= 8)   // （恒真，可删，只是保险）

    {

        // ③ 只读头，不推进 nowindex：第 2 个 int 用 nowindex+4 偏移读出来

        id = BitConverter.ToInt32(tempbuffer, nowindex);

        len = BitConverter.ToInt32(tempbuffer, nowindex + 4);

    }

    // ④ 整包（8 头 + len 体）没凑齐 → break 等下一批；此时 nowindex 一个字节都没动

    if (templen - nowindex &amp;lt; 8 + len) break;

    // ⑤ 确认整包齐了，才真正消费头：指针前进 8

    nowindex += 8;

    if (templen - nowindex &amp;gt;= len)   // （恒真，可删）

    {

        switch (id)

        {

            case 1111:

                // ⑥ 反序列化 body：Reading 读多少推进多少，正好走完 len 个字节

                Playerinfo info = new Playerinfo();

                info.Reading(tempbuffer, ref nowindex);

                Debug.Log($&quot;ID: {info.playerid}, Name: {info.playerdata.name}, Atk: {info.playerdata.atk}&quot;);

                break;

            default:

                // ⑦ 没写 case 的协议号：跳过它的 body，否则指针不动 → while 死循环

                nowindex += len;

                break;

        }

        continue;   // ⑧ 回到循环顶：继续看缓冲区里还有没有下一个整包（粘包在这里被逐个切开）

    }

}

// ⑨ 全部消费完才归零；有半包残留时保持现场，等下一批拼上（分包靠它）

if (nowindex == templen)

{

    nowindex = 0;

    templen = 0;

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
</content:encoded></item><item><title>JsonUility和LitJson区别</title><link>https://jinshuangyan.top/posts/unity-basic/</link><guid isPermaLink="true">https://jinshuangyan.top/posts/unity-basic/</guid><description>数据序列化</description><pubDate>Tue, 06 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;知识点一：相同点（3 条）&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;都是干 Json 的序列化和反序列化&lt;/li&gt;
&lt;li&gt;Json 文件编码格式都必须是 &lt;strong&gt;UTF-8&lt;/strong&gt;（不然中文乱码）&lt;/li&gt;
&lt;li&gt;都需要声明类来配合方法调用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;知识点二：不同点（7 条）&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;JsonUtility 是 Unity 自带；LitJson 是第三方，要 &lt;code&gt;using LitJson;&lt;/code&gt; 引命名空间&lt;/li&gt;
&lt;li&gt;JsonUtility 用自定义类时要&lt;strong&gt;加 &lt;code&gt;[Serializable]&lt;/code&gt; 特性&lt;/strong&gt;；LitJson 不需要&lt;/li&gt;
&lt;li&gt;JsonUtility 支持&lt;strong&gt;私有变量&lt;/strong&gt;（但要加 &lt;code&gt;[SerializeField]&lt;/code&gt; 特性）；LitJson 不支持私有&lt;/li&gt;
&lt;li&gt;JsonUtility &lt;strong&gt;不支持字典&lt;/strong&gt;；LitJson 支持（但键只能是字符串）&lt;/li&gt;
&lt;li&gt;JsonUtility 不能直接反序列化成&lt;strong&gt;数据集合&lt;/strong&gt;（数组、字典）；LitJson 可以&lt;/li&gt;
&lt;li&gt;JsonUtility 对自定义类&lt;strong&gt;要求有无参构造&lt;/strong&gt;；LitJson 不需要&lt;/li&gt;
&lt;li&gt;存空对象时：JsonUtility 会存&lt;strong&gt;默认值&lt;/strong&gt;而不是 null；LitJson 会存 &lt;strong&gt;null&lt;/strong&gt;（更准确）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;实际项目&lt;strong&gt;建议用 LitJson&lt;/strong&gt;，理由四条：不用加特性、支持字典、支持直接反序列化成数据集合、存 null 更准确。&lt;/p&gt;
&lt;p&gt;相关api&lt;/p&gt;
&lt;p&gt;| &lt;code&gt;JsonUtility.ToJson(obj)&lt;/code&gt; | 对象 → JSON 字符串 | 单行输出 |
| &lt;code&gt;JsonUtility.ToJson(obj, true)&lt;/code&gt; | 同上，带缩进换行 | &lt;code&gt;prettyPrint&lt;/code&gt;，人看舒服 |
| &lt;code&gt;JsonUtility.FromJson&amp;lt;T&amp;gt;(json)&lt;/code&gt; | JSON 字符串 → 新的 T 对象 | 类要 &lt;code&gt;[Serializable]&lt;/code&gt; |
| &lt;code&gt;JsonUtility.FromJsonOverwrite(json, 已有对象)&lt;/code&gt; | JSON 数据&lt;strong&gt;覆盖&lt;/strong&gt;到现有对象上 | 不新建对象、省 GC，适合把读出来的档直接灌进正在运行的对象 |&lt;/p&gt;
&lt;p&gt;| &lt;code&gt;JsonMapper.ToJson(obj)&lt;/code&gt; | 对象 → JSON 字符串 | 不用 &lt;code&gt;[Serializable]&lt;/code&gt;，不用无参构造 |
| &lt;code&gt;JsonMapper.ToObject&amp;lt;T&amp;gt;(json)&lt;/code&gt; | JSON → 指定类型 | &lt;code&gt;T&lt;/code&gt; 可以是类、&lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt;、&lt;code&gt;Dictionary&amp;lt;string,T&amp;gt;&lt;/code&gt; |
| &lt;code&gt;JsonMapper.ToObject(json)&lt;/code&gt; | JSON → &lt;code&gt;JsonData&lt;/code&gt; | 不知道类型时的万金油 |&lt;/p&gt;
</content:encoded></item><item><title>File和Directory相关操作</title><link>https://jinshuangyan.top/posts/unity-basics/</link><guid isPermaLink="true">https://jinshuangyan.top/posts/unity-basics/</guid><pubDate>Tue, 06 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;FliesApi速查&lt;/p&gt;
&lt;p&gt;| Api |
| 判断存在 | &lt;code&gt;bool File.Exists(path)&lt;/code&gt; | 文件是否存在 |
| 创建 | &lt;code&gt;FileStream File.Create(path)&lt;/code&gt; | &lt;strong&gt;返回 FileStream&lt;/strong&gt;，记得关流 |
| 写字节 | &lt;code&gt;File.WriteAllBytes(path, byte[])&lt;/code&gt; | 自动建文件，已有则&lt;strong&gt;整段覆盖&lt;/strong&gt; |
| 按行写 | &lt;code&gt;File.WriteAllLines(path, string[])&lt;/code&gt; | string 数组一行行写入 |
| 写文本 | &lt;code&gt;File.WriteAllText(path, string)&lt;/code&gt; | 整个字符串写入，&lt;code&gt;\n&lt;/code&gt; 生效 |
| 读字节 | &lt;code&gt;byte[] File.ReadAllBytes(path)&lt;/code&gt; | 配 &lt;code&gt;BitConverter&lt;/code&gt; 还原数据 |
| 按行读 | &lt;code&gt;string[] File.ReadAllLines(path)&lt;/code&gt; | 逐行读出 |
| 读全文 | &lt;code&gt;string File.ReadAllText(path)&lt;/code&gt; | 整段读成字符串 |
| 删除 | &lt;code&gt;File.Delete(path)&lt;/code&gt; | &lt;strong&gt;删除打开着的文件会报错&lt;/strong&gt; |
| 复制 | &lt;code&gt;File.Copy(src, dest, true)&lt;/code&gt; | 现有文件需是流关闭状态；第三参数 true=目标存在就覆盖 |
| 替换 | &lt;code&gt;File.Replace(源, 被替换, 备份)&lt;/code&gt; | 源内容顶掉目标内容，原目标内容转入备份文件 |
| 流打开 | &lt;code&gt;File.Open(path, FileMode, FileAccess)&lt;/code&gt; | 返回 FileStream，衔接文件流课程 |
| 追加 | &lt;code&gt;File.AppendAllText / AppendAllLines&lt;/code&gt; | WriteAll 系列是覆盖，想追加用它们（F12 可见） |&lt;/p&gt;
&lt;h3&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;判断存在&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool Directory.Exists(path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;文件夹是否存在&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;创建&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DirectoryInfo Directory.CreateDirectory(path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;返回 DirectoryInfo&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;删除&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Directory.Delete(path, true)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;第二参数 recursive：true 连根删，false 只删空目录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查找&lt;/td&gt;
&lt;td&gt;&lt;code&gt;string[] Directory.GetDirectories(path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;该路径下所有子文件夹名（全路径）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查找&lt;/td&gt;
&lt;td&gt;&lt;code&gt;string[] Directory.GetFiles(path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;该路径下所有文件名（全路径）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;移动&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Directory.Move(src, dest)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;移动，也可当重命名用&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&lt;/h3&gt;
&lt;p&gt;两个信息类&lt;/p&gt;
&lt;p&gt;FilInfo&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FileInfo[] finfos = dInfo.GetFiles();
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;for (int i = 0; i &amp;lt; finfos.Length; i++)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;{
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;    print(&quot;***************&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;    print(finfos[i].Name);      // 文件名
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;    print(finfos[i].FullName);  // 全路径
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;    print(finfos[i].Length);    // 字节长度
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;    print(finfos[i].Extension); // 后缀名
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DirectoryInfo&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1.CreateDirectory 的返回值
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;DirectoryInfo dInfo = Directory.CreateDirectory(Application.dataPath + &quot;/数据持久化123&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt; 2.Directory.GetParent 查上级文件夹信息
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;dInfo = Directory.GetParent(Application.dataPath + &quot;/数据持久化123&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// 常用属性
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;print(dInfo.FullName); // 全路径
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;print(dInfo.Name);     // 文件夹名
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// 重要方法
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;DirectoryInfo[] dInfos = dInfo.GetDirectories(); // 所有子文件夹的目录信息
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;FileInfo[] finfos = dInfo.GetFiles();            // 该文件夹下所有文件的信息
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Unity 二进制序列化：BinaryFormatter</title><link>https://jinshuangyan.top/posts/unity-binaryformatter/</link><guid isPermaLink="true">https://jinshuangyan.top/posts/unity-binaryformatter/</guid><pubDate>Tue, 06 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;两个核心 API&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;干什么&lt;/th&gt;
&lt;th&gt;相当于 JSON 里的&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bf.Serialize(stream, obj)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;对象 → 2进制，写进流&lt;/td&gt;
&lt;td&gt;&lt;code&gt;JsonUtility.ToJson&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bf.Deserialize(stream)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;从流读回 object（&lt;strong&gt;要 as 转型&lt;/strong&gt;）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;JsonUtility.FromJson&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;用法一：存文件 / 读文件（配 FileStream）&lt;/h2&gt;
&lt;p&gt;csharp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 存档：对象 → 文件
using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
{
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(fs, p);
    fs.Flush();
    fs.Close();
}


// 读档：文件 → 对象
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
{
    BinaryFormatter bf = new BinaryFormatter();
    Person p = bf.Deserialize(fs) as Person;
    fs.Close();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;用法二：字节数组 ↔ 对象（配 MemoryStream，网络传输场景）&lt;/h2&gt;
&lt;p&gt;csharp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 对象 → 字节数组
using (MemoryStream ms = new MemoryStream())
{
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(ms, p);
    byte[] bytes = ms.ToArray();   // GetBuffer() 也行，但带 0 填充
}


// 字节数组 → 对象
using (MemoryStream ms = new MemoryStream(bytes))
{
    BinaryFormatter bf = new BinaryFormatter();
    Person p = bf.Deserialize(ms) as Person;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;踩坑&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;嵌套类型没加 &lt;code&gt;[System.Serializable]&lt;/code&gt; → &lt;code&gt;SerializationException&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GetBuffer()&lt;/code&gt; 带 0 填充，要精确字节数用 &lt;code&gt;ToArray()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Deserialize&lt;/code&gt; 返回 &lt;code&gt;object&lt;/code&gt;，忘了 &lt;code&gt;as&lt;/code&gt; 编译不过。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>