unity贪吃蛇 笔记

(利用UGUI)

格子每格30

box collider2d不识别ugui的大小,需要自己设置size

游戏逻辑–实现思路

蛇头

  • 移动
    • (每隔一段时间invoke repeat)(突变Transform)
    • (差值loop、movetowards)
  • 碰撞食物
  • 传送
    • 蛇头
  • 死亡
    • 碰撞边界
    • 碰撞自己身体

蛇身

  • 移动
    • (继承前一个的位置;从后往前挪)(双色蛇身:奇偶性赋值图片)
    • (尾巴 移到蛇头后一个)
  • 增加

食物

  • 随机生成
  • 销毁

设置

固定分辨率:

file——build setting——player setting——resolution

取消勾选default full screen;设置为1280 x 720

Display resolution dialog选择disabled

公司名称:company name

导入资源:

project面板右键——import package——custom package添加unitypackage类型资源——import

  • Scenes场景
  • Scripts脚本
  • audio音频
  • effects特效
  • fonts字体
  • sprites图片
    • background
    • icon(道具)
    • snakepart(蛇)
    • UI

开始场景

canvas

  • bg(image)
    • 背景点缀图片(image)
  • controlPanel(image)
    • 皮肤(text)(Toggle group)
      • Toggle1蓝色
        • background
          • checkmark
          • 小蛇–蛇头蛇身组合(image)
        • label:科技小蛇
      • Toggle2黄色
        • background
          • checkmark
          • 小蛇–蛇头蛇身组合(image)
        • label:小黄人蛇
    • 模式(text)(Toggle group)
      • Toggle1
        • background
          • checkmark
        • label:边界模式(outline)
      • Toggle2
        • background
          • checkmark
        • label:自由模式(outline)
    • 分数(text)
      • 上次:长度0,分数0(text)
      • 最好:长度0,分数0(text)
  • title(text)(shadow)
  • go(image)(Button组件)(outline)(shadow)
    • 开始(text)

canvas:

render mode——screen space camera(多用于2d游戏)

(overlay:多用于3d,ui一直显示在最上层,hud抬头显示)

设置render camera为主摄像机main camera

**背景图片:**image

填充:anchor设置alt下最右下角

设置source image为背景图片

**左边栏:**image

设置source image为unity自带的background图片

color:透明度a为120

左侧填充:anchor设置alt下最左下角

**标题:**text

贪吃蛇

居中

选择字体

anchor设置 中上

**开始按钮:**image(Button组件)(outline)(shadow)

设置source image为go

set native size

左边栏控制:

皮肤、模式、分数:

居中、斜体、字体

anchor设置 中上

*分数:*text

anchor设置 中上

*模式:*创建Toggle

label——边界模式、自由模式

background——anchor设置 左中

checkmark——anchor设置alt下最右下角

模式添加Toggle group,Toggle设置Toggle group

皮肤:创建Toggle

background——anchor设置alt下最右下角

checkmark——anchor设置 最左上角

label——anchor设置alt 最右上角——科技小蛇、

background下面添加image小蛇

每个隔着35

(面板上 位置 可以写计算式子)

游戏场景

复制开始场景,进行修改

canvas

  • bg
    • 边界 上下左右(image)(box collider 2d)
  • panel
    • 模式
    • 得分
    • 长度
    • 首页按钮底图(image)(Button)
      • 首页图片(image)
    • 暂停按钮底图(image)(Button)
      • 暂停图片(image)

**模式:**无尽模式

设计的时候按照最大的来

得分、长度:(第一行)

0(第两行)

按钮:

底部图片设置为unity自带的UIsprite

子物体设置图片

**边界:**空物体

anchor设置 中上、下、左、右

添加collider、设置size

添加image、图片为自带的background、颜色红色

蛇头

image

设置图片

大小45

制作为预制体Prefab

添加脚本SnakeHead

》SnakeHead

自行移动(格子)

键盘控制移动

蛇头朝向变化

不能反向移动

按空格加速移动

public float velocity=0.35f;//移动的间隔时间
public int step;//一步走多少
private int x;//x坐标 增量
private int y;//y坐标 增量
private Vector3 headPos;//头部坐标

void Start()
{
    InvokeRepeating("Move",0,velocity);//重复调用(方法名字,执行此句后多久开始调用,隔多久调用一次)
    //最初的移动方向(向上)
    x=0;
    y=step;
}

void Update()
{
    if(Input.GetKeyDown(KeyCode.Space))//按空格 加速移动
    {
        CancelInvoke();//必须先结束重复调用 再修改参数
        InvokeRepeating("Move",0,velocity-0.2f);//缩短移动一次的间隔时间=加速移动
    }
    if(Input.GetKeyUp(KeyCode.Space))//抬起空格 恢复移动速度
    {
        CancelInvoke();
        InvokeRepeating("Move",0,velocity);
    }
    
    
    //Input.GetKey监测键盘按下一次(GetKeyDown是一直按住)
    if(Input.GetKey(KeyCode.W) && y!=-step)//上up(按下w键  蛇头不在向下走)
    {
        //改变蛇头的朝向
        gameObject.transform.localRotation=Quaternion.Euler(0,0,0);//四元数Quaternion.Euler() 3个浮点数(对应unity面板里的rotation的xyz)
        //改变移动的方向
        x=0;
        y=step;
    }
    if(Input.GetKey(KeyCode.S) && y!=step)//下down(按下S键  蛇头不在向上走)
    {
        gameObject.transform.localRotation=Quaternion.Euler(0,0,180);
        x=0;
        y=-step;
    }
    if(Input.GetKey(KeyCode.A) && x!=step)//左left(按下A键  蛇头不在向右走)
    {
        gameObject.transform.localRotation=Quaternion.Euler(0,0,90);
        x=-step;
        y=0;
    }
    if(Input.GetKey(KeyCode.D) && x!=-step)//右right(按下D键  蛇头不在向左走)
    {
        gameObject.transform.localRotation=Quaternion.Euler(0,0,-90);
        x=step;
        y=0;
    }
}

void Move()//蛇头移动
{
    headPos = gameObject.transform.localPosition;
    //UGUI下的rectTransform是Transform的子类(直接用Transform,相当于返回rectTransform)
    //(不能直接点出rectTransform)(可能需要getcomponent.rectTransform?)
    //canvas有缩放值,ugui的坐标与世界坐标可能有差异》使用本地坐标localPosition
    gameObject.transform.localPosition=new Vector3(headPos.x+x,headPos.y+y,headPos.z);//改变位置
}

食物

**食物预制体:**foodPrefab

image

大小40

制作为预制体Prefab

canvas下的空对象 foodHolder

食物的父物体

填充:anchor设置alt下最右下角

canvas外面的空物体 ScriptsHolder

添加脚本FoodMaker

设置foodPrefab食物预制体

设置foodSprites食物图片数组

》FoodMaker

随机生成食物(位置 在格子内)

设置食物的父物体

using UnityEngine.UI;

public int xlimit=21;//横向的格子数量
public int ylimit=11;//纵向的格子数量
public int xoffset=7;//横向额外的格子(左边栏)
public GameObject foodPrefab;//食物预制体
public Sprite[] foodSprites;//食物图片数组
private Transform foodHolder;//食物父物体 位置

void Start()
{
    foodHolder=GameObject.Find("foodHolder").transform;//查找游戏物体,获取物体的位置
    MakeFood();//游戏开始 生成一个食物
}


public void MakeFood()//随机生成
{
    //随机图片
    int index=Random.Range(0,foodSprites.Length);//生成随机数Random.Range(包含0,不包含 数组的长度)
    GameObject food=Instantiate(foodPrefab);//克隆预制体
    food.GetComponent<Image>().sprite=foodSprites[index];//需要引入命名空间
    
    //设置父物体
    food.transform.SetParent(foodHolder,false);//设置父对象(,是否保存世界坐标)
    //预制体实例化在canvas外边,设置父物体后自动随着canvas缩放改变 false不进行缩放
    
    
    //随机(格子数)位置
    int x=Random.Range(-xlimit+xoffset,xlimit);
    int y=Random.Range(-ylimit,ylimit);
    food.transform.localPosition=new Vector3(x*30,y*30,0);//设置位置 每格30
}

蛇头 吃 食物

蛇头:

添加碰撞器box collider2d

设置size为28

(碰撞器要比30格子小,等于30会监测到相邻的情况)

勾选is trigger

(能检测到碰撞,不会被挡住)

(一般2个中的1个勾选?)

添加刚体rigidbody

gravity设为0

(2个物体碰撞,只需要1个物体有刚体)

蛇身:

添加碰撞器box collider2d

设置size为28

设置tag为food

add tag——food

》SnakeHead

private void OnTriggerEnter2D(Collider2D collision)//进入触发检测
{
    if(collision.tag=="Food")
    //也可以写成if (collision.gameObject.CompareTag("Food"))
    {
        Destroy(collision.gameObject);//销毁(碰撞器。游戏物体)
        FoodMaker.Instance.MakeFood();//生成新食物(利用单例)
    }      
}

》FoodMaker

单例

private static FoodMaker _instance;//静态 可以用类名访问,不用对象了
public static FoodMaker Instance//外部可以访问的成员
{
    get//可以访问,不能修改
    {
        return _instance;
    }       
}

//赋值
void Awake()
{
    _instance=this;
}

蛇身

image

大小40

添加box collider2d

设置size为28

制作为预制体Prefab

》SnakeHead

奇偶数选择图片

碰撞食物后,增加蛇身

蛇身移动

(赋值public变量)

using UnityEngine.UI;

//不能Transform转rectTransform??
//教程public List<RectTransform> bodyList=new List<RectTransform>();
//修改后
public List<Transform> bodyList=new List<Transform>();//身体
public GameObject bodyPrefab;//蛇身预制体
public Sprite[] bodySprites=new Sprite[2];//蛇身图片 指定长度2
private Transform canvas;//canvas位置

void Awake()
{
    canvas=GameObject.Find("Canvas").transform;//找到游戏物体
}

void Grow()
{
    //身体奇偶数 决定图片
    int index=(bodyList.Count%2==0)?0:1;//三元表达式 
    GameObject body=Instantiate(bodyPrefab);//克隆蛇身预制体
    body.GetComponent<Image>().sprite=bodySpprites[index];//设置图片
    
    //设置父物体
    body.transform.SetParent(canvas,false);//设置在canvas下,不保存世界坐标
    
    //蛇身列表 增加 位置    
    bodyList.Add(body.transform);    
}

蛇身 移动方式 一(不适用)

最后一个移动到头后面(原来的头的位置)

花纹会变化,不符合需求

》SnakeHead

先生成在外边

using ;//List.Last需要引入命名空间
    
void Move()
{
    //... 
    if(bodyList.Count>0)
    {
        bodyList.Last().localPosition=headPos;//最后一个的位置 变为原来头的位置
    bodyList.Insert(0,bodyList.Last());//列表最开始 插入原来的最后一个
    bodyList.RemoveAt(bodyList.Count-1);//移出原来最后的一个
    } 
}

void Grow()
{
    int index=(bodyList.Count%2==0)?0:1; 
    GameObject body=Instantiate(bodyPrefab,new Vector3(2000,2000,0),Quaternion.identity);//克隆(对象,位置,旋转四元数)
    body.GetComponent<Image>().sprite=bodySpprites[index];
    body.transform.SetParent(canvas,false)    
    bodyList.Add(body.transform);
}

private void OnTriggerEnter2D(Collider2D collision)
{
    if(collision.tag=="Food")
    {
        Destroy(collision.gameObject);
        Grow();//吃完食物后,增加蛇身
        FoodMaker.Instance.MakeFood();
    }      
}

蛇身 移动方式 二

后一个移动到前一个位置

从后往前挪动

》SnakeHead

void Move()
{
    //...
    if(bodyList.Count>0)
    {
        for(int i=bodyList.Count-2;i>=0;i--)
        {
            bodyList[i+1].localPosition=bodyList[i].localPosition;//后一个身体 变成原来前一个的位置
        }
        bodyList[0].localPosition=headPos;//第一个蛇身的位置 是原来蛇头的位置
    }
}

void Grow()
{
    int index=(bodyList.Count%2==0)?0:1; 
    GameObject body=Instantiate(bodyPrefab,new Vector3(2000,2000,0),Quaternion.identity);//克隆(对象,位置,旋转四元数)
    body.GetComponent<Image>().sprite=bodySpprites[index];
    body.transform.SetParent(canvas,false)    
    bodyList.Add(body.transform);
}

private void OnTriggerEnter2D(Collider2D collision)
{
    if(collision.tag=="Food")
    {
        Destroy(collision.gameObject);
        Grow();//吃完食物后,增加蛇身
        FoodMaker.Instance.MakeFood();
    }      
}

碰撞身体、边界

身体

预制体 添加tag为body

》SnakeHead

private void OnTriggerEnter2D(Collider2D collision)
{
    //...
    else if(collision.tag=="Body")//身体
    {
        Debug.Log("die");
    }
    else//边界
    {
        Debug.Log("die");//边界模式
    }
}

边界传送

private void OnTriggerEnter2D(Collider2D collision)
{
    //...
    else//边界
    {
        //自由模式(传送)
        switch(collision.gameObject.name)//四周的边框 名称
        {
             case "Up"://上
                transform.localPosition=new Vector3(transform.localPosition.x,-transform.localPosition.y+30,transform.localPosition.z);
                break;
             case "Down"://下
                transform.localPosition=new Vector3(transform.localPosition.x,-transform.localPosition.y-30,transform.localPosition.z);
                break;
             case "Left"://左
                transform.localPosition=new Vector3(-transform.localPosition.x+180,transform.localPosition.y,transform.localPosition.z);
                break;   
             case "Right"://右
                transform.localPosition=new Vector3(-transform.localPosition.x+240,transform.localPosition.y,transform.localPosition.z);
                break;   
        }
    }
}

奖励

image

设置图片——大小30

添加box collider2d——设置size为28

设置tag为Reward

制作为预制体Prefab

》FoodMaker

在ScriptsHolder上赋值rewardPrefab

isReward判断是否生成额外奖励

public GameObject rewardPrefab;//奖励预制体

void Start()
{
    //...
    MakeFood(false);//第一次生成,不生成奖励
}

public void MakeFood(bool isReward)
{
    //...
    if(isReward)
    {
    	GameObject reward=Instantiate(rewardPrefab);
    	reward.transform.SetParent(foodHolder,false);
    	x=Random.Range(-xlimit+xoffset,xlimit);
    	y=Random.Range(-ylimit,ylimit);
    	reward.transform.localPosition=new Vector3(x*30,y*30,0);
    }
}

》SnakeHead

随机概率,生成额外奖励

检测碰撞

private void OnTriggerEnter2D(Collider2D collision)
{
    if(collision.tag=="Food")
    {
        Destroy(collision.gameObject);
        Grow();
        //20%的概率 生成食物和奖励;否则只生成食物
        FoodMaker.Instance.MakeFood((Random.Range(0,100)<20)?true:false);
    }
    else if(collision.tag=="Reward")
    {
        Destroy(collision.gameObject);
        Grow();
    }
    //...
}

分数、长度、阶段等级

创建脚本

ScriptsHolder挂载脚本

赋值public变量

》MainUIController

单例

更新UI

背景颜色切换

using UnityEngine.UI;

//单例
private static MainUIController _instance;
public static MainUIController Instance
{
    get
    {
        return _instance;
    }       
}    
    
public int score=0;//分数
public int length=0;//长度
public Text msgText;
public Text scoreText;
public Text lengthText;
public Image bgImage;//背景图片
private Color tempColor;//存储颜色

//单例赋值
void Awake()
{
    _instance=this;
}

void Update()
{
    //初始文字为 阶段1
    switch(score/100)
    {
        case 0:
        case 1:
        case 2:
            break;
        case 3:
        case 4:
            ColorUtility.TryParseHtmlString("#CCEEFFFF",out tempColor);//16进制颜色(字符,输出) 蓝色
            bgImage.color=tempColor;//更改背景颜色
            msgText.text="阶段"+2;
            break;
        case 5:
        case 6:
            ColorUtility.TryParseHtmlString("#CCFFDBFF",out tempColor);//绿色
            bgImage.color=tempColor;
            msgText.text="阶段"+3;
            break;
        case 7:
        case 8:
            ColorUtility.TryParseHtmlString("#EBFFCCFF",out tempColor);//黄色
            bgImage.color=tempColor;
            msgText.text="阶段"+4;
            break;  
        case 9:
        case 10:
            ColorUtility.TryParseHtmlString("#FFF3CCFF",out tempColor);//橙色
            bgImage.color=tempColor;
            msgText.text="阶段"+5;
            break;
        default:
            ColorUtility.TryParseHtmlString("#FFDACCFF",out tempColor);//红色
            bgImage.color=tempColor;
            msgText.text="无尽阶段";
            break;
    }
}

public void UpdateUI(int s=5,int l=1)//更新UI
{
    score+=s;
    length+=l;
    
    scoreText.text="得分:\n"+score;//  换行符\n
    lengthText.text="长度:\n"+length;
}

》SnakeHead

更新得分、长度

private void OnTriggerEnter2D(Collider2D collision)
{
    if(collision.tag=="Food")
    {
        Destroy(collision.gameObject);
        MainUIController.Instance.UpdateUI();//普通食物的得分
        Grow();
        FoodMaker.Instance.MakeFood((Random.Range(0,100)<20)?true:false);
    }
    else if(collision.tag=="Reward")
    {
        Destroy(collision.gameObject);
        MainUIController.Instance.UpdateUI(Random.Range(5,15)*10);//奖励的得分 随机
        Grow();
    }
    //...
}

暂停、返回菜单

暂停

在暂停按钮物体上的Button组件,设置click事件

为ScriptsHolder的pause方法

》MainUIController

暂停Time.timeScale

图标改变

public bool isPause;
public Image pauseImage;//暂停图片对象
public Sprite[] pauseSprites;//图片

public void Pause()
{
    isPause=!isPause;//按下按钮,状态取反
    if(isPause)//暂停
    {
        Time.timeScale=0;//时间冻结
        pauseImage.sprite=pauseSprites[1];   
    }
    else
    {
        Time.timeScale=1;//时间进行
        pauseImage.sprite=pauseSprites[0];
    }
}

默认空格键暂停,与加速移动冲突

》edit——project setting——input manger——Submit——删除space

蛇头移动

》SnakeHead

void Update()
{
    if(Input.GetKeyDown(KeyCode.Space) && MainUIController.Instance.isPause==false)
    {}
    ...
    ...
    //所有按键 if条件 都增加不能是暂停状态
}

主页按钮

file——build setting——add Scene增加场景

0开始场景

1游戏场景

绑定Button的click事件

》MainUIController

public void Home()
{
    UnityEngine.SceneManagement.SceneManager.LoadScene(0);//加载场景
}

死亡、记录得分

》SnakeHead

协程 停止后再继续

停止移动

死亡特效

回到起始状态

//using System.Collections;//协程的命名空间

private bool isDie=false;
public GameObject dieEffect;

void Update()
{
    if(Input.GetKeyDown(KeyCode.Space) && MainUIController.Instance.isPause==false && isDie==false)
    {}
    ...
    ...
    //所有按键 if条件 都增加不能是死亡状态
}

void Die()
{
    CancelInvoke();//停止移动
    isDie=true;
    Instantiate(dieEffect);//死亡特效
    
    //记录得分
    PlayerPrefs.SetInt("lastl",MainUIController.Instance.length);//记录得分 (键,值)
    PlayerPrefs.SetInt("lasts",MainUIController.Instance.score);
    //最佳得分
    if(PlayerPrefs.GetInt("bests",0)<MainUIController.Instance.score)//获取(键,不存在的话默认值)
    {
        PlayerPrefs.SetInt("bestl",MainUIController.Instance.length);
    	PlayerPrefs.SetInt("bests",MainUIController.Instance.score);
    }
    
    StartCoroutine(GameOver(1.5f));//调用协程
}

//协程 等待一定时间再继续
IEnumerator GameOver(float t)
{
    yield return new WaitForSeconds(t);
    UnityEngine.SceneManagement.SceneManager.LoadScene(1);//加载游戏场景
}

//碰撞身体后调用die
private void OnTriggerEnter2D(Collider2D collision)
{
    //...
    else if(collision.tag=="Body")//身体
    {
        Die();
    }
    else//边界
    {
        Die();//边界模式 碰撞边界死亡
    }
}

用户存储

开始场景

创建空物体ScriptsHolder

挂载脚本StartUIController

设置public参数

》StartUIController

选项卡数据

绑定Toggle的Value Changed事件

用动态方法,bool参数

开始游戏按钮,跳转游戏场景

绑定开始Button的click事件

//using UnityEngine.UI;

public Text lastText;
public Text bestText;

public Toggle blue;
public Toggle yellow;
public Toggle border;
public Toggle noBorder;

//获取记录得分
void Awake()
{
    lastText.text="上次:长度 "+PlayerPrefs.GetInt("lastl",0)+",分数 "+PlayerPrefs.GetInt("lasts",0);
    bestText.text="最佳:长度 "+PlayerPrefs.GetInt("bestl",0)+",分数 "+PlayerPrefs.GetInt("bests",0);
}

//获取设置
void Start()
{
    //皮肤
    if(PlayerPrefs.GetString("sh","sh01") == "sh01")//读取数据 默认值为sh01
    {
        blue.isOn=true;
        PlayerPrefs.SetString("sh","sh01");
        PlayerPrefs.SetString("sb01","sb0101");
        PlayerPrefs.SetString("sb02","sb0102");
    }
    else
    {
        yellow.isOn=true;
        PlayerPrefs.SetString("sh","sh02");
        PlayerPrefs.SetString("sb01","sb0201");
        PlayerPrefs.SetString("sb02","sb0202");
    }
    
    //模式
    if(PlayerPrefs.GetInt("border",1) == 1)//读取数据 默认值为1
    {
        border.isOn=true;
        PlayerPrefs.SetInt("border",1);
    }
    else
    {
        noBorder.isOn=true;
        PlayerPrefs.SetInt("border",0);
    }
}

//皮肤
public void BlueSelected(bool isOn)
{
    if(isOn)
    {
        PlayerPrefs.SetString("sh","sh01");//蛇头图片文件的名称
        PlayerPrefs.SetString("sb01","sb0101");//蛇身图片文件的名称
        PlayerPrefs.SetString("sb02","sb0102");
    }
}
public void YellowSelected(bool isOn)
{
    if(isOn)
    {
        PlayerPrefs.SetString("sh","sh02");
        PlayerPrefs.SetString("sb01","sb0201");
        PlayerPrefs.SetString("sb02","sb0202");
    }
}

//模式
public void BorderSelected(bool isOn)
{
    if(isOn)
    {
        PlayerPrefs.SetInt("border",1);//没有setBool方法,只能setInt
    }
}
public void NoBorderSelected(bool isOn)
{
    if(isOn)
    {
        PlayerPrefs.SetInt("border",0);
    }
}

//开始游戏 跳转游戏场景
public void StartGame()
{
    UnityEngine.SceneManagement.SceneManager.LoadScene(1);
}

配置读取

获取文件

创建Resources文件夹

添加游戏中需要动态加载的文件

通过文件名加载文件Resources.Load()

》SnakeHead

皮肤

//通过Resources.Load(string path)方法加载资源,path的书写不需要Resources/ 以及文件扩展名
//如果有重名文件 写泛型Resources.Load<Image>(string path)
//写了泛型 可以自动转换类型
void Awake()
{
    //...
    gameObject.GetComponent<Image>().sprite=Resources.Load<Sprite>(PlayerPrefs.GetString("sh","sh02"));
    bodySprites[0]=Resources.Load<Sprite>(PlayerPrefs.GetString("sb01","sb0201"));
    bodySprites[1]=Resources.Load<Sprite>(PlayerPrefs.GetString("sb02","sb0202"));
}

边界

边界的显示

》MainUIController

public bool hasBorder=true;

void Start()
{
    if(PlayerPrefs.GetInt("border",1)==0)
    {
        hasBorder=false;
        foreach(Transform t in bgImage.gameObject.transform)//遍历 父物体下的所有子物体
        {
            t.gameObject.GetComponent<Image>().enabled=false;//禁用Image组件
        }
    }  
}

》SnakeHead

边界模式:死亡

自由模式:传送

//...
else
{
    if(MainUIController.Instance.hasBorder)
    {
        Die();
    }
    else
    {
        //传送
        //switch...
    }  
}

声音

游戏场景

main camera挂载audio source

赋值背景音乐bgm

勾选loop

》SnakeHead

赋值音效

吃食物音效

死亡音效

public AudioClip eatClip;
public AudioClip dieClip;

void Grow()
{
    AudioSource.PlayClipAtPoint(eatClip,Vector3.zero);
    //...
}

void Die()
{
    AudioSource.PlayClipAtPoint(dieClip,Vector3.zero);
    //...
}

发布游戏

file——build setting

playersetting设置图标icon

build

注册表

win+R

regedit

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值