Unity 随机 生成地形 (PerlinNoise 柏林噪声)

这篇具有很好参考价值的文章主要介绍了Unity 随机 生成地形 (PerlinNoise 柏林噪声)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

程序化地图生成

地图显示模块

三种不同的地图格式数据生成:
	DrawNoiseMap() 噪声地图
	DrawTexture() 色彩地图
	DrawMesh() 网格地图
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 地图显示
/// 将噪声贴图转换为纹理 
/// </summary>
public class MapDisplay_ZH : MonoBehaviour
{
    [Header("纹理渲染")]
    public Renderer _TextureRender;
    [Header("网格数据")]
    public MeshFilter _MeshFilter;
    [Header("网格渲染")]
    public MeshRenderer _MeshRender;

    /// <summary>
    /// 噪声地图绘制
    /// </summary>
    /// <param 柏林噪声="_NoiseMap"></param>
    public void DrawNoiseMap(float[,] _NoiseMap)
    {
        //地图宽度
        int _Width = _NoiseMap.GetLength(0);
        //地图高度
        int _Height = _NoiseMap.GetLength(1);

        //根据传入值  确定渲染纹理长宽
        Texture2D _Texture = new Texture2D(_Width, _Height);

        //获取噪波数组中的所有值
        Color[] _ColourMap = new Color[_Width * _Height];


        //设置每个像素点的颜色
        for (int y = 0; y < _Width; y++)
        {
            for (int x = 0; x < _Height; x++)
            {
                //色彩取样
                // Color 是一维数组 噪声是二维数组
                //获取当前像素点所在位置 : Y值 乘 地图宽度 再加上 X  就是当前像素在噪声地图中的位置
                //当前取样点的颜色 
                //由于只想要 黑白色域  所以使用 Color.black 和 Color.white
                _ColourMap[y * _Width + x] = Color.Lerp(Color.black, Color.white, _NoiseMap[x, y]);
            }
        }
        //色彩传递
        //纹理贴图赋值
        _Texture.SetPixels(_ColourMap);
        _Texture.Apply();

        //主纹理贴图赋值
        _TextureRender.sharedMaterial.mainTexture = _Texture;
        //地图大小赋值
        _TextureRender.transform.localScale = new Vector3(_Width, 1, _Height);
    }

    /// <summary>
    /// Texture 地图绘制
    /// </summary>
    /// <param 图像="_Texture"></param>
    public void DrawTexture(Texture2D _Texture)
    {
        //渲染贴图赋予
        _TextureRender.sharedMaterial.mainTexture = _Texture;
        //渲染物体大小设置
        _TextureRender.transform.localScale = new Vector3(_Texture.width, 1, _Texture.height);
    }
    /// <summary>
    /// 网格地形绘制
    /// </summary>
    /// <param 地形网格="_MeshData"></param>
    /// <param 地形贴图="_Texture2D"></param>
    internal void DrawMesh(MeshData _MeshData, Texture2D _Texture2D)
    {
        _MeshFilter.sharedMesh = _MeshData.CreteMesh();

        _MeshRender.sharedMaterial.mainTexture = _Texture2D;
    }
}

柏林噪声地图模块

这个模块为基础模块:
	返回值在0.01.0之间。(返回值可能略低于0.0或超过1.0)
	关键性代码: Mathf.PerlinNoise()
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 柏林噪声地图生成
/// </summary>
public static class Noise_ZH
{

    /// <summary>
    /// 柏林噪声地图生成方法
    /// </summary>
    /// <param 地图宽度="_MapWidth"></param>
    /// <param 地图高度="_MapHeight"></param>
    /// <param 地图大小="_Scale"></param>
    /// <param 种子="_Seed"></param>
    /// <param 抵消="_Octaves"></param>
    /// <param 持续="_Persistance"></param>
    /// <param 空隙="_Lacunarity"></param>
    /// <param 偏移抵消="_Offset"></param>/// 
    /// <returns></returns>
    public static float[,] GenerateNoiseMap(int _MapWidth, int _MapHeight, float _Scale, int _Seed, int _Octaves, float _Persistance, float _Lacunarity, Vector2 _Offset)
    {
        //噪声地图
        float[,] _NoiseMap = new float[_MapWidth, _MapHeight];

        //地图种子随机
        System.Random _Prng = new System.Random(_Seed);

        //抵消
        Vector2[] _OctaveOffsets = new Vector2[_Octaves];
        for (int i = 0; i < _Octaves; i++)
        {
            float _OffsetX = _Prng.Next(-10000, 10000) + _Offset.x;
            float _OffsetY = _Prng.Next(-10000, 10000) + _Offset.y;
            _OctaveOffsets[i] = new Vector2(_OffsetX, _OffsetY);
        }

        //避免地图  不存在
        if (_Scale <= 0)
        {
            _Scale = 0.0001f;
        }

        //地图放大响应
        float _HalfWidth = _MapWidth / 2;
        float _HalfHeight = _MapHeight / 2;


        //最大噪声高度
        float _MaxNoiseHeight = float.MinValue;
        //最小噪声高度
        float _MinNoiseHeight = float.MaxValue;


        for (int y = 0; y < _MapHeight; y++)
        {
            for (int x = 0; x < _MapWidth; x++)
            {
                //振幅
                float _Amplitude = 1;
                //频率
                float _Frequency = 1;
                //噪波高度
                float _NoiseHeight = 0;

                for (int i = 0; i < _Octaves; i++)
                {

                    //地图单元取整
                    float _SampleX = (x - _HalfWidth) / _Scale * _Frequency + _OctaveOffsets[i].x;
                    float _SampleY = (y - _HalfHeight) / _Scale * _Frequency + _OctaveOffsets[i].y;

                    //根据传入参数 生成2D柏林噪声
                    float _PerlinValue = Mathf.PerlinNoise(_SampleX, _SampleY) * 2 - 1;
                    //噪波高度等于 柏林噪声 乘于 振幅
                    _NoiseHeight += _PerlinValue * _Amplitude;

                    //振幅变更
                    _Amplitude *= _Persistance;
                    //频率变更
                    _Frequency *= _Lacunarity;
                }

                //限值操作
                if (_NoiseHeight > _MaxNoiseHeight)
                {
                    _MaxNoiseHeight = _NoiseHeight;
                }
                else if (_NoiseHeight < _MinNoiseHeight)
                {
                    _MinNoiseHeight = _NoiseHeight;
                }

                //传入地图数据
                _NoiseMap[x, y] = _NoiseHeight;
            }
        }

        //噪声贴图 最大值最小值 限定输出
        //图像叠加
        for (int y = 0; y < _MapHeight; y++)
        {
            for (int x = 0; x < _MapWidth; x++)
            {
                _NoiseMap[x, y] = Mathf.InverseLerp(_MinNoiseHeight, _MaxNoiseHeight, _NoiseMap[x, y]);
            }
        }

        //返回地图数据
        return _NoiseMap;
    }

}

柏林噪声效果显示

unity 自动地形,Unity,unity,游戏引擎,图形渲染

彩色地图模块

色彩地图模块:
TextureFromColourMap() 纹理生成模块
TextureFromHeightMap() 高度生成模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 纹理生成器
/// </summary>
public static class TextureGenerator_ZH
{
    /// <summary>
    /// 颜色地图的纹理
    /// </summary>
    /// <param 色彩地图="_ColourMap"></param>
    /// <param 宽度="_Width"></param>
    /// <param 高度="_Height"></param>
    /// <returns></returns>
    public static Texture2D TextureFromColourMap(Color[] _ColourMap, int _Width, int _Height)
    {
        Texture2D _Texture = new Texture2D(_Width, _Height);

        //绘制模式 变更
        //点过滤-纹理像素变得块状近距离
        //边缘块状显示
        //_Texture.filterMode = FilterMode.Point;
        _Texture.filterMode = FilterMode.Point;

        //纹理坐标缠绕模式
        //将纹理夹紧到边缘的最后一个像素
        _Texture.wrapMode = TextureWrapMode.Clamp;

        //色彩传递
        //纹理贴图赋值
        _Texture.SetPixels(_ColourMap);

        //必须要应用一下不然不响应
        _Texture.Apply();

        return _Texture;
    }

    /// <summary>
    /// 高度贴图纹理
    /// </summary>
    /// <param 高度贴图="_HeightMap"></param>
    /// <returns></returns>
    public static Texture2D TextureFromHeightMap(float[,] _HeightMap)
    {
        //地图宽度
        int _Width = _HeightMap.GetLength(0);
        //地图高度
        int _Height = _HeightMap.GetLength(1);

        根据传入值  确定渲染纹理长宽
        //Texture2D _Texture = new Texture2D(_Width, _Height);

        //获取噪波数组中的所有值
        Color[] _ColourMap = new Color[_Width * _Height];


        //设置每个像素点的颜色
        for (int y = 0; y < _Width; y++)
        {
            for (int x = 0; x < _Height; x++)
            {
                //色彩取样
                // Color 是一维数组 噪声是二维数组
                //获取当前像素点所在位置 : Y值 乘 地图宽度 再加上 X  就是当前像素在噪声地图中的位置
                //当前取样点的颜色 
                //由于只想要 黑白色域  所以使用 Color.black 和 Color.white
                _ColourMap[y * _Width + x] = Color.Lerp(Color.black, Color.white, _HeightMap[x, y]);
            }
        }
        色彩传递
        纹理贴图赋值
        //_Texture.SetPixels(_ColourMap);
        //_Texture.Apply();


        return TextureFromColourMap(_ColourMap, _Width, _Height);
    }
}

色彩地图效果显示

unity 自动地形,Unity,unity,游戏引擎,图形渲染

基础网格生成规则

当三角网格数据绘制顺序为逆时针时:
123134  法线方向为朝上。
当三角网格数据绘制顺序为顺时针时:
143132  法线方向为朝下

unity 自动地形,Unity,unity,游戏引擎,图形渲染

法线方向:法线(normal line),是指始终垂直于某平面的直线。在几何学中,法线指平面上垂直于曲线在某点的切线的一条线。法线也应用于光学的平面镜反射上。
如果法线方向背对视角方向,那么该网格三角面将不可视。

unity 自动地形,Unity,unity,游戏引擎,图形渲染

网格地图模块

网格地图模块:
	MeshData 网格类
		AddTrianle()为关键性方法
		进行三角形网格绘制逻辑
		CreteMesh() 
		网格数据填充方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 网格生成器
/// </summary>
public static class MeshGenerator_ZH
{
    /// <summary>
    /// 地形网格生成
    /// </summary>
    /// <param 高度地图="_HeightMap"></param>
    public static MeshData GenerateTerrainMesh(float[,] _HeightMap)
    {
        //宽度
        int _Width = _HeightMap.GetLength(0);
        //高度
        int _Height = _HeightMap.GetLength(1);

        //网格划分
        float _TopLeftX = (_Width - 1) / -2f;
        float _TopLeftZ = (_Height - 1) / -2f;

        //网格数据
        MeshData _MeshData = new MeshData(_Width, _Height);

        //三角形 绘制序号
        int _VertexIndex = 0;

        //网格数据填充
        for (int y = 0; y < _Height; y++)
        {
            for (int x = 0; x < _Width; x++)
            {
                //顶点数据填充
                _MeshData._Vertices[_VertexIndex] = new Vector3(_TopLeftX + x, _HeightMap[x, y], _TopLeftZ + y);
                //UV 数据填充
                _MeshData._UVs[_VertexIndex] = new Vector2(x / (float)_Width, y / (float)_Height);

                //剔除 行 列 最边缘 的顶点  不计入网格渲染计算
                if (x < _Width - 1 && y < _Height - 1)
                {
                    _MeshData.AddTrianle(_VertexIndex, _VertexIndex + _Width + 1, _VertexIndex + _Width);
                    _MeshData.AddTrianle(_VertexIndex + _Width + 1, _VertexIndex, _VertexIndex + 1);
                }
                //序号增加
                _VertexIndex++;
            }
        }

        return _MeshData;
    }
}

/// <summary>
/// 网格数据
/// </summary>
public class MeshData
{
    //顶点数据
    public Vector3[] _Vertices;
    //绘制序列
    public int[] _Triangles;

    //UV 数据
    public Vector2[] _UVs;

     //三角 序号
    int _TriangleIndex;

    public MeshData(int _MeshWidth,int _MeshHeight)
    {
        //网格顶点数据
        _Vertices = new Vector3[_MeshWidth * _MeshHeight];

        //UV 数据
        _UVs = new Vector2[_MeshWidth * _MeshHeight];

        //三角形绘制序列
        _Triangles = new int[(_MeshWidth - 1) * (_MeshHeight - 1) * 6];
    }
    /// <summary>
    /// 网格三角形绘制
    /// </summary>
    /// <param 顶点A="_A"></param>
    /// <param 顶点B="_B"></param>
    /// <param 顶点C="_C"></param>
    public void AddTrianle(int _A,int _B,int _C)
    {
        //例如:
        // 1 2 3
        // 4 5 6
        // 7 8 9
        // 绘制 逻辑为  124 245 235 356 457 578 568 689  右手定则  逆时针 法线方向朝上
        //注意 绘制的的方向要统一   统一顺时针绘制  或者逆时针绘制
        _Triangles[_TriangleIndex] = _C;
        _Triangles[_TriangleIndex+1] = _B;
        _Triangles[_TriangleIndex+2] = _A;
        _TriangleIndex += 3;
    }

    /// <summary>
    /// 新建网格
    /// </summary>
    /// <returns></returns>
    public Mesh CreteMesh()
    {
        //网格数据
        Mesh _Mesh = new Mesh();
        //顶点数据赋予
        _Mesh.vertices = _Vertices;
        //三角形序列赋予
        _Mesh.triangles = _Triangles;
        //UV 数据 赋予
        _Mesh.uv = _UVs;
        //使用默认法线
        _Mesh.RecalculateNormals();
        //数据返回
        return _Mesh;
    }
}

网格地图效果显示

unity 自动地形,Unity,unity,游戏引擎,图形渲染

unity 自动地形,Unity,unity,游戏引擎,图形渲染

程序化地图管理模块

用作管理地图模块化生成
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
///  地图管理
/// </summary>
public class MapGenertor_ZH : MonoBehaviour
{
    /// <summary>
    /// 地图显示类型枚举
    /// </summary>
    public enum DrawMap { NoiseMap, ColourMap, MeshMap }

    [Header("地图绘制类型")]
    public DrawMap _DrawMap = DrawMap.NoiseMap;

    [Header("地图宽度")]
    public int _MapWidth;
    [Header("地图高度")]
    public int _MapHeight;
    [Header("地图大小")]
    public float _NoiseScale;

    [Header("八度")]
    public int _Octaves;
    [Header("持续")]
    [Range(0, 1)]
    public float _Persistance;
    [Header("空隙")]
    public float _Lacunarity;
    [Header("地图种子")]
    public int _Seed;
    [Header("偏移")]
    public Vector2 _Offset;

    [Header("地形区域")]
    public TerrainType[] _Regions;


    [Header("自动更新布尔")]
    public bool _AutoUpdate;

    /// <summary>
    /// 地图生成 调用
    /// </summary>
    public void GenerateMap()
    {
        //参数传递 
        float[,] _NoiseMap = Noise_ZH.GenerateNoiseMap(_MapWidth, _MapHeight, _NoiseScale, _Seed, _Octaves, _Persistance, _Lacunarity, _Offset);

        //色彩地图
        Color[] _ColourMap = new Color[_MapWidth * _MapHeight];

        //根据地形高度进行色彩赋予
        for (int y = 0; y < _MapHeight; y++)
        {
            for (int x = 0; x < _MapWidth; x++)
            {
                //获取当前地图高度值
                float _CurrentHeight = _NoiseMap[x, y];
                //进行地形区域判定
                for (int i = 0; i < _Regions.Length; i++)
                {
                    if (_CurrentHeight <= _Regions[i]._Height)
                    {
                        //色彩赋值
                        _ColourMap[y * _MapWidth + x] = _Regions[i]._Colour;
                        break;
                    }
                }

            }
        }

        //查找 MapDisplay_ZH 搭载物体
        MapDisplay_ZH _Display = FindObjectOfType<MapDisplay_ZH>();

        //根据不同类型进行不同地图绘制
        if (_DrawMap == DrawMap.NoiseMap)
        {
            //进行柏林噪声地形绘制
            _Display.DrawTexture(TextureGenerator_ZH.TextureFromHeightMap(_NoiseMap));
        }
        else if (_DrawMap == DrawMap.ColourMap)
        {
            //进行色彩贴图地形绘制
            _Display.DrawTexture(TextureGenerator_ZH.TextureFromColourMap(_ColourMap, _MapWidth, _MapHeight));
        }
        else if (_DrawMap == DrawMap.MeshMap)
        {
            //进行网格地形绘制
            _Display.DrawMesh(MeshGenerator_ZH.GenerateTerrainMesh(_NoiseMap), TextureGenerator_ZH.TextureFromColourMap(_ColourMap, _MapWidth, _MapHeight));
        }

    }

    /// <summary>
    /// 当脚本被加载或检查器在值被修改时调用此函数时(仅在编辑器调用中)
    /// </summary>
    private void OnValidate()
    {
        //保证初始化显示
        if (_MapWidth < 1)
        {
            _MapWidth = 1;
        }
        if (_MapHeight < 1)
        {
            _MapHeight = 1;
        }
        if (_Lacunarity < 1)
        {
            _Lacunarity = 1;
        }
        if (_Octaves < 0)
        {
            _Octaves = 0;
        }
    }
}

//序列化
[System.Serializable]
/// <summary>
/// 地形类型
/// </summary>
public struct TerrainType
{
    //名称
    public string _Name;
    //高度
    public float _Height;
    //颜色
    public Color _Colour;
}


地图管理 菜单编辑

需要在 根目录创建一个 Editor 文件夹并把代码文件放进去才能执行
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
/// <summary>
/// 菜单编辑
/// </summary>
[CustomEditor(typeof(MapGenertor_ZH))]
public class MapGeneratorEditor_ZH : Editor
{
    public override void OnInspectorGUI()
    {
        MapGenertor_ZH _MapGen = (MapGenertor_ZH)target;

        //绘制内置检查器
        //如果发现变更
        if (DrawDefaultInspector())
        {
            //如果更新布尔为 Ture  就持续更新地图
            if (_MapGen._AutoUpdate)
            {
                _MapGen.GenerateMap();
            }
        }

        //在 MapGenertor_ZH 上创建更新按钮
        if (GUILayout.Button("Generate"))
        {
            _MapGen.GenerateMap();
        }
    }
}

代码搭载

unity 自动地形,Unity,unity,游戏引擎,图形渲染
unity 自动地形,Unity,unity,游戏引擎,图形渲染

unity 自动地形,Unity,unity,游戏引擎,图形渲染

效果展示

根据地图种子的值来进行地图随机变换

unity 自动地形,Unity,unity,游戏引擎,图形渲染

unity 自动地形,Unity,unity,游戏引擎,图形渲染
unity 自动地形,Unity,unity,游戏引擎,图形渲染
unity 自动地形,Unity,unity,游戏引擎,图形渲染

暂时先这样吧,如果有时间的话就会更新,实在看不明白就留言,看到我会回复的。
路漫漫其修远兮,与君共勉。文章来源地址https://www.toymoban.com/news/detail-617030.html

到了这里,关于Unity 随机 生成地形 (PerlinNoise 柏林噪声)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 柏林噪声分形&幻想大陆地图生成

    之前介绍过perlin噪声的实现,现在应用实践一下——程序化生成幻想大陆 这里使用的是perlin噪声倍频技术(也称分形噪声),详情传送门:柏林噪声算法 代码示例使用的是shadertoy的语法规则,shandertoy传送门:ShaderToy 地形轮廓的生成主要依靠噪声,来看倍频相关代码(for迭代

    2024年02月07日
    浏览(44)
  • Unity地形动态生成的一些经验记录

            正经人谁写日记啊                                                 --汪涵         我以前也一直认为一个正经忙碌的人哪有时间和精力去写日记呢?         但是我错了,正因为忙绿没有精力,才要将重要的事情记录下来.         我记录的

    2024年02月09日
    浏览(38)
  • 【简单详细】Unity生成地形和UI小地图实时展示人物位置的详细制作过程

    效果: 注意:一个 父物体 Map是一个纯白色image,放到左上角,要加 Mask组件 哦!!!       它的 两个子物体, 其中一个(map)是显示出地图纹理的image,另外一个(mapPlayer)是一个小点,我们也用image来表示,也就是效果图上的 绿点 。     我们把人物放进去,挂载脚本

    2024年02月05日
    浏览(49)
  • Unity--随机生成游戏对象

    在脚本中声明数组 RandomObjects 用于保存生成对象的类型,在project文件中拖入对象。 先将脚本拖到一个对象上,然后点击检查器-覆盖-应用到全部,这样将使所有预制件都拥有该属性。

    2024年02月15日
    浏览(42)
  • unity如何制作随机生成器

    随机生成器的制作至少应具备四个个要素:需要随机生成的物体、物体随机生成的间隔时间、每生成一次的物体个数、一定时间后销毁物体。 其中最关键的问题是如何间隔一定时间生成,这里提供两种思路:1.运用协程。2.做个计时器。 在这里我们需要初步了解一下协程。

    2023年04月09日
    浏览(58)
  • Unity如何生成随机数(设置种子)

    我们可以使用Random类来生成一些随机数 Random类是用于生成随机数的类之一。它可以用于生成不同类型的随机数,如整数、浮点数和向量。 我们可以使用Random.Range来生成指定范围内的随机整数或浮点数。下面举两个例子: 运行结果如下所示: 使用Random.insideUnitCircle来获取一个

    2024年02月04日
    浏览(40)
  • Unity 中的随机数的基础常用的随机数生成方法

    在 Unity 中,可以使用 Random 类来生成随机数。以下是一些常用的随机数生成方法: Random.Range(min, max):生成一个在[min, max)范围内的随机整数。 Random.value:生成一个在[0, 1)范围内的随机浮点数。 Random.insideUnitCircle:生成一个在单位圆内的随机二维向量。 Random.insideUnitSphere:生成

    2024年02月20日
    浏览(55)
  • 在Unity中简单地制作一张2D地图并随机生成资源

    这个作业属于哪个课程 软件工程2022年春-F班 这个作业要求在哪里 软件工程实践总结个人技术博客 这个作业的目标 课程回顾、个人技术总结 其他参考文献 《构建之法》 1.1 技术使用场景   你不满足于别人提供的现成的地图,想要利用原有的素材来将你自己的想法实现吗?

    2023年04月08日
    浏览(47)
  • 【unity实战】随机地下城生成4——小地图的制作(2d,3d通用)(含源码)

    参考原视频链接: 【视频】:https://space.bilibili.com/370283072 注意 :本文为学习笔记记录,推荐支持原作者,去看原视频自己手敲代码理解更加深入 新建一个摄像机,用来查看小地图,我们分个图层只能新建的摄像机才可以拍摄到 如果对摄像机和渲染纹理还不理解的,强烈推

    2023年04月21日
    浏览(48)
  • 【unity实战】随机地下城生成2——绘制地图Tilemap的使用及一些技巧的使用(含源码)

    参考原视频链接: 【视频】:https://space.bilibili.com/370283072 注意 :本文为学习笔记记录,推荐支持原作者,去看原视频自己手敲代码理解更加深入 修改素材配置 切割图片 绘制瓦片地图 先新建我们的调色盘,保存好位置 拖入我们刚才切片好的素材 在房间预设体创建我们的瓦

    2024年02月13日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包