自由学习记录(36)

Linux

Linux 是一个开源的操作系统,其内核及大部分组件都遵循自由软件许可证(如 GPL) ,允许用户查看、修改和分发代码。这种开放性使得开发者和企业可以根据自己的需求定制系统​

"Linux"严格来说只是指由Linus Torvalds最初开发的那个内核,也就是系统的"心脏"。而我们平时说的"Linux操作系统"或"Linux系统",通常是指基于这个内核构建的各种发行版(Distribution),例如Ubuntu、Debian、CentOS、Fedora等。这些发行版除了包含Linux内核之外,还集成了GNU工具、图形界面、软件包管理器和其他应用程序,从而为用户提供了完整的操作系统环境。

双启动(Dual Boot)系统允许你在同一台电脑上安装多个操作系统,但在开机时只能运行其中一个。如果你想切换到另一个操作系统,就需要重启电脑,并在启动菜单中选择另一个系统。

如果你希望在Windows系统中同时体验和使用Linux,而不必重启电脑切换系统,那就使用虚拟机

这里使用VMware

虚拟机本身只是一个容器,并不自带操作系统。所以你需要从Linux发行版官方网站下载一个ISO镜像文件,然后在虚拟机里用这个镜像来安装Linux系统。这就像在一台真实电脑上安装系统一样,只不过虚拟机里的安装过程与实际硬件隔离开了。

"Linux系统"通常指的是由Linux内核加上一系列软件(如GNU工具、桌面环境、应用程序等)构成的完整操作系统。而**"镜像文件"是一个包含了操作系统安装所需全部文件的单个文件** (通常是++ISO格式++)

Python的学习

环境的搭建

一下子还没调整过来,,...先找B站资源吧


python的下载和PyCharm的使用

"将 bin 文件夹添加到 PATH"

  • 作用
    • 这个选项会将 PyCharm 的 bin 目录添加到 系统的环境变量 PATH 中。
    • 这样,你可以在 命令行(cmd / PowerShell / 终端) 直接输入 pycharmcharm 来启动 PyCharm,而不需要手动进入安装目录。

创建关联

".py"

  • 作用
    • 这个选项会将 .py 文件默认关联到 PyCharm。
    • 这样,双击 .py 文件时,会自动用 PyCharm 打开,而不是其他编辑器(如 Notepad++、VS Code 或默认的 IDLE)。
  • 影响
    • 如果勾选 ,以后双击 .py 文件时,会直接在 PyCharm 里打开。
    • 如果不勾选,可能还是默认用 Windows 自带的 Python IDLE 或其他软件打开。

python的venv文件夹是虚拟环境文件夹,创建新的脚本不能下载到这个文件夹

一给代码就运行了,做不到多行执行

所以这个时候就诞生了PyCharm这样的

直接python解释器解释 这个文件夹的文件内容了

一些print相关

单行注释

而多行注释为三个引号括起来""" """

print(内部可以用逗号多个隔开,直接连接)

我们通过type(变量)可以输出类型,这是查看变量的类型还是数据的类型?

查看的是:变量存储的数据的类型 。因为,变量无类型,但是它存储的数据有。

Unity的学习

在 Protocol Buffers(protobuf)中,并不能直接定义 C# 中所有的泛型容器,比如 Dictionary、Queue 或其他泛型集合,但 protobuf 提供了两种常用的集合类型来满足大部分需求:

  1. repeated 字段

    • 用于表示数组、列表或队列等顺序数据集合。
    • 在生成的 C# 代码中,这类字段会被映射为 RepeatedField<T> 类型。
    • 示例:定义一个字符串数组或一个浮点数队列,都可以用 repeated 实现。
  2. map 字段

    • 用于表示字典数据结构(键值对)。
    • 生成的 C# 代码中,map 字段会被映射为 MapField<TKey, TValue> 类型。
    • 注意:map 的键必须是标量类型(例如:int32、string 等),值可以是任意允许的类型。

下面给出一个示例 proto 文件,说明如何定义 int/string 的字典、string 数组和 float 队列:

cs 复制代码
syntax = "proto3";

message MyData {
  // 定义一个字典,key 为 string,value 为 int32
  map<string, int32> myDictionary = 1;

  // 定义一个字符串数组(或列表)
  repeated string myStringArray = 2;

  // 定义一个浮点数队列,这里用 repeated 表示
  // 注意:protobuf 不区分队列和数组,具体的队列操作需要在 C# 代码中实现
  repeated float myFloatQueue = 3;
}

对应到 C# 的生成代码

  • map<string, int32> 会生成一个 MapField<string, int> 类型的属性,例如 public MapField<string, int> MyDictionary { get; }
  • repeated string 会生成一个 RepeatedField<string> 类型的属性,例如 public RepeatedField<string> MyStringArray { get; }
  • repeated float 会生成一个 RepeatedField<float> 类型的属性,例如 public RepeatedField<float> MyFloatQueue { get; }

关于其他泛型容器

如果你在 C# 中使用其他泛型容器(例如 List<T>、Queue<T>),需要注意的是:

  • 在 proto 文件中,你只能用 repeated 来表达集合;
  • 生成的代码中的 RepeatedField<T> 实际上类似于一个只读列表,你可以将其转换成你需要的类型,但 protobuf 本身不提供额外的泛型容器支持。

定义一个简单的 proto 文件

假设我们有如下的 person.proto 文件:

复制代码
syntax = "proto3";

option csharp_namespace = "MyApp.Protos";

message Person {
  int32 id = 1;
  string name = 2;
}

解释:

  • syntax:指定使用 proto3 语法版本。
  • option csharp_namespace:指定生成的 C# 代码所属的命名空间。
  • message Person :定义了一个消息类型 Person
  • 字段
    • id:一个整型字段,对应字段编号为 1
    • name:一个字符串字段,对应字段编号为 2
bash 复制代码
protoc --csharp_out=./Generated person.proto

这条cmd命令会在 ./Generated 目录下生成一个 Person.cs 文件,文件中包含了 Person 类的定义

你可以像使用普通的 C# 类那样使用生成的 Protobuf 类。下面给出一个简单的示例,展示如何实例化、赋值、序列化以及反序列化 Person 类:

cs 复制代码
using System;
using System.IO;
using MyApp.Protos;       // 使用在 .proto 文件中指定的命名空间
using Google.Protobuf;    // 引用 Google.Protobuf 库

class Program
{
    static void Main(string[] args)
    {
        // 1. 实例化 Person 对象并设置属性
        Person person = new Person
        {
            Id = 1,
            Name = "Alice"
        };

        // 2. 序列化:将对象转换为二进制数据
        // 方法一:使用 WriteTo 写入到流中
        using (MemoryStream stream = new MemoryStream())
        {
            // 将 person 对象写入流中
            person.WriteTo(stream);

            // 获取二进制数据
            byte[] data = stream.ToArray();
            Console.WriteLine("序列化后的二进制数据长度: " + data.Length);

            // 3. 反序列化:从二进制数据还原对象
            // 使用 Person.Parser.ParseFrom 方法从字节数组解析数据
            Person deserializedPerson = Person.Parser.ParseFrom(data);

            Console.WriteLine($"反序列化后: Id = {deserializedPerson.Id}, Name = {deserializedPerson.Name}");
        }

        // 方法二:直接使用 ToByteArray 方法
        byte[] bytes = person.ToByteArray();
        Person person2 = Person.Parser.ParseFrom(bytes);
        Console.WriteLine($"使用 ToByteArray 反序列化后: Id = {person2.Id}, Name = {person2.Name}");
    }
}

sealed 关键字表示这个类不能被继承,也就是说,不能再创建继承自 Person 的子类。

是为了保证序列化逻辑的一致性与安全性,同时也有助于提升性能(因为编译器可以进行更多优化)

partial 关键字允许一个类的定义可以分布在多个文件中

如果没有 partial,你将无法在不修改生成文件的情况下扩展该类。这就意味着每次重新生成代码时,你都需要手动整合你的自定义修改,容易出错且不利于维护。

person.WriteTo(stream)person.ToByteArray() 都是 Protobuf 生成代码中预先定义好的方法,用于实现序列化和反序列化操作。这两个方法的作用分别是:

  • WriteTo(stream)

    person 对象按照 Protobuf 协议格式序列化,并写入到指定的流中(例如 MemoryStream 或文件流)。

  • ToByteArray()

    person 对象序列化为一个字节数组,这样你可以直接获得序列化后的二进制数据。

其他常用的方法

除了上述两个方法,Protobuf 生成的类还包含其他一些常用的方法,主要包括:

  • CalculateSize()

    计算序列化后消息占用的字节数。这对于提前分配足够大小的缓冲区非常有用。

  • MergeFrom(...)

    将传入的二进制数据或流中的数据合并到当前对象中。常见的重载版本有:

    • MergeFrom(CodedInputStream input)
    • MergeFrom(byte[] data)

    这使得你可以在已有对象的基础上更新数据,而不是每次都创建新的对象。

  • Clone()

    创建当前消息对象的深拷贝。

  • Parser.ParseFrom(...)

    这是一个静态方法,通过 Person.Parser 访问。它提供了从二进制数据或流中直接解析出 Person 对象的功能。例如:

    Person person2 = Person.Parser.ParseFrom(bytes);

  • ToString()

    重写了 ToString() 方法,通常用于调试时获取消息的文本表示。

cs 复制代码
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    // 构造函数,必须传入 id 和 name
    public Person(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

// 使用构造函数创建对象:
Person person1 = new Person(1, "Alice");

如果有无参构造函数则可以new Person{ }

cs 复制代码
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    // 无参构造函数
    public Person() { }
}

// 使用对象初始化器:
Person person2 = new Person
{
    Id = 1,
    Name = "Alice"
};

当然,也可以两种混着用

cs 复制代码
public class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Grade { get; set; }

    // 带参数的构造函数
    public Student(string name)
    {
        Name = name;
    }
}

// 使用带参数构造函数,再通过对象初始化器设置其他属性:
Student student = new Student("Bob")
{
    Age = 20,
    Grade = "A"
};

protobuf 的协议生成,


什么是Protobuf

Protobuf全称是protocol-buffers(协议缓冲区),是谷歌提供给开发者的一个开源的协议生成工具

//它的主要工作原理和我们之前做的自定义协议工具类似

//只不过它更加的完善,可以基于协议配置文件生成

//C++、Java、C#、Objective-C、PHP、Python、Ruby、Go等等语言的代码文件

//它是商业游戏开发中常常会选择的协议生成工具

//有很多游戏公司选择它作为协议工具来进行网络游戏开发

//因为它通用性强,稳定性高,可以节约出开发自定义协议工具的时间

//protocol-buffers官网

https://developers.google.com/protocol-buffers

#endregion

协议本质上是一套双方都认可并遵循的通信规则,规定了数据如何格式化、传输和解释 。这些规则是语言无关的 ,它们++只描述"做什么"而不是"如何实现"++。

如果你在客户端的 C# 代码中编写了所有用于解析、响应服务端请求的逻辑,这些代码实际上是对协议的实现。

协议本身仍然是那个双方共同理解的数据交换规则,而你用 C# 写的代码只是用来满足这些规则的手段。

需要注意的是,写完的成员变量默认就是已经存在里面了,已经可以当正常的类来写了

在这种生成式的代码里面,对于自身类的函数方法,要根据自身的成员变量有哪些类型去确认自己要转换成byte数组,需要多少位时,

在自己写进去的脚本里,包含了别的自己写进去的类和枚举

又开了一个类 专门管理 生成脚本的逻辑,传入的参数只需要是XmlNodeList nodes

就是纯正的xml格式被c#的相关类吸收成NodeList

#region知识点一协议(消息)生成主要做什么

//协议生成主要是使用配置文件中读取出来的信息

//动态的生成 对应语言的代码文件

1/每次添加消息或者数据结构类时,我们不需要再手写代码了

//我们不仅可以生成c#脚本文件,还可以根据需求生成别的语言的文件

#endregion

#region知识点二制作功能前的准备工作

//协议生成 是不会在发布后使用的功能,主要是在开发时使用

//所以我们在Unity当中可以把它作为一个编辑器功能来做

//因此我们可以专门新建一个Editor文件夹(专门放编辑器相关内容,不会发布)

//在具中放置配置文件、自动生成相关脚本文件

#endregion


这都是在协议的自定义里的

(//field)[last()-2]

  • last() 代表最后一个节点。
  • last()-2 代表倒数第三个 field

//enum[@name='E_MONSTER_TYPE']/field[2]

  • //enum[@name='E_MONSTER_TYPE'] → 选中 name="E_MONSTER_TYPE"<enum>
  • /field[2] → 在 E_MONSTER_TYPE 里面,选择 第二个 field (即 BOSS)。

  • (//field)[3]
    • //field → 选择所有 <field> 节点(不管在哪)。
    • (//field)[3] → 括号表示把它当作一个集合,取第 3 个元素(索引从 1 开始)。
  • SelectSingleNode() 只返回第一个匹配的 field ,这里确保获取的就是 第三个

不用字段< />

要字段则<message > </message>

xml的数据格式去定义对应的规则(节点式的规则)

也是自定义的管理模版


enumNode.Attributes["name"].Value 获取 name 属性值。

可以使用 XmlDocumentXDocument (Linq to XML) 来解析 XML 数据并转换成 C# 对象结构


SelectSingleNode("//field") → 获取第一个匹配的 field

SelectNodes("//field") → 获取所有 field 节点

如果你只想找出 第一个 <field> 节点的值,而不是所有的 <field>,你可以使用 SelectSingleNode() 方法,而不是 SelectNodes()

写法 是否完整 是否有文本内容 适用场景
<field name="MAIN">1</field> ✅完整写法 ✅有文本内容 1 存储具体数据
<field name="OTHER"/> ✅完整(但简写) ❌无文本内容 仅存储属性信息

如果你的 XML 需要存储复杂的数组结构,可以直接把 JSON 作为字符串存储


结构体的存储方法(那也可以塞在属性里)

XML 复制代码
<players>
    <player>
        <name>Alice</name>
        <level>20</level>
    </player>
    <player>
        <name>Bob</name>
        <level>15</level>
    </player>
</players>
cs 复制代码
XmlDocument doc = new XmlDocument();
doc.Load("data.xml");
XmlNodeList playerNodes = doc.SelectNodes("//players/player");

foreach (XmlNode node in playerNodes)
{
    Console.WriteLine(node.InnerText); // 输出 Alice, Bob, Charlie
}

在c#中的对这些xml节点的解析

XML 中,**数组(或列表)**的存储方式并没有一种固定的标准,而是取决于你如何组织数据结构。

  • 第一种方式适合简单数据,适用于轻量级 XML
  • 第二种方式更适合复杂结构,便于扩展和查询。

属性可以赋值也可以不赋值

可以只使用属性而不提供文本内容

<field name="MAIN">1</field>

里面的 1元素的文本内容 ,也就是 field 的值

  • field 是 XML 的元素(节点)。
  • name="MAIN"field 的属性:
    • name 是属性的名称
    • "MAIN" 是该属性的 ,用引号包裹(可以是双引号 "" 或单引号 ''

属性标签内的键值对,它用于存储附加信息

协议生成工具的主要优势:

  1. 提升开发效率:自动化生成消息类,减少了手动编写的工作量,加快了开发进程。

  2. 降低沟通成本:前后端统一使用协议生成工具,确保消息格式一致,避免了因手动声明导致的格式不一致问题。

  3. 减少错误率:自动化生成的代码减少了人为错误的可能性,提高了代码的可靠性。

  4. 易于维护和扩展:当需要添加新消息时,只需在协议文件中定义,工具会自动生成相应的类,方便后续维护和扩展。

后面发现File作为泛型没有object好用,没改泛型的type判断,在传入参数的时候改成传入object了

到了最后的管理区域,只需要管要下什么,和要怎么用就可以了,也确实是方便,也是因为里面的泛型什么的省了很多事

有自己的类型可以自己去加

异步

  • 异步处理async/await 更注重于异步执行任务,通常用于 I/O 操作(例如文件读取、网络请求等),它能够避免阻塞主线程。使用 asyncawait,代码看起来是同步的,但实际上会在等待某些操作完成时暂停执行,允许其他任务继续进行。

  • 不能一帧一帧控制 :虽然 async 方法能够处理异步任务,但它不能像协程那样按帧执行。await 会让方法暂停,直到操作完成为止。在这一点上,async 方法更像是线程间的切换,控制力度较弱,只能基于任务的完成情况来决定下一步执行。

协程

  • 按帧控制 :协程允许你在执行过程中暂停和恢复,这种暂停可以基于时间(例如 WaitForSeconds)或者每一帧的控制(例如 yield return null)。这样你就可以控制协程每一帧的行为,逐步执行任务。

  • 更高的控制力 :由于协程是基于 IEnumerator 的,你可以在每次暂停时根据具体的条件决定是否继续执行,也可以在每一帧的执行过程中进行复杂的判断和操作。这使得协程非常适合逐步执行的任务,比如动画、物理模拟、加载过程等。

协程的控制力更强,适合精细的帧间控制,但在使用上稍微复杂一些。

async/await 使得异步任务的使用更加方便,代码更简洁,但它不能像协程那样逐帧控制,适合处理 I/O 密集型任务。

如果协程有多个 yield return 表达式,yield break 会导致协程提前终止,跳过后续的操作

yield break :在协程中,yield break结束整个协程的执行

请在一个NetWWWMgr的管理类 当中,封装一个基于
UnityWebRequest下载远程数据
加载本地数据 的方法

加载完成后,通过委托的形式外部使用


#region知识点一回顾高级操作中的获取数据

//主要做法:将2进制字节数组处理 ,独立到下载处理对象中进行处理

---------------主要就是设置unityWebRequest对象中 downloadHandler 变量

//Unity写好的类有

//1.DownloadHandlerBuffer 用于简单的数据存储,得到对应的2进制数据。

//2.DownloadHandlerFile 用于下载文件并将文件保存到磁盘(内存占用少)。

//3.DownloadHandlerTexture 用于下载图像。

//4.DownloadHandlerAssetBundle 用于提取 AssetBundle。

//5.DownloadHandlerAudioClip用于下载音频文件。

自己要拓展处理方式,继承DownloadHandlerScript,并重写其中的固定方法,自己处理字节数组

相关推荐
西岸行者6 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意6 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码6 天前
嵌入式学习路线
学习
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习
im_AMBER6 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J6 天前
从“Hello World“ 开始 C++
c语言·c++·学习