Unity 2D 新手练习整理 FrogJump 上

这篇具有很好参考价值的文章主要介绍了Unity 2D 新手练习整理 FrogJump 上。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概要

第一个Unity demo 记录一下,一个2D休闲过关小游戏
跟着这个up主操作的

https://www.bilibili.com/video/BV1k64y1N7MV/?spm_id_from=333.337.search-card.all.click&vd_source=c861e132f3eaa49aa9a51955095f5a4e
相关软件版本 Tuanjie Editor 1.0.4

1 资源收集

1.1 Unity商店找使用的资源

https://assetstore.unity.com/zh-CN

1.2 下载的资源包括

图片资源–> Pixel Adventure 1
音效资源–>FREE Casual Game SFX Pack
BGM–>Casual Game BGM #5
字体–>https://www.font.im/english 谷歌字体 搜索Press Start 2P,进行下载

1.3 资源导入

下载完成后在Unity的Package Manager 窗口导入资源,让资源成功在项目的Assets中显示,以备使用;字体文件下载后解压,将 ttf 格式文件拖拽到项目的Assets层级下,以备使用,可进行分类创建font文件夹存放

资源如下:
Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

2 开始结束页面

2.1 创建开始结束页面

Project窗口Scenes文件夹中,将scene重命名为start,并且右键创建一个新场景scene,命名为end,两个页面为开始页面和结束页面

Project窗口是管理项目资源的地方,其中Scenes文件夹用于存放场景文件这里可以添加技术细节

2.2 开始页面添加文字,图片及按钮

  • 在start场景,右侧Hierarchy层级窗口右键创建一个UI-Legacy-Text Button Image,创建时为了清晰功能可以进行相应的命名
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

  • 创建好后,在主屏幕Scene窗口移动创建的几个元素,点击Scene 鼠标滑轮比例缩小看到全貌

  • 点击几个元素,点击+W快捷键->移动 点击+R快捷键->调整大小 分别进行移动到合适位置及合适的大小
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

  • 在下方Project窗口中选取下载的Pixel素材,找一个适合开始页面的图片,点击Image,将选好的图片拖拽到Image元素的Inspector窗口的Source Image处,让Image显示选中的图片,可以修改width和height再次调整比例大小
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

  • 点击Text进行编辑,在Inspector窗口可以修改展示的文本内容、字体颜色、大小、位置等,将下载好的字体文件,拖拽到Font属性上进行应用

  • Button上的文字可以同样进行设置
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

  • 右键创建一个panel控件,用于管理页面上的UI元素,可以设置panel的背景颜色,将Image、Button、Text三个编辑过的元素拖到panel上,让他们受控制于panel,此时,移动panel上面的元素会一起移动
    应该先创建panel,在panel上创建其他节点的
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

2.3 在按钮上绑定启动关卡脚本

  • 给Button上增加一个脚本,使其在点击时实现相应的功能,可以再Project窗口个下新建一个script文件夹,专门放脚本文件,在文件夹中创建C#脚本,命名为NextLevel 并打开文件编辑一个nextLevel方法
 public void nextLevel()
 {
     SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
 }

获取当前激活的场景(Active Scene)的构建索引(buildIndex),然后将其加1,接着加载新的场景。

  • 脚本编辑完保存退出,两个拖拽:将脚本拖拽到Button对象上,点击Button让其显示Inspector页面,在Button的onClick处点击加号添加条件,将Button拖拽到为none的对象选择处。后面的Nofunction选择刚刚创建的方法nextLevel
    在Insepector窗口on Click再次绑定Button才能选择function
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

2.4 添加背景BGM

在Hierarchy层级窗口创建一个空对象 Create Empty 可以命名为BGM,右侧Inspector窗口为其添加组件Audio Source;在Project窗口将准备好的BGM音频拖到Audio Source的AudioClip上,volume可以调节音量
Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
提示:此时 简单的开始游戏页面制作完成,执行效果为有背景音效的开始页面,有按钮可以进行点击,此时点击会报错,因为只有一个页面跳转失败,是正常的
Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

2.5 完善结束页面

  • 在Project窗口点击end场景进入并编辑,可以在Hierarchy窗口对start场景的节点进行全部复制并粘贴到end场景,适当修改实现区分及不同功能的实现,复制一个button节点,一个按钮实现重新开始,一个按钮实现退出游戏
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
  • 将end场景增加到project中:File----Build Setting----Add Open Scenes 进行添加
    添加完成后,两个场景的编号分别是0和1
    每增加一个场景 都要add一下,并可以拖动上下的顺序,及时add及时修改顺序
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
  • 修改NextLevel脚本,增加重启和退出的实现方法
public void reLoad()
{
    SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex - 1);
}

public void quit()
{
    Application.Quit();
}

减一是由场景个数决定,quit方法在unity窗口没有明显反应

  • 修改两个按钮在Inspector窗口onclick中function实现的方法为脚本中对应方法
  • 此时开始页面和结束页面可实现相互跳转

3 创建地形

3.1 创建level1场景

  • Project窗口右键新建一个scene命名为level1,并进入编辑

  • 在Hierarchy窗口右键新建两个2DObject----Tilemap----Rectangular节点,一个命名为BackGroud,点击Tilemap会有一个调色板窗口open tile palette,点开这个窗口
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

3.2 创建Tilemap图层设置平台地图

  • 将palette窗口打开,并拖到屏幕的合适位置
  • 点击palette窗口No Valid Palette创建一个新的palette
  • 在Project窗口找到下载好的图片素材 找到土地的资源Terrain下的Terrain Sliced,在Inspector窗口修改这个图片的尺寸Pixels Per Unit为16,修改大小后保存,拖到palette上如图
  • Hierarchy窗口点击Tilemap节点,使用palette窗口的paint选取背景涂到Tilemap上,为了方便区分,可以先选择Background节点Inspector窗口将Tilemap Renderer的对号勾选掉,这样主屏幕就只是Tilemap节点的画面
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

3.3 创建背景板background

  • 找到background素材,选择喜欢的颜色,点击,图片的尺寸Pixels Per Unit为16,将改好大小的图片拖到new palette页面
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
  • Tilemap节点Inspector窗口将Tilemap Renderer的对号勾选掉,使其不显示(临时操作,后面会恢复)
  • Hierarchy窗口点击Background节点,使用palette窗口的paint选取背景涂到Background上

3.4 图层设置

  • 设置图层:在Background的Inspector窗口Tilemap Renderer,点击sorting layer,默认是Default,点击创建一个图层Add Sorting Layer

  • 点击进去,点击加号,会多一行Layer1 可以进行命名,此处命名为Background,并拖到最上面
    最上面的在最底层显示Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

  • 此时Background节点的sorting layer也设置为Background

  • 最后将Tilemap节点Inspector窗口将Tilemap Renderer的对号勾选上(恢复之前的临时操作),此时Tilemap图层在Background图层的上面显示,效果如图Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

  • 最后最后,将新建的level1场景添加到Project:File—Build Setting—Add Open Scenes,并将它移动到end上,此时试运行就会进入到level1的场景
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

4 主角闪亮登场

4.1 创建主角动画人物

  • Hierarchy窗口右键新建一个2D Object–Sprite–Square,并命名为Player
  • 在Project窗口个Pixel素材中找到主角人物图片
  • 点击Player,将主角图片拖动到Player的Inspector窗口的sprite属性栏中,使其展示
  • 调整Player位置大小Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
  • 设置Tilemap使角色能够站在设置的路线上,点击Tilemap打开他的Inspector,Layer处点击
  • 新建一个ground层,并将Tilemap设置为ground
  • 设置同一图层的优先级:点击Player,在Inspector窗口设置Order in Layer为1,,此时他和Tilemap在一个图层,此数值越大越在上面优先展示
  • 给人物和地面添加碰撞体,实现角色站立在地面上
  • 点击Player,在Inspector检查窗口的最下面添加component:Box Collider 2D、Rigidbody 2D
    Collide 碰撞体检测物体之间的碰撞情况 Box形状为盒形;Rigidbody 2D刚体组件物理模拟功能
  • 点击Tilemap,添加组件Tilemap Collider 2D、Composite Collider 2D
    Tilemap Collider 2D管理和处理瓦片地图的碰撞检测
    Composite Collider 2D复合碰撞器,自动合并相邻碰撞器的范围 添加他刚体自动添加
  • ilemap Collider 2D组件选中Used By Composite,将该Collider 2D组件纳入一个Composite Collider 2D组件的管理范围内;
  • Rigidbody 2D选中Body Type为Static,让Tilemap静止状态
    ilemap Collider 2D组件选中Used By Composite:他会自动连接所有瓦片资源更方便,刚体Static意为让Tilemap节点静止在画面上,否则Tilemap会在掉落
  • Player的 Rigidbody 2D组件中,将Constrains 中Freeze Rotation Z选中 锁定Z轴,在操作中,人物就不会在Z轴旋转,只能XY轴移动
    Tilemap的碰撞体是盒型,不锁定状态人物可进行垂直面移动,不符合游戏设置,为了更合理锁定Z轴

4.2 主角动画效果添加

  • 在Project窗口右键新建Create一个Animation并命名为move

  • 将move动画拖拽到Hierarchy的Player节点上

  • 双击move进行编辑动画,窗口打开后,还需点一下Player进行激活,后才能操作

  • 将Idle空闲状态图片全选拖拽到move中
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

  • 将Samples调整到20,动作的频率会变慢一点,move动画在Inspector窗口中的loop打开让他动作循环

  • 同样的方法创建run角色跑的动画 jump角色跳的动画 appear和desappear出现和消失的动画,只有run和move是循环的,其他不用循环看情况而定

  • 将创建的所有动画移动到新建文件夹中,注意点,动画循环要勾选,动画要拖到角色身上
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

  • 创建一个障碍物,当角色掉落时碰到障碍物执行death动画,在Hierarchy窗口右键新建一个2D Object-Sprites-Square命名为deadline,选择一个图片拖动到sprite属性处展示,调整大小到最下面,以备接住掉落的角色

  • 给deadline加上碰撞体组件Box Collider 2DUnity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

4.3 新建人物移动脚本

  • 在Project窗口script文件夹下右键新建C#脚本,命名为Camera
  • 编写脚本,将编辑好的脚本保存退出,将Camera脚本拖拽到Hierarchy的Main Camera上
 [SerializeField] private Transform player;
 void Update()
 {
     transform.position = new Vector3(player.position.x, player.position.y, transform.position.z);
 }

[SerializeField],用于在Unity的Inspector窗口中显示私有变量,可以直接设置和查看这个私有变量的值;
Update 方法会在每一帧被调用,用于更新游戏逻辑;
Transform 是Unity中用于表示游戏对象位置、旋转和缩放的类
当前游戏对象跟随定义的player变量的位置变动而变动
注意 z轴为对象本身的z轴,不是player的z轴,只有xy轴随着player的变化而变化 z轴不动

  • 点击Main Camera 在Inspector检查窗口点击拖上去的脚本,将Hierarchy的Player节点拖拽到Main Camera 在Inspector检查窗口脚本的Player处,使实现Main Camera跟随Player移动
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
  • 在Project窗口script文件夹下右键新建C#脚本,命名为Movement,并拖拽到Play节点上,使其生效
  • 编写脚本
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Movement : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 7f;
    [SerializeField] private float jumpForce = 7f;
    [SerializeField] private LayerMask jumpGround;
    private Rigidbody2D rb;
    private Animator anim;
    private BoxCollider2D coll;
    private SpriteRenderer sprite;
    private float dirX = 0f;

    private enum MovementState { move, run, jump, fall }
    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
        coll = GetComponent<BoxCollider2D>();
        sprite = GetComponent<SpriteRenderer>();

    }

    // Update is called once per frame
    void Update()
    {
        dirX = Input.GetAxisRaw("Horizontal");
        rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y);
        if (Input.GetButtonDown("Jump") && IsGround())
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpForce);

        }
        UpdateAnimationState();

    }

    private void UpdateAnimationState()
    {
        MovementState state;
        if(dirX > 0f)
        {
            state = MovementState.run;
            sprite.flipX = false;
        }
        else if(dirX < 0f)
        {
            state = MovementState.run;
            sprite.flipX = true;
        }
        else
        {
            state = MovementState.move;
        }
        if(rb.velocity .y > .1f)
        {
            state = MovementState.jump;
        }
        if (rb.velocity.y < -.1f)
        {
            state = MovementState.fall;
        }
        anim.SetInteger("state", (int)state);
    }

    private bool IsGround()
    {
        return Physics2D.BoxCast(coll.bounds.center, coll.bounds.size, 0f, Vector2.down, .1f, jumpGround);
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.name == "deadline")
        {
            anim.SetTrigger("death");
        }
    }

    private void RestartLevel()
    {
        anim.SetTrigger("appear");
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    }
}

显示三个变量 :moveSpeed移动速度、jumpForce跳跃高度、JumpGround跳跃的平台
MovementState 枚举值为四种状态 注意索引是从0开始的
SpriteRenderer对象的flipX方法 镜像对象,使其向左向右都会有动作,控制sprite是否在水平方向上翻转
update方法中,获取用户移动和跳跃键的意图,并将对象进行移动
UpdateAnimationState 更新对象状态的判断,判断结果通过animator的setInteger方法,给state赋值
rb.velocity .y > .1f 判断是否在垂直方向上以大于0.1单位每秒的速度向上或向下移动

4.4 动作转换

  • 点击之前创建动画是生成的Animator Controller,调整各个动作的转换
    避雷:AnimationEvent has no function name specified!AnimationEven存在动画事件没有指定函数名! 解决办法:找到动画中添加的多余空AnimationEvent,删除掉即可。
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
  • 创建三个Parameters参数,一个是int数值类型的state,用于各个状态转换的标记,两个trigger触发器类型的death和appear,触发则执行相应动画
  • 在Any State处右击Make Transition 连接到appear和disappear,其他连接都使用右键Make
  • 过渡线的Inspector页面的Has Exit Time都不勾选(为true时只有当动画播放结束才会过渡到下一个)
  • 下面的Fixed Duration勾选,固定秒数节省时间,Transition Duration可以都设置为0
    Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
  • 动作间的转换,条件一定要准确,此处的state数值和代码中顺序要对应,才会执行成功

4.5 执行

Hierarchy窗口点击Player节点,Inspector窗口展示了脚本中暴露的可修改参数,可以在此修改移动速度和跳跃高度,选择跳跃的地面,选择ground(因为是Tilemap在的图层)
Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎
此时执行demo,效果为角色可以执行简单的移动,死亡后会起点重生
Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎Unity 2D 新手练习整理 FrogJump 上,unity,unity,游戏引擎

5 移动机关

6 设置奖励及主角收集

7 设置陷阱

8 检查点

9 场景跳跃

10 项目导出运行

报错总结

1 给Player节点加了刚体和碰撞体,人物还是掉落,掉落时会被Tilemap遮挡

解决:当只给Player加上碰撞体,而没有其他碰撞体,Play也相当于没有碰撞体,没有可以发生交互的物体;被Tilemap遮挡说明在同一图层或在他之下,修改order in Layer 的数值让其在上面

2 给Tilemap节点加了刚体和碰撞体组件,但是人物和地面一起下落

解决:加了碰撞体但没有进行相应的设置,要在刚体处设置为静止状态static,不设置默认为动态,碰撞体交给composite一起管理

3 挂载上相机,但运行画面为全蓝色

解决:代码中的位置只有x、y的数值随着Play的移动而移动,z轴不动

4 加上运动脚本设置好Animation的过渡后,人物运动不连贯或不按照设想的运动

解决:连接线的条件判断错误,代码中第一位的下标为0,所以连接线这边要从0开始,以此类推,条件要准确无误才会成功

5 人物行动至Tilemap的边缘会沿着边缘移动

解决:因为Tilemap设置的为盒型碰撞体,有z轴人物运动至此会沿着z轴旋转,可以在人物处设置刚体的锁定z轴,让人物无法在Z轴移动

6 人物无法实现跳跃

解决:只给人物挂载了脚本,脚本中暴露的地面参数,要进行选择后才能在地面上跳动

第一次接触Unity,记录一下。 如有明显错误,欢迎指正!文章来源地址https://www.toymoban.com/news/detail-845766.html

到了这里,关于Unity 2D 新手练习整理 FrogJump 上的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • UNITY—2D游戏制作入门!

    Unity作为当今最流行的游戏引擎之一,受到各大厂商的喜爱。 像是炉石传说,以及逃离塔克夫,都是由unity引擎开发制作。 作为初学者的我们,虽然无法直接做出完成度那么高的作品,但每一个伟大的目标,都有一个微不足道的开始。让我们从一个2D小游戏入手,来学习Unit

    2024年02月04日
    浏览(41)
  • unity-2D游戏地面检测 三射线检测

        2D游戏中跳跃是不可或缺的功能,要实现跳跃功能,就必须进行地面检测!常规方法是使用一根往角色下方延伸的射线检测,但是这种方法在一些复杂不规则的地面效果通常不尽人意。通过增加射线数量,即可完善这种方法的不足,达到在复杂地面也能正确检测角色是否

    2024年02月15日
    浏览(26)
  • Unity之创建第一个2D游戏项目

    一 Unity环境配置 1.1 Untity资源官网下载:https://unity.cn/releases 1.2 Unity Hub集成环境,包含工具和项目的管理 1.3 Unity Editor编辑器 1.4 Visual Studio 2022脚本编辑器 1.5 AndroidSKD,JDK,NDK工具,用于android环境的运行 二 创建Unity项目 2.1 新建2D模板项目 2.2 新建2D物体 2.3 新建C#脚本文件 

    2024年02月04日
    浏览(35)
  • ​Unity Vuforia 新手(图片识别)教程,后续整理 实体识别 详细流程

    最近有个Holoens2 识别灭火器实体交互的项目,大概有2-3年没有搞MR的项目了,重新看一下,以前没有记录的习惯,现在慢慢培养一下。 小Dome链接: link 图片识别 新手教程链接: link 识别物体 新手教程链接: link unity Holoens2开发使用Vuforia 新手教程链接: link 运行官方 Vuforia Hololen

    2024年02月04日
    浏览(36)
  • 用unity写一个2D类的拼图游戏

    前几天接了一个拼图项目刚好现在写完了,拿出来分享,拼图不难,我也是看了一个官方案例写的,因为当我们写图片跟随鼠标的时候,鼠标已经有一个图片了,这个图片会遮挡射线,然后就无法判断当前拼图块在哪里,话不多说,上菜 1、新建总控脚本LevelManager 2、新建Pu

    2024年02月08日
    浏览(34)
  • Unity:2D游戏设置相机orthographicSize动态设置

    目录 根据设备分辨率动态设置相机 orthographicSize 2d游戏里面相机的Orthan.size确定的是高度,宽度是按照屏幕的宽高比计算出来的 cameraWidthSize = camera.Orthographic.size*(Screen.Width/Screen.height) 我在游戏里设置的 开发分辨率是1080*1920 所以我在原先Y=1920情况下 Camera设置的orthographicSize=

    2024年01月25日
    浏览(38)
  • 【Unity】2D平台游戏初中级教程-笔记补充

    由于代码真的是太多,又被作者重构多次,各位可以根据下面的链接和文章有需自取。 链接地址 油管主( 作者 ):Barden 原视频连接 https://www.youtube.com/watch?v=Pux1GlFwKPslist=PLy78FINcVmjA0zDBhLuLNL1Jo6xNMMq-Windex=1 原视频简介: Discord Server: https://discord.gg/uHQrf7K Assets: https://drive.google.co

    2024年02月11日
    浏览(32)
  • Unity 2D横版闯关游戏 (JUNGLE RULES)

    目录 游戏演示 项目内容 (1) 项目内容 项目分析  (1) 游戏策划 (2) 游戏美术 (3) 游戏程序  项目实现 (1) 游戏角色(Player) (2) 小怪(Enemy) (3) UI界面 (4) 摄像机(Camera) (5) 公告板(Billboard) (6) 游戏场景(Scene) 项目源码​​​​​​​                     阿里云盘分享 https://www.aliyu

    2023年04月08日
    浏览(38)
  • Unity2D绘制游戏地图

    首先,我们选择我们的地图素材(由于笔者在前段时间已经进行切割,最近才开始整理这一段,所以这个是用的老素材,可能有朋友就会觉得切割前后不一致,但是思路是一样的,大家学习思路即可)  接着,我们找到右上角的Slice进行图片切割,我这边简单介绍一下这个切

    2024年02月08日
    浏览(36)
  • Unity实现2D游戏跟随摄像机(平滑移动)

    摄像机运行效果如下所示。 首先创建一个可用的玩家角色,写好移动逻辑,如果要使用在Unity商店中购买的资源,可以点击Window菜单栏 Package Manager选项,来打开Package Manager窗口,如下所示,然后下载你需要的资源。 如果你没有在资源商店的精灵资产,可以在Hierarchy视图,右

    2024年02月19日
    浏览(38)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包