Unity绘制六边形体

现在steam上面有很多下棋类/经营类的游戏都是用六边形的地形,比较美观而且实用,去年在版本末期我也自己尝试做了一个绘制六边体的demo,一年没接触unity竟然都要忘光了,赶紧在这边记录一下。
想cv代码可以直接拉到代码章节

功能

能够动态生成一系列可以“挖空中心”的六边形。指定innerWidth为0也可以生成实心的六边体。
在这里插入图片描述
在这里插入图片描述
能够生成平铺/直铺的六边形群,调整之间距离
在这里插入图片描述在这里插入图片描述在这里插入图片描述

绘制思路

将绘制一个六边形看成六个下面这种等腰体,绕中心旋转60度之后合并成一个。
在这里插入图片描述
在这里插入图片描述
一个这种等腰体又可以看成绘制四个面:上面的等腰梯形,内测的长方形,下面的等腰梯形,外侧的长方形,两边无需绘制,因为合并之后不会显示出来。
所以只需要通过三角函数计算出我们所需的所有点->拼出一个面->合成一个等腰体->合成一个六边体。

组件

我们需要一个MeshFilter来设置mesh,一个MeshRenderer来设置mesh的材质。同时需要对mesh所需的内置成员变量有些了解。
在这里插入图片描述

        m_meshFilter = GetComponent<MeshFilter>();
        m_meshRenderer = GetComponent<MeshRenderer>();

        m_mesh = new Mesh();
        m_mesh.name = "HexMesh";

        m_meshFilter.mesh = m_mesh;
        m_meshRenderer.material = m_material;
		
		//最终数据传入
		m_mesh.vertices = verticles.ToArray();
        m_mesh.triangles = tris.ToArray();
        m_mesh.uv = uvs.ToArray();
        m_mesh.RecalculateNormals();

具体计算

绘制某个点

根据前面需要绘制的等腰梯形,设A是梯形长边的点,B是梯形短边的点,易得平面内某个点的计算方式
在这里插入图片描述
定义一个CreatePoint接口,根据width和y轴高度height来生成某个点的三维向量,(注意unity下生成图中y轴实际上是三维空间的z轴)

  private Vector3 CreatePoint(float distance, float height, float angle)
    {
        float rad = angle * Mathf.Deg2Rad; //Mathf接收的参数需要是弧度制
        return new Vector3(distance * Mathf.Cos(rad), height, distance * Mathf.Sin(rad));
    }

生成面所需的数据

上文提到的等腰体四个不同面实际上都是四个顶点组成的,并且都是两个点组成的平行的线段,所以我们可以提供一个接口,只需指定高度和半径,就可以画出这四种不同的面,同时存在上下和内外两侧面的朝向是相反的,所以提供reverse接口来进行反向。

    /// <summary>
    /// 上下底面的单独一个等腰梯形
    /// </summary>
    /// <param name="innerRad">内径</param>
    /// <param name="outerRad">外径</param>
    /// <param name="heightA">外高</param>
    /// <param name="heightB">内高</param>
    /// <param name="point">顺序</param>
    /// <param name="reverse">连接方向</param>
    /// <returns></returns>
    private Face CreateFace(float innerRad, float outerRad, float heightA, float heightB, int point, bool reverse = false)
    {
        float angle1 = point * 60;
        float angle2 = angle1 + 60;
        if (!isFlat){ //竖着排布,初始角度是-30
            angle1 -= 30;
            angle2 -= 30;
        }
        List<Vector3> verticals = new List<Vector3>();
        //.......C.
        //..B.......
        //..........
        //...A......D
        verticals.Add(CreatePoint(innerRad, heightA, angle1));
        verticals.Add(CreatePoint(innerRad, heightA, angle2));
        verticals.Add(CreatePoint(outerRad, heightB, angle2));
        verticals.Add(CreatePoint(outerRad, heightB, angle1));
        List<int> tris = new List<int> { 0, 1, 2, 2, 3, 0};
        List<Vector2> uv = new List<Vector2> { new Vector2(0, 0),new Vector2(1,0),new Vector2(1,1),new Vector2(0,1) };
        //vertical顺序颠倒,就会按照顺时针绘制。
        if(reverse)
        {
            verticals.Reverse();
        }
        return new Face(verticals, tris, uv);
    }

这里有一些关于mesh的基础知识,首先是三个顶点能够组成一个面,从上往下看如果点之间是逆时针顺序的话,就是面向我们的。这里我们添加了四个点。tirs指定其顺序,每三个一组将会连成一个面,uvs代表是渲染的时候的uv坐标,这里如果六边体有规范的话,就需要根据需求设置对应的uv值,这里就不关注这个了。

      List<int> tris = new List<int> { 0, 1, 2, 2, 3, 0};
       List<Vector2> uv = new List<Vector2> { new Vector2(0, 0),new Vector2(1,0),new Vector2(1,1),new Vector2(0,1) };
public struct Face
{
    //顶点位置数组
    public List<Vector3> verticles { get; private set; }
    //三角形顶点索引数组,按给定的顺序连接顶点,为顺时针三个一组的顺序
    public List<int> triangles { get; private set; }
    public List<Vector2> uvs { get; private set; }

    public Face(List<Vector3> verticles, List<int> triangles, List<Vector2> uvs)
    {
        this.verticles = verticles;
        this.triangles = triangles;
        this.uvs = uvs;
    }
}

这样能够生产出一个面,接下来我们批量生产所需的面,只需要不断让角度偏移60度(忘记了可以去看上面计算A点坐标),重复刚才的步骤,将所有的面的数据都生成

 private void DrawFaces()
    {
        m_faces = new List<Face>();

        //上表面
        for(int point = 0; point < 6; point ++)
        {
            m_faces.Add(CreateFace(innerWidth, outerWidth, height / 2, height / 2, point));
        }
        //下表面
        for (int point = 0; point < 6; point++)
        {
            m_faces.Add(CreateFace(innerWidth, outerWidth,- height / 2, -height / 2, point,true));
        }
        //侧面
        for (int point = 0; point < 6; point++)
        {
            m_faces.Add(CreateFace(outerWidth, outerWidth, height / 2, -height / 2, point));
        }
        //里侧面
        for (int point = 0; point < 6; point++)
        {
            m_faces.Add(CreateFace(innerWidth, innerWidth, height / 2, -height / 2, point,true));
        }
    }

组装

刚才我们将数据填入Face,但是Face是不能直接使用的,我们要将刚才生成的顶点信息,uv信息,三角形信息等一次灌入Mesh中,
Mesh提供了成员变量来接收这些数据。
顶点和uv直接添加就可以,注意三角形数据需要根据顶点数据来加下标。

    private void CombineFaces()
    {
        List<Vector3> verticles = new List<Vector3>();
        List<int> tris = new List<int>();
        List<Vector2> uvs = new List<Vector2>();

        for(int i = 0; i < m_faces.Count; i++)
        {
            verticles.AddRange(m_faces[i].verticles); //AddRange方法可以把list中所有数据从头到尾添加到新的list
            uvs.AddRange(m_faces[i].uvs);

            //注意:这里需要依次指定指定所有顶点在最终mesh的三角形顺序,由于每个face里面包括四个顶点,每次+4
            int offset = (4 * i);
            foreach(int triangle in m_faces[i].triangles)
            {
                tris.Add(triangle + offset);
            }
        }

        m_mesh.vertices = verticles.ToArray();
        m_mesh.triangles = tris.ToArray();
        m_mesh.uv = uvs.ToArray();
        m_mesh.RecalculateNormals();
    }

排布

要让游戏能玩,肯定需要一系列整齐布局的六边形,所以我们需要一个动态创建六边形的管理器。

纵向排布

在这里插入图片描述
前面我们生成面的时候发现有个isFlat变量,这个变量就是控制了第一个面的生成角度,所以横向的时候能保证六边形是横着的。

    private Face CreateFace(float innerRad, float outerRad, float heightA, float heightB, int point, bool reverse = false)
    {
        float angle1 = point * 60;
        float angle2 = angle1 + 60;
        if (!isFlat){ //竖着排布,初始角度是-30
            angle1 -= 30;
            angle2 -= 30;
        }
        ......

问题是如何计算出每个六边形的中心点在哪。这里用三角函数也非常容易看出来
下面是六边体“直立“”情况下,设两个六边形之间间隔为d,六边形中心到外顶点的距离为L
可以发现Y轴方向每个六边形之间距离为(L * cos(30°) * 2 + d)* sin60°
X轴方向每个六边形之间距离为(L*(cos(30°)*2 + d)
同时注意距离偶数行的X轴要添加一个(L * cos(30°) * 2 + d)*sin30°的偏移

具体计算就初中级别的数学,就不一步步画图了
在这里插入图片描述

横向排布

同理横向布局也很好计算
可以发现Y轴方向每个六边形之间距离为(L * cos(30°) * 2 + d)
X轴方向每个六边形之间距离为(L*(cos(30°)*2 + d) *sin60°
同时注意距离偶数行的Y轴要添加一个(L * cos(30°) * 2 + d)*sin30°的偏移

在这里插入图片描述
万事具备,我们只需要计算每一行每列的点即可生成蜂窝了。

    public void SetInterval()
    {
        centerDistance = outterWidth * 2 * Mathf.Sin(60 * Mathf.Deg2Rad) + interval;
    }
	 private void UpdateGrid(GameObject[][] girds)
 {
     if (girds.Length <= 0) return;
     bool shouldOffset = false;
     for (int j = 0; j < heightCount; j++)
     {
         if (!isFlat)
         {
             shouldOffset = j % 2 != 0;
         }
         for (int i = 0; i < widthCount; i++)
         {
             if (isFlat)
             {
                 shouldOffset = i % 2 != 0;
             }
             HexagonRenderer render = girds[i][j].GetComponent<HexagonRenderer>();
             //计算六边形位置
             Vector3 pos = Getpos(i, j, shouldOffset);
             Debug.Log(pos);
             render.SetAtrributes(innerWidth, outterWidth, height, pos, matrial, isFlat);
             render.DrawMesh();
         }
     }
 }

 private Vector3 Getpos(int i, int j, bool shouldOffset)
 {
     float angle60 = 60 * Mathf.Deg2Rad;
     float angle30 = 30 * Mathf.Deg2Rad;
     if (isFlat)
     {
         if (shouldOffset)
         {
             return new Vector3(i * centerDistance * Mathf.Sin(angle60) , transform.position.y, j * centerDistance +centerDistance * Mathf.Sin(angle30));
         }
         else
         {
             return new Vector3(i * centerDistance * Mathf.Sin(angle60), transform.position.y, j * centerDistance);
         }
     }
     else
     {
         if (shouldOffset)
         {
             return new Vector3(i * centerDistance + centerDistance * Mathf.Sin(angle30), transform.position.y, j * centerDistance * Mathf.Sin(angle60));
         }
         else
         {
             return new Vector3(i * centerDistance, transform.position.y, j * centerDistance * Mathf.Sin(angle60));
         }
     }

 }

完整代码

在场景中创建一个空物体,将GenerateMap.cs挂载在其身上即可,将会自动生成一系列身上挂载HexagonRenderer.cs的物体

GenerateMap.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GenerateMap : MonoBehaviour
{
    [Header("Grid Settings")]
    public int widthCount;
    public int heightCount;

    [Header("Layout Settings")]
    public float innerWidth;
    public float outterWidth;
    public float height;
    public bool isFlat;
    public Material matrial;
    /// <summary>
    /// 六边形之间的间隔
    /// </summary>
    public float interval;
    private float centerDistance;


    /// <summary>
    /// 存储所有的六边形
    /// </summary>
    private GameObject[][] girds;
    private bool hasGenerate = false;
    public void Start()
    {
        girds = new GameObject[widthCount][];
        for (int i = 0; i < girds.Length; i++)
        {
            girds[i] = new GameObject[heightCount];
        }
        SetInterval();
        GenerateGrid();
        LayoutGrid();
    }

    public void SetInterval()
    {
        centerDistance = outterWidth * 2 * Mathf.Sin(60 * Mathf.Deg2Rad) + interval;
    }
    /// <summary>
    /// 设置六边形布局,从左下角生成
    /// </summary>
    private void LayoutGrid()
    {
        UpdateGrid(girds);
    }

    private void GenerateGrid()
    {
        if (hasGenerate == true) return;
        for (int j = 0; j < heightCount; j++)
        {
            for (int i = 0; i < widthCount; i++)
            {
                GameObject single = new GameObject($"HEX:({i},{j})", typeof(HexagonRenderer)); //$代表string.format
                girds[i][j] = single;
                single.transform.SetParent(transform, true);
            }
        }
        hasGenerate = true;
    }

    private void UpdateGrid(GameObject[][] girds)
    {
        if (girds.Length <= 0) return;
        bool shouldOffset = false;
        for (int j = 0; j < heightCount; j++)
        {
            if (!isFlat)
            {
                shouldOffset = j % 2 != 0;
            }
            for (int i = 0; i < widthCount; i++)
            {
                if (isFlat)
                {
                    shouldOffset = i % 2 != 0;
                }
                HexagonRenderer render = girds[i][j].GetComponent<HexagonRenderer>();
                //计算六边形位置
                Vector3 pos = Getpos(i, j, shouldOffset);
                Debug.Log(pos);
                render.SetAtrributes(innerWidth, outterWidth, height, pos, matrial, isFlat);
                render.DrawMesh();
            }
        }
    }

    private Vector3 Getpos(int i, int j, bool shouldOffset)
    {
        float angle60 = 60 * Mathf.Deg2Rad;
        float angle30 = 30 * Mathf.Deg2Rad;
        if (isFlat)
        {
            if (shouldOffset)
            {
                return new Vector3(i * centerDistance * Mathf.Sin(angle60) , transform.position.y, j * centerDistance +centerDistance * Mathf.Sin(angle30));
            }
            else
            {
                return new Vector3(i * centerDistance * Mathf.Sin(angle60), transform.position.y, j * centerDistance);
            }
        }
        else
        {
            if (shouldOffset)
            {
                return new Vector3(i * centerDistance + centerDistance * Mathf.Sin(angle30), transform.position.y, j * centerDistance * Mathf.Sin(angle60));
            }
            else
            {
                return new Vector3(i * centerDistance, transform.position.y, j * centerDistance * Mathf.Sin(angle60));
            }
        }

    }
}

HexagonRenderer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public struct Face
{
    //顶点位置数组
    public List<Vector3> verticles { get; private set; }
    //三角形顶点索引数组,按给定的顺序连接顶点,为顺时针三个一组的顺序
    public List<int> triangles { get; private set; }
    public List<Vector2> uvs { get; private set; }

    public Face(List<Vector3> verticles, List<int> triangles, List<Vector2> uvs)
    {
        this.verticles = verticles;
        this.triangles = triangles;
        this.uvs = uvs;
    }
}
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]

public class HexagonRenderer : MonoBehaviour
{
    private Mesh m_mesh;
    private MeshFilter m_meshFilter;
    private MeshRenderer m_meshRenderer;

    private List<Face> m_faces;

    private bool isFlat = true;

    public Material m_material;
    public float innerWidth;
    public float outerWidth;
    public float height;
    private void Awake()
    {
        m_meshFilter = GetComponent<MeshFilter>();
        m_meshRenderer = GetComponent<MeshRenderer>();

        m_mesh = new Mesh();
        m_mesh.name = "HexMesh";

        m_meshFilter.mesh = m_mesh;
        m_meshRenderer.material = m_material;
    }
    public void SetAtrributes(float innerWidth, float outerWidth, float height, Vector3 position, Material material, bool isFlat)
    {
        this.innerWidth = innerWidth;
        this.outerWidth = outerWidth;
        this.isFlat = isFlat;
        this.height = height;
        transform.position = position;
        m_material = material;
        m_meshRenderer.material = m_material;

        DrawMesh();
    }
    private void OnEnable()
    {
        DrawMesh();
    }

    //渲染整个六边形体
    public void DrawMesh()
    {
        DrawFaces();
        CombineFaces();
    }

    private void OnValidate()
    {
    }

    private void DrawFaces()
    {
        m_faces = new List<Face>();

        //上表面
        for (int point = 0; point < 6; point++)
        {
            m_faces.Add(CreateFace(innerWidth, outerWidth, height / 2, height / 2, point));
        }
        //下表面
        for (int point = 0; point < 6; point++)
        {
            m_faces.Add(CreateFace(innerWidth, outerWidth, -height / 2, -height / 2, point, true));
        }
        //侧面
        for (int point = 0; point < 6; point++)
        {
            m_faces.Add(CreateFace(outerWidth, outerWidth, height / 2, -height / 2, point));
        }
        //里侧面
        for (int point = 0; point < 6; point++)
        {
            m_faces.Add(CreateFace(innerWidth, innerWidth, height / 2, -height / 2, point, true));
        }
    }
    private void CombineFaces()
    {
        List<Vector3> verticles = new List<Vector3>();
        List<int> tris = new List<int>();
        List<Vector2> uvs = new List<Vector2>();

        for (int i = 0; i < m_faces.Count; i++)
        {
            verticles.AddRange(m_faces[i].verticles);AddRange方法可以把list中所有数据从头到尾添加到新的list
            uvs.AddRange(m_faces[i].uvs);

            //注意:这里需要依次指定指定所有顶点在最终mesh的三角形顺序,由于每个face里面包括四个顶点,每次+4
            int offset = (4 * i);
            foreach (int triangle in m_faces[i].triangles)
            {
                tris.Add(triangle + offset);
            }
        }

        m_mesh.vertices = verticles.ToArray();
        m_mesh.triangles = tris.ToArray();
        m_mesh.uv = uvs.ToArray();
        m_mesh.RecalculateNormals();
    }
    /// <summary>
    /// 上下底面的单独一个等腰梯形
    /// </summary>
    /// <param name="innerRad">内径</param>
    /// <param name="outerRad">外径</param>
    /// <param name="heightA">外高</param>
    /// <param name="heightB">内高</param>
    /// <param name="point">顺序</param>
    /// <param name="reverse">连接方向</param>
    /// <returns></returns>
    private Face CreateFace(float innerRad, float outerRad, float heightA, float heightB, int point, bool reverse = false)
    {
        float angle1 = point * 60;
        float angle2 = angle1 + 60;
        if (!isFlat)
        {
            angle1 -= 30;
            angle2 -= 30;
        }
        List<Vector3> verticals = new List<Vector3>();
        //.......C.
        //..B.......
        //..........
        //...A......D
        verticals.Add(CreatePoint(innerRad, heightA, angle1));
        verticals.Add(CreatePoint(innerRad, heightA, angle2));
        verticals.Add(CreatePoint(outerRad, heightB, angle2));
        verticals.Add(CreatePoint(outerRad, heightB, angle1));
        List<int> tris = new List<int> { 0, 1, 2, 2, 3, 0 };
        List<Vector2> uv = new List<Vector2> { new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1) };

        if (reverse)
        {
            verticals.Reverse();
        }
        return new Face(verticals, tris, uv);
    }
    /// <summary>
    /// 创造一个顶点
    /// </summary>
    /// <param name="distance">距离坐标原点距离</param>
    /// <param name="height">y轴高度</param>
    /// <param name="angle">和坐标轴所成夹角</param>
    /// <returns></returns>

    private Vector3 CreatePoint(float distance, float height, float angle)
    {
        float rad = angle * Mathf.Deg2Rad;
        return new Vector3(distance * Mathf.Cos(rad), height, distance * Mathf.Sin(rad));
    }
}

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

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

相关文章

go语言魔法技能go:linkname

我们在看Go语言的源码时&#xff0c;经常会看到一些特别的注释&#xff0c;比如&#xff1a; //go:build //go:linkname //go:nosplit //go:noescape //go:uintptrescapes //go:noinline //go:nowritebarrierrec等等&#xff0c;这些特别的注释其实是Go编译器的指示指令。这里…

基于JAVA的毕业设计分配选题系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 专业档案模块2.2 学生选题模块2.3 教师放题模块2.4 选题审核模块 三、系统展示四、核心代码4.1 查询专业4.2 新增专业4.3 选择课题4.4 取消选择课题4.5 审核课题 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpri…

Go语言必知必会100问题-10 小心类型嵌入导致的问题

小心类型嵌入导致的问题 在定义结构体时&#xff0c;Go语言支持通过类型嵌入的形式定义结构体字段。但是&#xff0c;如果我们没有真正理解类型嵌入的意义&#xff0c;有时可能会导致意想不到的行为。本文将主要分析如何嵌入类型&#xff0c;类型嵌入的作用以及可能出现的问题…

lv20 QT对话框3

1 内置对话框 标准对话框样式 内置对话框基类 QColorDialog, QErrorMessage QFileDialog QFontDialog QInputDialog QMessageBox QProgressDialogQDialog Class帮助文档 示例&#xff1a;各按钮激发对话框实现基类提供的各效果 第一步&#xff1a;实现组件布局&…

Redis 之三:发布订阅(pub/sub)

概念介绍 Redis 发布订阅 (pub/sub) 是一种消息通信模式&#xff0c;它允许客户端之间进行异步的消息传递 Redis 客户端可以订阅任意数量的频道。 模型中的角色 在该模型中&#xff0c;有三种角色&#xff1a; 发布者&#xff08;Publisher&#xff09;&#xff1a;负责发送信…

【Hudi】核心概念

https://www.bilibili.com/video/BV1ue4y1i7na?p17&vd_sourcefa36a95b3c3fa4f32dd400f8cabddeaf 大数据新风口&#xff1a;Hudi数据湖&#xff08;尚硅谷&Apache Hudi联合出品&#xff09; 1 基础概念 1.1 时间轴(TimeLine) 1.2 文件布局(File Layout) 1.3 索引(In…

Unity-PDF分割器(iTextSharp)

PDF分割器 Unity-PDF分割器前言核心思路解决过程一、Unity安装iTextSharp二、运行时计算将要生成文件的大小三、分割核心代码四、使用StandaloneFileBrowser五、其他的一些脚本六、游戏界面主体的构建MainWindowWarningPanel & FinishPanel By-Round Moon Unity-PDF分割器 …

浅谈 Linux 网络编程 socket

文章目录 socket 介绍 socket 介绍 socket 被翻译成 网络套接字&#xff0c;这个名字实在是不好理解&#xff0c;我更愿意称为"插槽"。 忽略 socket 的中文名&#xff0c;先无脑记住两个规则&#xff1a; ① 记住&#xff0c;一个文件描述符(fd) 指向一个 socket&…

同芯.共赢 | 暴雨服务器亮相AMD EPYC合作伙伴峰会

2月29日&#xff0c;AMD EPYC合作伙伴峰会活动在北京成功举行&#xff0c;暴雨作为AMD重要生态合作伙伴应邀参加。作为AMD开年首场活动&#xff0c;此次活动意义非凡&#xff0c;AMD在现场向合作伙伴分享了AMD数据中心全新产品路线、解决方案以及生态建设领域的最新进展。 AMD是…

android开发平台,Java+性能优化+APP开发+NDK+跨平台技术

开头 通常作为一个Android APP开发者&#xff0c;我们并不关心Android的源代码实现&#xff0c;不过随着Android开发者越来越多&#xff0c;企业在筛选Android程序员时越来越看中一个程序员对于Android底层的理解和思考&#xff0c;这里的底层主要就是Android Framewok中各个组…

机器学习专项课程03:Unsupervised Learning, Recommenders, Reinforcement Learning笔记 Week02

Week 02 of Unsupervised Learning, Recommenders, Reinforcement Learning 课程地址&#xff1a; https://www.coursera.org/learn/unsupervised-learning-recommenders-reinforcement-learning 本笔记包含字幕&#xff0c;quiz的答案以及作业的代码&#xff0c;仅供个人学习…

二分查找讲解

关于我为什么要写单独开一篇文章写二分,实际上那么多困难的算法,比如线段树,并查集等等都没有难倒我,我最近却被二分难倒了,而且是两次,两次在赛场上做不出来二分的应用题,于是我决定写一篇二分查找的算法总结.刚接触算法的时候本来是要写一篇的,但后面因为各种原因搁置了,现在…

R语言数学建模(二)—— tidymodels

R语言数学建模&#xff08;二&#xff09;—— tidymodels 文章目录 R语言数学建模&#xff08;二&#xff09;—— tidymodels前言一、示例数据集二、拆分数据集2.1 拆分数据集的常用方法2.2 验证集2.3 多层次数据2.4 其他需考虑问题 三、parsnip用于拟合模型3.1 创建模型3.2 …

腾讯云优惠券领取的三个渠道,先领券再下单!

腾讯云代金券领取渠道有哪些&#xff1f;腾讯云官网可以领取、官方媒体账号可以领取代金券、完成任务可以领取代金券&#xff0c;大家也可以在腾讯云百科蹲守代金券&#xff0c;因为腾讯云代金券领取渠道比较分散&#xff0c;腾讯云百科txybk.com专注汇总优惠代金券领取页面&am…

禁止safari浏览器网页双击缩放功能

普通浏览器 普通浏览器&#xff0c;只需要增加meta标签禁止缩放功能就行了 <meta content"widthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalable0;" name"viewport" /> user-scalableno或0 //禁止双指缩放页面initial-scale1.0…

企业文件图纸加密有哪些?图纸文件加密防泄密软件如何选?

在现在的市场发展中&#xff0c;对于企业的图纸文件安全问题越来越重视&#xff0c;如设计图纸&#xff0c;重要文件等&#xff0c;一旦泄漏就会给企业造成巨大的经济损失。所以对企业管理者来讲&#xff0c;如何才能选择一款好用的适合本企业的图纸文件加密软件是非常重要的&a…

【C++】手把手教你手搓模拟实现string类

前言 string类一直都是C的经典问题&#xff0c;之前的文章已经对string类做了一个基本的介绍&#xff08;string类的基本常用接口&#xff09;&#xff0c;为了更好理解string类的功能&#xff0c;此篇文章将手把手教你带你手搓模拟实现string类&#xff0c;快来一起学习吧&am…

Spring 事务传播机制

事务传播机制&#xff1a;多个事务⽅法存在调⽤关系时, 事务是如何在这些⽅法间进⾏传播的。 ⽐如&#xff1a;有两个⽅法A&#xff0c;B都被 Transactional 修饰,&#xff0c;A⽅法调⽤B⽅法 A⽅法运⾏时, 会开启⼀个事务。当A调⽤B时&#xff0c; B⽅法本⾝也有事务&#xf…

Pod 进阶

目录 资源限制 健康检查 资源限制 当定义 Pod 时可以选择性地为每个容器设定所需要的资源数量。 最常见的可设定资源是 CPU 和内存大小&#xff0c;以及其他类型的资源。 当为 Pod 中的容器指定了 request 资源时&#xff0c;代表容器运行所需的最小资源量&#xff0c;调度器…

打造去中心化透明储蓄罐:Solidity智能合约的又一实践

一、案例背景 传统的储蓄罐通常是由个人或家庭使用&#xff0c;用于存放硬币或小额纸币。然而&#xff0c;这样的储蓄罐缺乏透明性&#xff0c;用户无法实时了解储蓄情况&#xff0c;也无法确保资金的安全性。 通过Solidity智能合约&#xff0c;我们可以构建一个去中心化…