<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh_CN">
  <title>jinshuangyan</title>
  <subtitle>Unity · C# · 学习笔记</subtitle>
  <link href="https://jinshuangyan.top/" rel="alternate" type="text/html"/>
  <link href="https://jinshuangyan.top/atom.xml" rel="self" type="application/atom+xml"/>
  <id>https://jinshuangyan.top/</id>
  <updated>2026-10-11T00:00:00.000Z</updated>
  <entry>
    <title>解决通信中的粘包和分包问题</title>
    <link href="https://jinshuangyan.top/posts/unity-tcp-sticky-packet/" rel="alternate" type="text/html"/>
    <id>https://jinshuangyan.top/posts/unity-tcp-sticky-packet/</id>
    <published>2026-10-11T00:00:00.000Z</published>
    <updated>2026-10-11T00:00:00.000Z</updated>
    <summary></summary>
    <content type="html"><![CDATA[<p>byte[] tempbuffer = new byte[1024 * 1024];   // 缓冲区：收到的字节全攒这里</p>
<p>int templen;                                 // 写指针：缓冲区有效数据的末尾</p>
<p>int nowindex;                                // 读指针：解析到哪了</p>
<p>public void HandResMeg(byte[] buffer, ref int offset)</p>
<p>{</p>
<pre><code>// ① 收多少存多少：永远追加到已有数据的末尾（templen），不能写 nowindex 位置

buffer.CopyTo(tempbuffer, templen);

templen += buffer.Length;

while (true)

{

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

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

    if (templen - nowindex &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 &lt; 8 + len) break;

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

    nowindex += 8;

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

    {

        switch (id)

        {

            case 1111:

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

                Playerinfo info = new Playerinfo();

                info.Reading(tempbuffer, ref nowindex);

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

                break;

            default:

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

                nowindex += len;

                break;

        }

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

    }

}

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

if (nowindex == templen)

{

    nowindex = 0;

    templen = 0;

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