当前位置: 首页 > article >正文

自由学习记录(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 队列:

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
protoc --csharp_out=./Generated person.proto

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

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

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() 方法,通常用于调试时获取消息的文本表示。

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{      }

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

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

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

当然,也可以两种混着用

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 作为字符串存储

--------

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

<players>
    <player>
        <name>Alice</name>
        <level>20</level>
    </player>
    <player>
        <name>Bob</name>
        <level>15</level>
    </player>
</players>
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,并重写其中的固定方法,自己处理字节数组


http://www.kler.cn/a/555365.html

相关文章:

  • C语言之typedef
  • MySQL分库分表之ShardingSphere实战
  • 【Spring详解四】自定义标签的解析
  • EasyExcel的简单使用
  • 网络缓存加速技术解析:从诞生到演进
  • Spring Cloud LoadBalancer 负载均衡
  • 自然语言处理:第九十二章 chatBI 经验(转载)
  • 【DeepSeek】如何将DeepSeek部署到本地?如何给本地 LLM 提供UI界面?CherryStudio 的使用
  • 对CSS了解哪些?
  • Lab12_ Blind SQL injection with conditional errors
  • MariaDB10创建用户并授权
  • 一个前端,如何同时联调多个后端
  • Qt的QStackedWidget样式设置
  • 数据结构之堆(Heap)
  • 【C#零基础从入门到精通】(二十六)——C#三大特征-多态详解
  • Airtest与持续集成(CI)工具的集成实操案例
  • 【Leetcode 每日一题】2595. 奇偶位数
  • Mac安装配置Tomcat 8
  • Django5 实用指南(四)URL路由与视图函数
  • 【CV前沿】YOLOv12: Attention-Centric Real-Time Object Detectors