Unity百游修炼24——C#和Unity实现2D完整小项目《捕鱼达人》(附有完整项目和素材)

 一、项目声明

         本博客使用的所有素材(包括但不限于图片、视频、音频、文字等)已购买版权,仅供个人学习、交流、参考和研究使用,同学们如果想要用于个人项目或商业游戏请到https://itch.io/购买版权进行使用,否则需承担因使用该素材而产生的全部责任。本博客不承担任何直接或间接的法律责任。

二、项目演示

捕鱼达人测试

三、前期准备

1.创建2D核心模版项目

2.导入项目资源(创建文件夹进行分类保存)

四、游戏场景UI设计——经验条UI设计

1.创建2个场景GameScene和MainMenuScene

2.选中GameScene,创建背景2DSprite——Square,拖入背景素材,调整大小(1.3倍即可)。调整Game视图分辨率为1280*720

3.创建Image,设置锚点为左上角,拖入素材,设置合适大小和位置(保证后续便于计算取整数)

4.创建1个slider滑条作为经验条,删除滑条的滑头,给fill换成经验条素材

5.创建1个Text,作为等级文本,调整合适的范围以及大小

6.创建1个Text,作为经验等级称号文本,设置合适的大小和范围

五、游戏场景UI设计——倒计时和金币UI设计

1.倒计时UI设计

(1)创建1个Image,用来作为UI背景

(2)创建1个Text,作为时间计数

2.金币UI设计

(1)创建1个Image作为背景

(2)创建一个image,作为金币图标

(3)创建1个text,作为金币的数量

六、水波纹设计

水波测试

1.水波纹材质效果设计

(1)创建一个材质,选择Shader按照图中所找,然后拖入Texture即可生成水波纹材质

(2)创建3D物体Plane,旋转并且调整大小,使其在UI和UIBG中间,然后把材质拖入给Plane身上

2.实现水循环循环播放的效果

效果演示如下:

(1)创建脚本控制Texture进行贴图轮换从而实现效果

(2)把素材全部拖入数组中

(3)代码

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

public class Effect : MonoBehaviour
{
    public Material WaterWave_Material;
    public Texture[] WaterWave_Textures;
    private int index = 0;

    private void Start()
    {
        WaterWave_Material=this.GetComponent<MeshRenderer>().material;
        InvokeRepeating("ChangeWaterWaveTexture",0,0.1f);
    }

    public void ChangeWaterWaveTexture()
    {
        WaterWave_Material.mainTexture = WaterWave_Textures[index];
        index=(index+1)% WaterWave_Textures.Length;
    }
}

七、制作炮架和炮台

1.制作炮架

(1)创建1个空物体用来管理炮相关的物体

(2)创建3个2D Sprite 方形,添加木板素材

(3)调整合适的大小

2.制作炮台

(1)创建4个2D Sprite 方形,添加炮台素材

(2)创建一个空物体,作为后面子弹发射的位置

(3)把炮台做成预制体保存起来

3.让炮台跟着鼠标旋转

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

public class RotateGun : MonoBehaviour
{
    void Update()
    {
        ChangeGunDir();
    }

    private void ChangeGunDir()
    {
        Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        mousePos.z = 0;

        Vector3 dir=mousePos-transform.position;
        float angle=Mathf.Atan2 (dir.y,dir.x)*Mathf.Rad2Deg;
        transform.rotation = Quaternion.Euler(0, 0, angle - 90);
    }
}

八、生成鱼

1. Fish 脚本:单条鱼的移动控制

public class Fish : MonoBehaviour
{
    public float moveSpeed = 0f; // 移动速度(可在Inspector面板设置)
    public int fishNum = 0; // 该品种鱼群的生成数量(每个预制体独立配置)

    private void Update()
    {
        // 沿自身右方向移动(受旋转影响,实现不同方向游动)
        transform.Translate(Vector3.right * Time.deltaTime * moveSpeed);
    }
}

2.关键逻辑

  • moveSpeed 控制移动快慢,fishNum 定义该品种一次生成多少条鱼(由预制体本身决定,比如 “小丑鱼” 一次生成 5 条,“鲨鱼” 一次生成 2 条)。
  • Update 中通过 Translate 实现移动,Vector3.right 是 “自身局部坐标系的右方向”,因此鱼的移动方向会受其旋转角度影响(这与生成时的旋转设置呼应)。

3. GrowFish 脚本:鱼群生成逻辑

public class GrowFish : MonoBehaviour
{
    // 生成点数组(在Inspector拖入场景中的生成点对象)
    public Transform[] GrowPoints;
    // 鱼预制体数组(拖入不同品种的鱼预制体)
    public GameObject[] FishPrefabs;

    private void Start()
    {
        // 启动重复调用:从0秒开始,每0.5秒执行一次GrowFishs()
        InvokeRepeating("GrowFishs", 0, 0.5f);
    }

    public void GrowFishs()
    {
        // 随机选择一个生成点
        int PointIndex = Random.Range(0, GrowPoints.Length);
        // 随机选择一种鱼预制体
        int FishIndex = Random.Range(0, FishPrefabs.Length);

        // 随机旋转角度(-45°到45°,让鱼群方向有轻微差异)
        float randomAngle = Random.Range(-45f, 45f);
        Quaternion randomRotation = Quaternion.Euler(0, 0, randomAngle);
        // 最终旋转 = 生成点自身旋转 + 随机角度(生成点可预设基础方向)
        Quaternion finalRotation = GrowPoints[PointIndex].rotation * randomRotation;

        // 获取该品种鱼的生成数量(从预制体的Fish组件中读取)
        int num = FishPrefabs[FishIndex].GetComponent<Fish>().fishNum;

        // 启动协程生成鱼群
        StartCoroutine(MakeOnePeciesFish(
            FishPrefabs[FishIndex],  // 选中的鱼预制体
            GrowPoints[PointIndex],  // 选中的生成点
            finalRotation,           // 最终旋转角度
            num                      // 生成数量
        ));
    }

    // 协程:按间隔生成单种鱼群的多条鱼
    IEnumerator MakeOnePeciesFish(GameObject fishPrefab, Transform growPoint, 
                                 Quaternion rotation, int num)
    {
        for (int i = 0; i < num; i++)
        {
            // 实例化鱼:位置=生成点位置,旋转=计算好的角度,父物体=生成点(方便管理)
            GameObject fish = Instantiate(fishPrefab, growPoint.position, rotation, growPoint);
            // 调整层级:每条鱼的排序值+1,避免重叠时完全遮挡
            fish.GetComponent<SpriteRenderer>().sortingOrder += i;
            // 每条鱼生成间隔1秒(可调整)
            yield return new WaitForSeconds(1f);
        }
    }
}

4.关键逻辑

  • 生成点与预制体管理:通过数组 GrowPoints 和 FishPrefabs 灵活配置生成位置和鱼种类,方便在编辑器中调整,无需改代码。
  • 随机化设计:随机选择生成点、鱼种类、旋转角度,让鱼群生成更自然,避免机械重复。
  • 协程控制间隔:用 MakeOnePeciesFish 协程实现 “同一种鱼群中每条鱼的生成间隔”(当前是 1 秒),避免瞬间生成一堆鱼导致重叠。
  • 层级管理:通过 sortingOrder += i 让同群鱼按生成顺序轻微错开层级,解决 2D 渲染中完全重叠的问题。

5.鱼超出边界消失

(1)创建边界碰撞器,同时给鱼添加碰撞器(修改为触发器)和刚体组件

(2)代码

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

public class Fish : MonoBehaviour
{
    public float moveSpeed = 0f;
    public int fishNum=0;
    private void Update()
    {
        transform.Translate(Vector3.right*Time.deltaTime*moveSpeed);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Wall")
        {
            Destroy(gameObject);
        }
    }
}

九、制作切换炮台的按钮

1.制作炮台的预制体

(1)拖入素材,创建炮台

(2)挂载鼠标跟随脚本,制作所有炮台的预制体

2.炮台切换脚本

(1)创建2个button,然后拖入素材

(2)创建炮台脚本UIManger

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

public class UIManager : MonoBehaviour
{
    public GameObject[] ShootPrefebs;
    private int index = 0;
    private void Start()
    {
        ShootPrefebs[index].SetActive(true);
    }

    public void Btn_Shoot_Add()
    {
        ShootPrefebs[index].SetActive(false);
        index =(index+1)% ShootPrefebs.Length;
        if (index == ShootPrefebs.Length)
        {
            index=0;
        }
        ShootPrefebs[index].SetActive(true);
    }

    public void Btn_Shoot_Sub()
    {
        ShootPrefebs[index].SetActive(false);
        index = (index - 1) % ShootPrefebs.Length;
        if (index == -1)
        {
            index = ShootPrefebs.Length-1;
        }
        ShootPrefebs[index].SetActive(true);
    }
}

十、实现炮台发射子弹

1.实现鼠标生成子弹

(1)创建脚本挂在到炮台上,实现可在指定位置生成子弹预制体

(2)实现随机生成不同的子弹

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

public class BulletShoot : MonoBehaviour
{
    public GameObject[] Bullets;
    public GameObject BulletGrow;
    private int index;

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            ShootBullet();
        }
    }

    public void ShootBullet()
    {
        index=Random.Range(0, Bullets.Length);
        Instantiate(Bullets[index], BulletGrow.transform.position, BulletGrow.transform.rotation);
    }
}

2.实现子弹本身移动和销毁功能

(1)创建脚本,挂载到子弹预制体上,并设置合适的速度

(2)给子弹添加碰撞器,鱼和墙添加标签,墙也添加刚体(因为子弹给刚体,我担心发射太多子弹降低性能)

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

public class Bullet : MonoBehaviour
{
    public float Speed_bullet=0f;

    private void Update()
    {
        transform.Translate(Vector3.up * Speed_bullet * Time.deltaTime);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Fish")
        {
            Destroy(gameObject);
            //鱼处理逻辑
        }
        if (collision.tag == "Wall")
        {
            Destroy(gameObject);
        }
    }
}

十一、捕网制作以及销毁

1.捕网制作

(1)修改鱼的碰撞体(使得子弹确保打到鱼)

(2)创建2D Sprite ,添加网的素材。制作四种网并添加圆形碰撞器,制成预制体

(3)同时网的层级应该在鱼的上面(我的鱼最高位270),炮台和木板的下面(这个最低是1000)

(4)将网的生成与子弹碰到鱼联系到一起

        表示子弹销毁的同时,生成网的预制体

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

public class Bullet : MonoBehaviour
{
    public float Speed_bullet=0f;
    public GameObject WebPrefeb;

    private void Update()
    {
        transform.Translate(Vector3.up * Speed_bullet * Time.deltaTime);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Fish")
        {
            GameObject Web=Instantiate(WebPrefeb,gameObject.transform.position,Quaternion.identity);
            Destroy(gameObject);
            //鱼处理逻辑
        }
        if (collision.tag == "Wall")
        {
            Destroy(gameObject);
        }
    }
}

2.捕网销毁

(1)创建一个新的脚本控制网本身

(2)定时销毁

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

public class Web : MonoBehaviour
{
    public float Web_Destroy_Time = 0f;

    private void Start()
    {
        Destroy(this,Web_Destroy_Time);
    }
}

3.被捕的鱼销毁以及释放动画

(1)添加鱼碰到网销毁

(2)为了避免后续鱼移动到网里面不需要销毁,我写了获取碰撞器并让他失活

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

public class Fish : MonoBehaviour
{
    public float moveSpeed = 0f;
    public int fishNum=0;
    private void Update()
    {
        transform.Translate(Vector3.right*Time.deltaTime*moveSpeed);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Wall")
        {
            Destroy(gameObject);
        }

        if (collision.tag == "Web")
        {
            Destroy(gameObject);
            collision.gameObject.GetComponent<CircleCollider2D>().enabled = false;
        }
    }
}

(3)获取鱼的动画并播放动画然后再次销毁

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

public class Fish : MonoBehaviour
{
    public float moveSpeed = 0f;
    public int fishNum=0;
    public GameObject fish_Die_Anim;
    private void Update()
    {
        transform.Translate(Vector3.right*Time.deltaTime*moveSpeed);
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Wall")
        {
            Destroy(gameObject);
        }

        if (collision.tag == "Web")
        {
            GameObject DieAnim = Instantiate(fish_Die_Anim,gameObject.transform.position, gameObject.transform.rotation);
            Destroy(gameObject);
            collision.gameObject.GetComponent<CircleCollider2D>().enabled = false;
            Destroy(DieAnim,1f);
        }
    }
}

十二、UI制作

注意:这部分重在思路,代码也比较分散,所以一定要理解思路,自己写

1.等级更新以及相应称号更新

// 等级与称号相关变量
private int GradeValue = 0; // 当前等级
private string[] TitleText = { "菜鸟", "新手", "熟练", "大师", "渔王" };

// 等级计算方法
public void Grade_Calculate()
{
    GradeValue = (int)(LV_Value / Grade_Need_LV);
    Grade.text = GradeValue.ToString();
}

// 称号更新方法
public void Update_Title_Text()
{
    if (GradeValue >= 0 && GradeValue < 20) Title.text = TitleText[0];
    else if (GradeValue >= 20 && GradeValue < 40) Title.text = TitleText[1];
    else if (GradeValue >= 40 && GradeValue < 60) Title.text = TitleText[2];
    else if (GradeValue >= 60 && GradeValue < 80) Title.text = TitleText[3];
    else if (GradeValue >= 80 && GradeValue < 100) Title.text = TitleText[4];
}

核心逻辑:通过鱼被捕获获得经验,经验累积到阈值后升级,等级决定称号。

  • 经验机制:鱼被捕获时(Fish脚本的OnTriggerEnter2D),通过单例UIManager.Instance.LV_Value += fish_Admire_LV添加经验。每次升级后,经验值会扣除当前等级所需经验(实现 “归零” 效果),同时下一级所需经验增加(Grade_Need_LV += LevelUpExpIncrease),保证后期升级难度递增。

  • 等级计算:在Grade_Calculate中使用while循环处理(而非if),避免一次获得大量经验时只升一级的问题(例如一次获得 1000 经验,直接从 0 级升到 2 级)。

  • 称号系统:采用 “等级区间映射” 逻辑:每 20 级对应一个称号(0-19 级→菜鸟,20-39 级→新手...),通过GradeValue / 20计算称号索引,简洁且易扩展。

2.经验条UI制作

// 经验条相关变量
public Slider LVSlider; // 经验条组件
public float LV_Value = 0; // 当前经验值
public float Grade_Need_LV = 500; // 升级所需经验

// 经验条更新方法
public void Update_LVSlider()
{
    LVSlider.value = LV_Value % Grade_Need_LV / Grade_Need_LV;
}

实现方式:使用 Unity 的Slider组件作为经验条容器。

  • 进度计算:经验条的value属性(0-1 之间)由 “当前经验 / 升级所需经验” 计算得出(LVSlider.value = LV_Value / Grade_Need_LV),实时反映升级进度。

3.金币UI制作

// 金币相关变量
public Text Coin; // 金币文本组件
public float CoinCapcity = 500; // 当前金币数量

// 金币更新方法
public void Update_Coin_Text()
{
    Coin.text = CoinCapcity.ToString();
}

// 金币增减逻辑(分布在其他脚本)
// 1. 发射子弹消耗金币(BulletShoot.cs)
if (UIManager.Instance.CoinCapcity >= Bullet_Cost_Coin)
    UIManager.Instance.CoinCapcity -= Bullet_Cost_Coin;

// 2. 捕获鱼获得金币(Fish.cs)
UIManager.Instance.CoinCapcity += fish_Admire_Coin;

核心逻辑:金币通过捕获鱼增加(Fish脚本),发射子弹消耗(BulletShoot脚本),UI 实时显示当前金币数。

  • 性能优化:原代码中Update_Coin_TextUpdate中每帧调用,优化后改为 “数值变化时外部调用”(例如捕获鱼或发射子弹后调用),减少不必要的 UI 刷新。

  • 显示格式:使用ToString("F0")确保金币显示为整数(避免因浮点数精度问题出现小数,如 “500” 而非 “500.0”)。

4.倒计时UI制作

// 倒计时相关变量
public Text CountTime; // 倒计时文本组件
public float Time_Sum = 300; // 总倒计时(秒)
private float Timerjishu = 0; // 时间累加器

// 倒计时更新方法
public void Update_CountTime_Text()
{
    Timerjishu += Time.deltaTime;
    if (Timerjishu >= 1f)
    {
        Time_Sum--;
        Timerjishu = 0;
    }
    CountTime.text = Time_Sum.ToString();
}

实现方式:通过累计时间差实现整数秒递减,避免小数显示。

  • 核心逻辑:用Timerjishu累计每帧的Time.deltaTime,当累计满 1 秒时,Time_Sum减 1 并重置累加器,确保每秒只减 1 次。

  • 边界处理:当Time_Sum <= 0时停止更新,避免出现负数;可扩展添加倒计时结束逻辑(如游戏结束弹窗)。

5.设置按钮打开暂停界面

(1)功能说明

通过点击按钮触发暂停界面的显示,同时冻结游戏时间;暂停状态下可通过 “继续游戏” 按钮恢复正常运行。

(2)代码

// 暂停相关变量
public GameObject PausePanel; // 暂停面板UI
public AudioSource keySound; // 按钮音效

// 打开暂停面板
public void Open_PausePanel()
{
    keySound.Play(); // 播放按钮点击音效
    PausePanel.SetActive(true); // 显示暂停面板
    Time.timeScale = 0; // 冻结游戏时间(暂停Update逻辑)
}

// 继续游戏(关闭暂停面板)
public void Continue_Game()
{
    keySound.Play(); // 播放按钮点击音效
    PausePanel.SetActive(false); // 隐藏暂停面板
    Time.timeScale = 1; // 恢复游戏时间
}

(3)实现逻辑分析

  1. 面板显示与隐藏

    • 暂停面板(PausePanel)默认处于隐藏状态(SetActive(false)),点击 “暂停” 按钮时调用 Open_PausePanel(),通过 SetActive(true) 显示面板。
    • 点击 “继续游戏” 按钮时调用 Continue_Game(),隐藏面板(SetActive(false)),恢复游戏界面恢复游戏视图。
  2. 时间冻结机制

    • 调用 Time.timeScale = 0 冻结时间,使依赖 Time.deltaTime 的逻辑(如鱼的移动、倒计时)暂停,同时配合前文对 RotateGun 和 BulletShoot 的修改,确保暂停时无法操作枪支和射击。
    • 恢复时设置 Time.timeScale = 1,游戏逻辑正常运行。
  3. 交互反馈:按钮点击时播放 keySound 音效,增强用户操作的听觉反馈,提升交互体验。

6.游戏结束界面

(1)金币为0

// 金币与结束面板变量
public Text Coin; // 金币显示文本
public float CoinCapcity = 500; // 当前金币数量
public GameObject OverPanel; // 结束面板UI

// Update中检测金币是否耗尽
private void Update()
{
    // 其他UI更新逻辑...
    if (CoinCapcity <= 0 || Time_Sum == 0)
    {
        Open_overPanel(); // 触发结束界面
    }
}

// 打开结束面板
public void Open_overPanel()
{
    OverPanel.SetActive(true); // 显示结束面板
    Time.timeScale = 0; // 冻结时间
}
  • 金币通过射击消耗(BulletShoot 脚本中 CoinCapcity -= Bullet_Cost_Coin),当 CoinCapcity 减至 0 或负数时,Update 函数检测到条件并调用 Open_overPanel(),显示结束界面。
  • 结束后时间冻结(Time.timeScale = 0),防止后续逻辑继续执行。

(2)时间为0

// 倒计时相关变量
public Text CountTime; // 倒计时显示文本
public float Time_Sum = 300; // 总倒计时(秒)
private float Timerjishu = 0; // 时间累加器

// 倒计时更新
public void Update_CountTime_Text()
{
    Timerjishu += Time.deltaTime;
    if (Timerjishu >= 1f)
    {
        Time_Sum--; // 每秒减1
        Timerjishu = 0;
    }
    CountTime.text = Time_Sum.ToString(); // 显示剩余时间
}

// Update中检测时间是否为0
private void Update()
{
    // 其他UI更新逻辑...
    if (CoinCapcity <= 0 || Time_Sum == 0)
    {
        Open_overPanel(); // 触发结束界面
    }
}
  • 倒计时通过 Update_CountTime_Text() 每秒递减 1,当 Time_Sum 减至 0 时,Update 函数检测到条件并调用 Open_overPanel(),显示结束界面。
  • 与金币耗尽逻辑共用同一个结束面板,统一游戏结束入口。

十三、开始界面制作

(1)添加素材,个性化设置

(2)创建脚本,挂载代码

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

public class UIManagerMainMenu : MonoBehaviour
{
    public void Btn_EnterGame()
    {
        SceneManager.LoadScene(1);
    }

    public void Btn_Return_Menu()
    {
        SceneManager.LoadScene(0);
    }

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

(3)设置场景

十四、总结以及完整项目

通过网盘分享的文件:CatchFish.zip
链接: https://pan.baidu.com/s/13wpkdy_NoGG60b5TntnoOg?pwd=1111 提取码: 1111 
--来自百度网盘超级会员v5的分享

        回顾这篇项目记录更像一份一份 “从 0 到 1” 的解谜日志 —— 没有一开始始就追求完美,而是在 “发现问题 - 解决问题” 的循环中逐步搭建起完整的游戏框架。回头看,最核心的收获并非最终实现了多少功能,而是学会了如何将一个复杂的游戏需求拆解为可落地的小目标,并用逻辑串联起来。

        希望大家自己根据项目和素材完善其他功能和玩法,一定不要被上面的过程禁锢,按照自己的想法来

房屋与网球场目标检测数据集 一、基础信息 • 数据集名称:房屋与网球场目标检测数据集 • 图片数量: 训练集:273张图片 验证集:75张图片 测试集:92张图片 总计:440张图片 • 训练集:273张图片 • 验证集:75张图片 • 测试集:92张图片 • 总计:440张图片 • 分类类别: House(房屋):常见的住宅建筑类型。 TennisCourt(网球场):用于网球运动的专用场地。 • House(房屋):常见的住宅建筑类型。 • TennisCourt(网球场):用于网球运动的专用场地。 • 标注格式:YOLO格式,包含边界框类别标签,适用于目标检测任务。 • 数据来源:来源于航拍或相关图像数据集。 二、适用场景 • 城市规划与土地管理:自动检测房屋网球场,辅助城市发展分析土地利用规划。 • 房地产评估与开发:用于识别住宅建筑体育设施,支持房产估值项目规划。 • 体育设施监控:监控网球场的分布状态,优化体育资源管理维护。 • 航拍图像分析:适用于无人机或卫星图像中的目标检测,提升地理信息系统(GIS)遥感应用效率。 三、数据集优势 • 标注精准可靠:采用YOLO格式标注,边界框定位准确,确保模型训练的有效性。 • 类别聚焦实用:专注于房屋网球场两个常见类别,覆盖住宅娱乐设施,具有实际应用价值。 • 数据划分合理:提供训练集、验证集测试集,数据量分配科学,支持模型开发与评估。 • 兼容性强:标注格式兼容主流深度学习框架,如YOLO、PyTorch等,便于直接使用集成。 • 任务适配性高:专为目标检测任务设计,帮助构建高效、准确的AI模型,适用于多种现实场景。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值