【Unity】网格系统:物体使用网格坐标定位

需求分析

前面物体放置在地板上都是地板任意位置放置,本节开始对物体放置的位置做限制。

  • 建立网格,网格可以设置起始世界坐标、单元格大小和规格;
  • 单元格中包括内部物体的信息;
  • 物体的位置通过网格的坐标确定;
  • 单元格中已经存在物体,该位置不能再放入其他物体;

成果展示

![[obsidian://open?vault=MDnotes&file=gif_%E7%BD%91%E6%A0%BC%E7%B3%BB%E7%BB%9F.gif]]

Scene部分

![[png_网格系统-1.png]]

场景中删除了手动拖入的预制体,改为通过代码将物体在指定网格单元格中。
每个预制体都增加脚本PlaceObject.cs
![[png_网格系统-2.png]]

脚本部分

设计网格类GridXZ

属性:宽度高度、单元格大小、原点世界坐标位置、网格矩阵;
方法:

  • 创建网格(构造函数),需要将每个单元格填充入对应类实例。
  • 世界坐标和网格坐标相互转换,根据世界坐标位置获取网格坐标位置,根据网格坐标获取世界坐标位置。
  • 设置或获取单元格中实例,定义单元格中实例发生变化的响应事件。
public class GridXZ<TGridObject>
{
    public event EventHandler<OnGridObjectChangedEventArgs> OnGridObjectChanged;
    public class OnGridObjectChangedEventArgs : EventArgs
    {
        public int x;
        public int z;
    }

    private int width;
    private int height;
    private float cellSize;
    private Vector3 originPosition;
    private TGridObject[,] gridArray;

    public GridXZ(int width, int height, float cellSize, Vector3 originPosition, Func<GridXZ<TGridObject>, int, int, TGridObject> createGridObject)
    {
        this.width = width;
        this.height = height;
        this.cellSize = cellSize;
        this.originPosition = originPosition;

        gridArray = new TGridObject[width, height];

        for (int x = 0; x < gridArray.GetLength(0); x++)
        {
            for (int z = 0; z < gridArray.GetLength(1); z++)
            {
                gridArray[x, z] = createGridObject(this, x, z);
            }
        }

//绘制了调试线,帮助观察网格的状态
        #region debugDrawLine
        bool showDebug = true;
        if (showDebug)
        {
            TextMesh[,] debugTextArray = new TextMesh[width, height];

            for (int x = 0; x < gridArray.GetLength(0); x++)
            {
                for (int z = 0; z < gridArray.GetLength(1); z++)
                {
                    debugTextArray[x, z] = UtilsClass.CreateWorldText("", null, GetWorldPosition(x, z) + new Vector3(cellSize, 0, cellSize) * .5f,8, Color.white, TextAnchor.MiddleCenter, TextAlignment.Center);
                    Debug.DrawLine(GetWorldPosition(x, z), GetWorldPosition(x, z + 1), Color.white, 100f);
                    Debug.DrawLine(GetWorldPosition(x, z), GetWorldPosition(x + 1, z), Color.white, 100f);
                }
            }
            Debug.DrawLine(GetWorldPosition(0, height), GetWorldPosition(width, height), Color.white, 100f);
            Debug.DrawLine(GetWorldPosition(width, 0), GetWorldPosition(width, height), Color.white, 100f);

            OnGridObjectChanged += (object sender, OnGridObjectChangedEventArgs eventArgs) =>
            {
                debugTextArray[eventArgs.x, eventArgs.z].text = gridArray[eventArgs.x, eventArgs.z]?.ToString();
            };
        }

        #endregion
    }

    public int GetWidth()
    {
        return width;
    }

    public int GetHeight()
    {
        return height;
    }

    public float GetCellSize()
    {
        return cellSize;
    }

    public Vector3 GetWorldPosition(int x, int z)
    {
        return new Vector3(x, 0, z) * cellSize + originPosition;
    }

    public void GetXZ(Vector3 worldPosition, out int x, out int z)
    {
        x = Mathf.FloorToInt((worldPosition - originPosition).x / cellSize);
        z = Mathf.FloorToInt((worldPosition - originPosition).z / cellSize);
    }

    public void SetGridObject(int x, int z, TGridObject value)
    {
        if (x >= 0 && z >= 0 && x < width && z < height)
        {
            gridArray[x, z] = value;
            TriggerGridObjectChanged(x, z);
        }
    }

    public void TriggerGridObjectChanged(int x, int z)
    {
        OnGridObjectChanged?.Invoke(this, new OnGridObjectChangedEventArgs { x = x, z = z });
    }

    public void SetGridObject(Vector3 worldPosition, TGridObject value)
    {
        GetXZ(worldPosition, out int x, out int z);
        SetGridObject(x, z, value);
    }

    public TGridObject GetGridObject(int x, int z)
    {
        if (x >= 0 && z >= 0 && x < width && z < height)
        {
            return gridArray[x, z];
        }
        else
        {
            return default(TGridObject);
        }
    }

    public TGridObject GetGridObject(Vector3 worldPosition)
    {
        int x, z;
        GetXZ(worldPosition, out x, out z);
        return GetGridObject(x, z);
    }

    public Vector2Int ValidateGridPosition(Vector2Int gridPosition)
    {
        return new Vector2Int(
            Mathf.Clamp(gridPosition.x, 0, width - 1),
            Mathf.Clamp(gridPosition.y, 0, height - 1)
        );
    }
}

设计单元格内对象GridObject

属性:对应的网格实例、坐标、单元格中可以填充的内容。
针对单元格中可以填充的内容,不同的业务,设计的类可能不同。
本篇中,单元格中是放置处理过的物体预制体,为了统一每个预制体,所有可以放入单元格的预制体会绑定一个脚本PlaceObject
因此设计时,会加入该属性,当单元格中放入物体时,该属性会被赋值。当物体被销毁时,该属性值会被null
放该属性发生变化时,会触发网格类中定义的事件。

public class GridObject
{
    private GridXZ<GridObject> grid;
    private int x;
    private int z;

    private PlaceObject placeObject;
    public GridObject(GridXZ<GridObject> grid, int x, int z)
    {
        this.grid = grid;
        this.x = x;
        this.z = z;
    }

    public PlaceObject GetPlaceObject()
    {
        return placeObject;
    }

    public void SetPlaceObject(PlaceObject placeObject)
    {
        this.placeObject = placeObject;
        grid.TriggerGridObjectChanged(x, z);
    }
    public void ClearPlaceObject()
    {
        placeObject = null;
        grid.TriggerGridObjectChanged(x, z);
    }

    public bool CanBuild()
    {
        return placeObject == null;
    }
//可以用来标记单元格中的内容
    public override string ToString()
    {
        return x + "," + z;// + "\n" + placeObject?.goodsName;
    }
}

单元格中可以填充的内容类PlaceObject

将前面章节中实例化预制体的部分写入该类中。
属性:PlacedObjectTypeSO实例、物体原点坐标、方向、父物体。
可以创建物体、销毁物体;
获取物体所占的所有网格坐标;

public class PlaceObject : MonoBehaviour
{
    public static PlaceObject Create(
        Vector3 worldPosition, Vector2Int origin,
        PlacedObjectTypeSO.Dir dir, PlacedObjectTypeSO placedObjectTypeSO, Transform parent
        )
    {

        Transform placeObjectTransform = Instantiate(
            placedObjectTypeSO.prefab,
            worldPosition,
            Quaternion.Euler(0, placedObjectTypeSO.GetRotationAngle(dir), 0)
            );

        placeObjectTransform.SetParent(parent);
        placeObjectTransform.gameObject.SetActive(true);
        PlaceObject placeObject = placeObjectTransform.GetComponent<PlaceObject>();
        placeObject.origin = origin;
        placeObject.dir = dir;
        placeObject.placedObjectTypeSO = placedObjectTypeSO;

        return placeObject;
    }

    private PlacedObjectTypeSO placedObjectTypeSO;
    private Vector2Int origin;
    private PlacedObjectTypeSO.Dir dir;

    public void DestorySelf()
    {
        Destroy(gameObject);
    }

    public List<Vector2Int> GetGridPositionList()
    {
        return placedObjectTypeSO.GetGridPositionList(origin, dir);
    }

    public GoodsName goodsName {
        get {
            return placedObjectTypeSO.goodsName;
        }
    }
}

使用网格

修改PlaceObjectBuilding中网格相关的部分

实例化网格类

//初始状态,每个单元格中都没有物体
grid = new GridXZ<GridObject>(16, 16, 1, new Vector3(0, 0, 0), (GridXZ<GridObject> g, int x, int z) => new GridObject(g, x, z));

放置物体

传入网格坐标、方向、PlacedObjectTypeSO实例;
检查传入坐标单元格及物体需要占据的全部单元格是否已经被占用;
依次给所占的所有单元格中内容赋值;

[! WARNING] 修正物体放置的位置
鼠标选择单元格时,获取的是单元格中的任意位置,而物体放置时位置是以其Anchor为原点的。因此需要将鼠标的点击位置修正为单元格的Anchor,使物体能够刚好放入单元格中。
修正结果需要分别显现在物体放置和物体跟随鼠标两个地方。

Vector3 clickedPosition = Mouse3D.GetMouseWorldPosition();
grid.GetXZ(clickedPosition, out int x, out int z);
Vector2Int rotationOffset = placedObjectTypeSO.GetRotationOffset(dir);
Vector3 placedObjectWorldPosition = grid.GetWorldPosition(x, z) + new Vector3(rotationOffset.x, 0, rotationOffset.y) * grid.GetCellSize();

放置物体时,可以通过代码直接放置、也可以点击单元格放置。

private void ExcutePlaceObjectOnGrid(Vector2Int gridPosition, PlacedObjectTypeSO placedObjectTypeSO, Dir dir)
{
	Vector2Int rotationOffset = placedObjectTypeSO.GetRotationOffset(dir);
	Vector3 placedObjectWorldPosition = grid.GetWorldPosition(gridPosition.x, gridPosition.y) +
		new Vector3(rotationOffset.x, 0, rotationOffset.y) * grid.GetCellSize();

	//需要判断当前位置是否能够放置,是否已经被占用
	List<Vector2Int> locateGridPositions = placedObjectTypeSO.GetGridPositionList(gridPosition, dir);
	bool canBuild = true;
	foreach (var item in locateGridPositions)
	{
		if (!grid.GetGridObject(item.x, item.y).CanBuild())
		{
			canBuild = false;
			break;
		}
	}
	if (canBuild)
	{
		PlaceObject placeObject = PlaceObject.Create(placedObjectWorldPosition, gridPosition, dir, placedObjectTypeSO, transform.parent);

		//需要标记对应网格被占用
		locateGridPositions.ForEach(_ =>
		{
			grid.GetGridObject(_.x, _.y).SetPlaceObject(placeObject);
		});
	}

}

//直接代码放置物体
ExcutePlaceObjectOnGrid(new Vector2Int(3, 10), placedObjectTypeSOList[0], Dir.Down);

//点击单元格放置物体
private void Update()
{
	if (selectedPlacedObjectTypeSO != null)
	{
		if (Input.GetMouseButtonDown(0))
		{
			Vector3 placePosition = Mouse3D.GetMouseWorldPosition();
			grid.GetXZ(placePosition, out int x, out int z);

			Vector2Int rotationOffset = selectedPlacedObjectTypeSO.GetRotationOffset(dir);
			if (Mouse3D.GetClickedTransform().parent == transform.parent)
			{
				ExcutePlaceObjectOnGrid(new Vector2Int(x, z), selectedPlacedObjectTypeSO, dir);

				DeselectObjectType();
			}
		}
	}
}

删除物体

删除物体也同样需要清除物体所占用的所有单元格中的物体信息。

public void DestroyPlacedObject(PlaceObject placeObject)
{
    if (placeObject != null)
    {
        AddGoods(placeObject.goodsName);
        placeObject.DestorySelf();

        List<Vector2Int> gridPositionList = placeObject.GetGridPositionList();
        foreach (var gridPosition in gridPositionList)
        {
            grid.GetGridObject(gridPosition.x, gridPosition.y).ClearPlaceObject();
        }
    }
}

使用删除
业务中,是玩家触碰到物体时,物体被摧毁并放入仓库,因此在Player.cs脚本中修改相关的代码。

public class Player : MonoBehaviour
{
    private void OnTriggerEnter(Collider c)
    {
        Transform cProfab = c.transform.parent.parent;
        if (Enum.TryParse(cProfab.tag, true, out GoodsName goodsName))
        {
            
            PlaceObjectBuilding.Instance.DestroyPlacedObject(cProfab.GetComponent<PlaceObject>());
        }
    }
}

完整代码

public class PlaceObjectBuilding : MonoBehaviour
{
	private void Awake()
	{
	    Instance = this;
	
	    inventory = new Inventory(new List<Goods>(), (goods) =>
	    {
	        inventory.DeleteGoods(goods.GetGoodsName());
	        selectedPlacedObjectTypeSO = placedObjectTypeSOList.Find(_ => _.nameString == goods.GetGoodsName().ToString());
	        RefreshSelectedObjectType();
	    });
	
	    ui_inventory.Init(inventory);
	
	    grid = new GridXZ<GridObject>(16, 16, 1, new Vector3(0, 0, 0), (GridXZ<GridObject> g, int x, int z) => new GridObject(g, x, z));
	
	    inventory.AddGoods(placedObjectTypeSOList[1]);
	    inventory.AddGoods(placedObjectTypeSOList[1]);
	    inventory.AddGoods(placedObjectTypeSOList[2]);
	    inventory.AddGoods(placedObjectTypeSOList[3]);
	    inventory.AddGoods(placedObjectTypeSOList[4]);
	
	    ExcutePlaceObjectOnGrid(new Vector2Int(3, 10), placedObjectTypeSOList[0], Dir.Down);
	    ExcutePlaceObjectOnGrid(new Vector2Int(15, 6), placedObjectTypeSOList[0], Dir.Down);
	    ExcutePlaceObjectOnGrid(new Vector2Int(9, 1), placedObjectTypeSOList[2], Dir.Down);
	}
	
	private void ExcutePlaceObjectOnGrid(Vector2Int gridPosition, PlacedObjectTypeSO placedObjectTypeSO, Dir dir)
    {
        Vector2Int rotationOffset = placedObjectTypeSO.GetRotationOffset(dir);
        Vector3 placedObjectWorldPosition = grid.GetWorldPosition(gridPosition.x, gridPosition.y) +
            new Vector3(rotationOffset.x, 0, rotationOffset.y) * grid.GetCellSize();

        //需要判断当前位置是否能够放置,是否已经被占用
        List<Vector2Int> locateGridPositions = placedObjectTypeSO.GetGridPositionList(gridPosition, dir);
        bool canBuild = true;
        foreach (var item in locateGridPositions)
        {
            if (!grid.GetGridObject(item.x, item.y).CanBuild())
            {
                canBuild = false;
                break;
            }
        }
        if (canBuild)
        {
            PlaceObject placeObject = PlaceObject.Create(placedObjectWorldPosition, gridPosition, dir, placedObjectTypeSO, transform.parent);

            //需要标记对应网格被占用
            locateGridPositions.ForEach(_ =>
            {
                grid.GetGridObject(_.x, _.y).SetPlaceObject(placeObject);
            });
        }

    }
    
    private void Update()
    {
        if (selectedPlacedObjectTypeSO != null)
        {
            if (Input.GetMouseButtonDown(0))
            {
                Vector3 placePosition = Mouse3D.GetMouseWorldPosition();
                grid.GetXZ(placePosition, out int x, out int z);

                Vector2Int rotationOffset = selectedPlacedObjectTypeSO.GetRotationOffset(dir);
                if (Mouse3D.GetClickedTransform().parent.parent == craftTable.parent)
                {
                    PlaceObject.Create(placePosition + new Vector3(rotationOffset.x, 0, rotationOffset.y), Vector2Int.zero, dir, selectedPlacedObjectTypeSO, craftTable);
                    craftingRecipeSOList.ForEach(_ =>
                    {
                        PlacedObjectTypeSO outGoodsSo = _.GoodsOnTableChanged(selectedPlacedObjectTypeSO);

                        if (outGoodsSo != null)
                        {
                            for (int i = 0; i < craftTable.childCount; i++)
                            {

                                Destroy(craftTable.GetChild(i).gameObject);
                            }
                            craftingRecipeSOList.ForEach(recipeSo =>
                            {
                                recipeSo.Init();
                            });
                            inventory.AddGoods(outGoodsSo);
                        };
                    });

                    DeselectObjectType();

                }
                else if (Mouse3D.GetClickedTransform().parent == transform.parent)
                {
                    ExcutePlaceObjectOnGrid(new Vector2Int(x, z), selectedPlacedObjectTypeSO, dir);

                    DeselectObjectType();
                }
            }

            if (Input.GetKeyDown(KeyCode.Alpha0))
            {
                GoodsName goodsName = (GoodsName)Enum.Parse(typeof(GoodsName), selectedPlacedObjectTypeSO.nameString);
                inventory.AddGoods(selectedPlacedObjectTypeSO);
                DeselectObjectType();

            }

            if (Input.GetKeyDown(KeyCode.R))
            {
                dir = GetNextDir(dir);
            }
        }
    }
    public Vector3 GetMouseWorldSnappedPosition()
	{
	
	    Vector3 mousePosition = Mouse3D.GetMouseWorldPosition();
	    if (grid == null) return mousePosition;
	    grid.GetXZ(mousePosition, out int x, out int z);
	
	    if (selectedPlacedObjectTypeSO != null)
	    {
	        Vector2Int rotationOffset = selectedPlacedObjectTypeSO.GetRotationOffset(dir);
	        Vector3 placedObjectWorldPosition = grid.GetWorldPosition(x, z) + new Vector3(rotationOffset.x, 0, rotationOffset.y) * grid.GetCellSize();
	        return placedObjectWorldPosition;
	    }
	    else
	    {
	        return mousePosition;
	    }
	}
	
	public void DestroyPlacedObject(PlaceObject placeObject)
	{
	    if (placeObject != null)
	    {
	        AddGoods(placeObject.goodsName);
	        placeObject.DestorySelf();
	
	        List<Vector2Int> gridPositionList = placeObject.GetGridPositionList();
	        foreach (var gridPosition in gridPositionList)
	        {
	            grid.GetGridObject(gridPosition.x, gridPosition.y).ClearPlaceObject();
	        }
	    }
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/918831.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络协议(4)拥塞控制

之前已经说过了tcp也是会考虑网络的情况的&#xff0c;也就是当网络出现问题的时候tcp不会再对报文进行重传。当所有的用户在网络不好的时候都不会对丢失的报文进行重传。这样就会防止网络瘫痪。 这样的机制也就是tcp会进行拥塞控制。 拥塞控制 所谓的慢启动看下面这张图就能…

集群聊天服务器(8)用户登录业务

目录 登录状态业务层代码数据模型层代码记录用户的连接信息以及线程安全问题客户端异常退出业务 登录状态 登录且状态变为online 业务层代码 #include "chatservice.hpp" #include "public.hpp" #include <string> #include <muduo/base/Loggi…

生成自签名证书并配置 HTTPS 使用自签名证书

生成自签名证书 1. 运行 OpenSSL 命令生成证书和私钥 在终端中输入以下命令&#xff0c;生成自签名证书和私钥文件&#xff1a; sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout self_signed.key -out self_signed.pem-x509&#xff1a;生成自签名证书。…

鸿蒙HarmonyOS 地图定位到当前位置 site查询等操作

应用服务Map使用 地图定位 地点查询及导航 周边查询 点位标记定义等 地图定位 前提地图已经能正常显示&#xff0c;若不能显示请大家参考之前的那篇如何显示地图的博文 地图相关的api 位置效果图&#xff1a; module.json5配置权限 "requestPermissions": [{&…

matlab-fmincon函数做优化、optimoptions用法

定义&#xff1a; x fmincon(fun,x0,A,b,Aeq,beq,lb,ub,nonlcon,options) 含义&#xff1a;从 x0 开始&#xff0c;尝试在满足线性等式 Aeq*x beq 以及不等式 A*x ≤ b、 c(x) ≤ 0 和 ceq(x) 0 的情况下寻找 fun 中所述的函数的最小值点 x。对 x 中的设计变量定义一组下界…

prop校验,prop和data区别

prop:组件上注册的一些自定义属性 prop作用&#xff1a;向子组件传递数据 特点&#xff1a; 可以传递任意数量&#xff0c;任意类型的prop 父组件 &#xff08;一个个地传递比较麻烦&#xff0c;可以直接打包成一个对象传过去&#xff0c;然后通过点属性接收&#xff09; <t…

K8S containerd拉取harbor镜像

前言 接前面的环境 K8S 1.24以后开始启用docker作为CRI&#xff0c;这里用containerd拉取 参考文档 正文 vim /etc/containerd/config.toml #修改内容如下 #sandbox_image "registry.aliyuncs.com/google_containers/pause:3.10" systemd_cgroup true [plugins.…

ClickHouse的介绍、安装、数据类型

1、介绍和安装 1.1、简介 ClickHouse是俄罗斯的Yandex于2016年开源的列式存储数据库&#xff08;DBMS&#xff09;&#xff0c;使用C语言编写&#xff0c;主要用于在线分析处理查询&#xff08;OLAP&#xff09;&#xff0c;能够使用SQL查询实时生成分析数据报告。 OLAP&…

Websocket如何分块处理数据量超大的消息体

若我们服务端一次性最大处理的字节数是1M,而客户端发来了2M的数据&#xff0c;此时服务端的数据就要被切割成两次传输解码。Http协议中有分块传输&#xff0c;而在Websocket也可以分块处理超大的消息体。在jsr356标准中使用javax.websocket.MessageHandler.Partial可以分块处理…

卡尔曼滤波学习资料汇总

卡尔曼滤波学习资料汇总 其实&#xff0c;当初的目的&#xff0c;是为了写 MPU6050 的代码的&#xff0c;然后不知不觉学了那么多&#xff0c;也是因为好奇、感兴趣吧 有些还没看完&#xff0c;之后笔记也会同步更新的 学习原始材料 【卡尔曼滤波器】1_递归算法_Recursive P…

Javaweb-day13事务管理AOP

spring的事务管理&spring框架的第二大核心AOP面向切面编程 spring框架的第一大核心是前面讲的IOC控制反转 事务管理 事务回顾 概念&#xff1a;事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;这些操作要么同时成功&#xff0c;要么同时失败。…

JavaWeb后端开发知识储备1

目录 1.DTO/VO/PO 2.MVC架构/微服务架构 3.JWT令牌流程 4.ThreadLocal 5.接口路径/路径参数 6.自定义注解 1.DTO/VO/PO 1.1 DTO DTO 即 Data Transfer Object—— 数据传输对象&#xff0c;是用于传输数据的对象&#xff0c;通常在服务层与表现层之间传递数据&#xff…

BLE 蓝牙客户端和服务器连接

蓝牙通信在设计小型智能设备时非常普遍&#xff0c;之前一直没有使用过&#xff0c;最近使用ardunio ESP32 做了一些实验&#xff0c;做了一个收听播客的智能旋钮&#xff08;Smart Knob&#xff09;&#xff0c;它带有一个旋转编码器和两个按键。 本文介绍BLE 服务器Server和W…

海康威视和大华视频设备对接方案

目录 一、海康威视 【老版本】 【新版本】 二、大华 一、海康威视 【老版本】 URL规定&#xff1a; rtsp://username:password[ipaddress]/[videotype]/ch[number]/[streamtype] 注&#xff1a;VLC可以支持解析URL里的用户名密码&#xff0c;实际发给设备的RTSP请求不支…

STM32设计智能翻译手势识别加算法系统

目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 电路图采用Altium Designer进行设计&#xff1a; 三、实物设计图 四、程序源代码设计 五、获取资料内容 前言 在全球化的浪潮下&#xff0c;语言的多样性也为人们的交流带来了不小的挑战…

基本定时器---内/外部时钟中断

一、定时器的概念 定时器&#xff08;TIM&#xff09;&#xff0c;可以对输入的时钟信号进行计数&#xff0c;并在计数值达到设定值的时候触发中断。 STM32的定时器系统有一个最为重要的结构是时基单元&#xff0c;它由一个16位计数器&#xff0c;预分频器&#xff0c;和自动重…

Qt文件目录操作

文件目录操作相关类 Qt 为文件和目录操作提供了一些类&#xff0c;利用这些类可以方便地实现一些操作。Qt 提供的与文件和目录操作相关的类包括以下几个&#xff1a; QCoreApplication&#xff1a;用于提取应用程序路径&#xff0c;程序名等文件信息&#xff1b;QFile&#x…

.NET 通过模块和驱动收集本地EDR的工具

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

css中的box-sizing,记录

border-box&#xff1a;最终高度为height&#xff0c;默认包含padding border等属性 content-box&#xff1a;box-sizing默认值&#xff0c;最终大小为heightpaddingborder 等

【AI绘画】Alpha-VLLM 的 Lumina-Next:新一代图像生成器

简介 Lumina-Next-T2I 是在 Lumina-T2I 成功基础上发展起来的尖端图像生成模型。它采用了带有 2B 参数模型的 Next-DiT 和 Gemma-2B 文本编码器&#xff0c;推理速度更快&#xff0c;生成样式更丰富&#xff0c;并增强了多语言支持。 模型架构 Lumina-Next-T2I 的生成模型建…