C/C++程序员如何理解Godot的场景式游戏开发逻辑的?

godot引擎c++源码深度解析系列一 许久没有使用c++开发过项目了,如果按照此时单位的入职要求,必须拥有项目经验的话,那我就得回到十多年前,大学的时代,哪个时候真好,电脑没有这么普及,手机没有这么智能,网络没有这么发达,信息传播没有此时的快,哪个年代,5毛钱一份的报纸或许真的很好。入职it这个行业以来,想必很多的同行,前辈们,都知道,软件开发唯一的要求,实践能力必须很强,不亲自实操,你永远不知道代码里有多少个坑,同样的一段代码,在A电脑能运行,在B电脑就运行不了,这就是环境对此的影响。 阅读详情

C/C++程序员如何理解Godot的场景式游戏开发逻辑的?

阅读和写作背景

本人之前只接触过c/c++/python等传统串行程序,此前从未接触过任何游戏引擎(Unity/虚幻5等都没有接触过)。
本意想尝试自己做做游戏,因为Godot完全开源免费,因此第一意向就是Godot。

很小白,第一次接触Godot,照着Godot官网进行初步学习,学习到Godot关键概念概述的时候,我是懵逼的,我是不知所措的,有很多疑惑。在Godot游戏编辑器上创建的项目能够运行并且显示图片,但是我依然一头雾水,自觉脑袋很乱。如果你也有以下的疑惑,可以在本文中找找灵感:

  • Godot项目的程序入口在哪儿?
    • c/c++中有main程序入口,python的话直接运行哪个.py就是直接执行哪个.py的代码,很清晰。
    • 这个Godot项目到底是如何设置主要程序的呢?
  • 什么是场景/场景树?
    • 这些场景/场景树在游戏过程中怎么进入/使用/退出的?
    • 场景/场景树可以用面向对象的什么方式等效看待呢?
  • 等等疑惑

那么开始从“C++ 等传统串行程序思维”转向Godot这种“场景树 + 信号驱动 + 生命周期框架”的思维模式吧。

传统串行程序实现游戏的基本思路

假如想要实现一个简单的游戏例子:玩家角色可以通过↑↓←→移动,按[空格]可以对敌人造成20点的hp伤害,敌人满血100点,当血量降为0的时候消失。

总结下就是以下的要点:

  • 玩家角色
    • 贴图显示
    • 玩家移动控制
  • 敌人
    • 贴图显示
    • hp值
  • 玩家角色和敌人之间的互动(攻击等)

那么我们自然而然可以想到如下的基本结构体

// 角色基类
class CharacterBody2D {
public:
    CharacterBody2D(Texture2D img, Vector2 position): _img(img), _position(postition) {};
    
    void process(float delta) {}; // 每帧调用函数

    void draw() {draw(_img)}; // 使用_img这个贴图进行图像显示
private:
    Texture2D _img;
    Vector2 _position;
    int _speed = 200;
};

// 玩家角色
class Player: public CharacterBody2D {
public:
    void process(float delta){
        auto dir = Vector2.ZERO;
        // 玩家控制方向移动角色
        if (is_action_pressed('→')){
            dir.x += 1;
        }
        if (is_action_pressed('←')){
            dir.x -= 1;
        }
        if (is_action_pressed('↑')){
            dir.y -= 1;
        }
        if (is_action_pressed('↓')){
            dir.y += 1;
        }
        Vector2 velocity = dir.normalized() * _speed;
        // 根据速度和时间移动本身
        move_and_slide(delta, velocity);
    }

    void move_and_slide(float delta, Vector2 velocity){
        if (velocity != Vector2(0,0)) {
            _position += velocity * delta;
        }
    }
private:
    int _speed = 200;
}

// 敌人角色
class Enemy: public CharacterBody2D {
public:
    void process(float delta){
        auto dir = Vector2.ZERO;
        // 玩家控制方向移动角色
        if (is_action_pressed('→')){
            dir.x += 1;
        }
        if (is_action_pressed('←')){
            dir.x -= 1;
        }
        if (is_action_pressed('↑')){
            dir.y -= 1;
        }
        if (is_action_pressed('↓')){
            dir.y += 1;
        }
        Vector2 velocity = dir.normalized() * _speed;
        // 根据速度和时间移动本身
        move_and_slide(delta, velocity);
    }

    void take_damage(int amount){
        _health -= amount; // 被攻击后更新自身血量
    }

    bool should_disappear() {
        return _health <= 0;// 用于判断是否应该消失
    }

    void draw() {
        CharacterBody2D::draw();
        draw_str(str(_health));
    }
private:
    int _health = 100; // 血量
}

当我们有了上述的玩家角色Player和敌人Enemy这两种类,为了游戏主逻辑清晰简单,可以使用下面的类进行整体的流程的封装:

class MainScene {
public:
    MainScene() {
        _player = Player();
        _enemy = Enemy();
    };

    void process(float delta) {
        _player.process(delta);
        _enemy.process(delta);
    };
    
    void draw(){
        _player.draw();
        if (!_enemy.should_disappear()){
            _enemy.draw();
        }
    }
private:
    Player _player;
    Enemy _enemy;
}

最后,我们按照游戏主循环编写,流程如下:

int main(){
    MainScene Game = MainScene(); // 初始化游戏,加载纹理资源、必要的游戏对象
    while(true) {
        Game.process(); // 处理输入,同时更新事件等
        Game.draw(); // 绘制每一帧内容
    }
    return 0;
}

Godot实现游戏的基本思路

还是以上面所描述的游戏为例,在Godot中到底又是如何构建起这个小游戏的呢?依然采取自下而上的方式进行设计:

玩家场景Player

  • tscn文件:
    • 类型:CharachterBody2D
    • 依赖额外资源:
      • Texture2D: player.png贴图文件
      • Script: player.gd控制脚本
    • 依赖子资源:
      • RectangleShape2D:用于定义碰撞体积
    • 成员对象实例:
      • Sprite2D:贴图对象
      • CollisionShape2D:碰撞对象
[gd_scene load_steps=4 format=3 uid="uid://dcjlwlvhmti0k"]

[ext_resource type="Texture2D" uid="uid://cag1qcbgl437k" path="res://asserts/player.png" id="1_4flbx"]
[ext_resource type="Script" uid="uid://dhf8rfgovq2s1" path="res://player.gd" id="1_onrkg"]

[sub_resource type="RectangleShape2D" id="RectangleShape2D_onrkg"]

[node name="CharacterBody2D" type="CharacterBody2D"]
script = ExtResource("1_onrkg")

[node name="Sprite2D" type="Sprite2D" parent="."]
position = Vector2(300, 300)
scale = Vector2(0.2, 0.2)
texture = ExtResource("1_4flbx")

[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2(300, 300)
scale = Vector2(4, 4)
shape = SubResource("RectangleShape2D_onrkg")

  • gdscript文件:
extends CharacterBody2D

## 定义执行对敌人攻击的信号
signal attack_enemy()

## 玩家角色移动速度
var speed: int = 200

func _process(_delta: float) -> void:
	var dir := Vector2.ZERO
	if Input.is_action_pressed("ui_right"):
		dir.x += 1
	if Input.is_action_pressed("ui_left"):
		dir.x -= 1
	if Input.is_action_pressed("ui_up"):
		dir.y -= 1
	if Input.is_action_pressed("ui_down"):
		dir.y += 1

	velocity = dir.normalized() * speed
	move_and_slide()

func _input(event):
	if event is InputEventKey and event.is_pressed() and event.keycode == KEY_SPACE:
		emit_signal("attack_enemy")

敌人场景Enemy

  • tscn文件:
    • 类型:CharachterBody2D
    • 依赖额外资源:
      • Texture2D: enemy.png贴图文件
      • Script: enemy.gd控制脚本
    • 依赖子资源:
      • RectangleShape2D:用于定义碰撞体积
    • 成员对象实例:
      • Sprite2D:贴图对象
      • CollisionShape2D:碰撞对象
      • Label: 显示血量
[gd_scene load_steps=4 format=3 uid="uid://bvorb11f3qd8v"]

[ext_resource type="Script" uid="uid://c2mtv5iqfmlpg" path="res://enemy.gd" id="1_4gyqm"]
[ext_resource type="Texture2D" uid="uid://cyyfxt16mcyhu" path="res://asserts/enemy.png" id="1_7k104"]

[sub_resource type="RectangleShape2D" id="RectangleShape2D_7k104"]

[node name="Enemy" type="CharacterBody2D"]
script = ExtResource("1_4gyqm")

[node name="Sprite2D" type="Sprite2D" parent="."]
position = Vector2(700, 400)
scale = Vector2(0.2, 0.2)
texture = ExtResource("1_7k104")

[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2(700, 400)
scale = Vector2(4, 4)
shape = SubResource("RectangleShape2D_7k104")

[node name="HPLabel" type="Label" parent="."]
offset_left = 677.0
offset_top = 282.0
offset_right = 717.0
offset_bottom = 305.0
text = "100"
horizontal_alignment = 1

  • gdscript文件:
extends CharacterBody2D

var health := 100

@onready var hp_label = $HPLabel

func _ready() -> void:
	update_hp_label()
	
func update_hp_label() -> void:
	hp_label.text = str(health)

func take_damage(amount):
	health -= amount
	update_hp_label()
	print("Enemy HP:", health)
	if health <= 0:
		queue_free()


主场景MainScene

现在,我们回到一开始的问题

  • Godot的程序入口是什么?
    • Godot的程序入口就是在项目中设置的主场景
  • 那么一个场景又代表什么呢?
    • 一个场景代表可以复用的模块,这个模块包含了_ready/_input/_process/_exit等覆盖了游戏循环内外的各个主要任务的这些接口。
  • 那么这个例子中我们又该如何理解场景树呢?
    • 场景树由不同的场景经过实例化组成,而子场景由是由其他Node或者场景实例化组合而成的。组合是Godot复用的关键功能。
MainScene
PlayerScene
EnemyScene
MainScene
Sprite2D
EnemyScene
CollisionShape2D
Label
Sprite2D
PlayerScene
CollisionShape2D

那么来看下这个小游戏的Godot表现效果吧。

在这里插入图片描述

那么最终串行程序跟Godot之间的核心对照表是如何的呢?

概念C++ 程序Godot
主入口main() 函数主场景 (MainScene.tscn)
对象关系成员变量嵌套节点树嵌套
生命周期构造/析构函数_ready() / _exit_tree()
逻辑更新process() 主循环_process(delta) 引擎回调
输入检测轮询 handle_input()_input(event) 自动回调
通信方式函数调用 / 引用对象信号 (signal-slot)
资源加载手动 load 文件preload() 自动管理
销毁delete obj;queue_free()
程序控制权你拥有主循环Godot 拥有主循环
在Godot 4.2中使用GDExtension方式制作C++插件 这几天在B站和U管看了好多视频,结果还是看的还是云里雾里,视频解说找到很多,但是都是缺少严格的一不步步操作提示,今晚总算看官方文档,一步步搞成功了。用ZIP方式下载的文件名字会是godot-cpp-godot-4.2.2-stable,解压后把文件夹名字改成godot-cpp,并把这个文件复制到我们创建项目的根目录下;5、在项目根目录的src文件中我们开始编写插件文件。8、其实插件到这里已经是完成了,接下来我们打开godot,就会发现我们写的C++类,搜索节点类GDExample,就能找到我们写的插件了。 阅读详情

相关推荐

godot4.2 + GDextension c++在 vs code 中断点调试配置

我们点击 “configure task”按钮,然后搜索框这里会提示,我们选择"create task.json file form template",然后再点击 “orthers” 这类型的任务。完:写好以上这2个配置文件,然后点debug 运行,就能自动启动godot游戏窗口,而且我们还能再自己的c++插件代码中打断点调试。注意:1、program 这项的值是我们godot应用程序的路径,你要修改成自己的安装路径,注意后缀是.exe,然后要把"\",改成双斜杠"\\".我们要做修改,改成如下。

恒心的专栏 1364

Godot引擎Gdnative配置VisualStudio的方法 (c++)

1.官网下载godot-cpp 下载链接:https://github.com/godotengine/godot-cpp !注意 ! 下载如果使用的是zip下载,一定要进入godot-header文件夹,把godot-header也下载了,并放在相应的位置 推荐使用递归克隆 2.使用scons命令生成godot-cpp的lib库 打开cmd使用cd命令进入godot-cpp对应的文件夹,使用如下指令: ...

qq_31805591的博客 4815

Godot-CPP开发指南:从GDExtension原理到高性能游戏模块实践

在游戏开发领域,扩展系统是连接引擎核心功能与开发者自定义需求的关键桥梁。GDExtension作为Godot 4引入的扩展标准,通过动态库机制实现了引擎核心与扩展模块的解耦,为性能优化和功能定制提供了底层支持。其技术价值在于允许开发者在保持Godot高效编辑器工作流的同时,直接调用底层API进行高频计算和复杂逻辑处理。在应用场景上,特别适用于物理模拟、AI行为树、自定义渲染管线等性能关键路径。本文聚焦于Godot-CPP这一C++绑定库,它作为GDExtension的封装层,通过自动生成的C++类库和智能指

weixin_34323858的博客 426

探索godot-cpp: 开启游戏开发的C++新纪元

在游戏开发的世界里,每一个工具和框架都承载着开发者们对创意和技术追求的梦想。今天,我们要带您走进一个令人兴奋的开源项目——godot-cpp。这款基于著名的Godot引擎的C++绑定库,正以它强大的功能和灵活的设计,重新定义了跨平台游戏开发的可能性。 ## 一、项目介绍 ### 神来之笔 ——神级游戏引擎Godot与C++ Godot引擎凭借其简洁高效的界面设计、丰富的功能集以及活跃的社区支

gitblog_00992的博客 1980

Godot引擎C++绑定开发指南:GDExtension性能优化与第三方库集成

在游戏开发中,脚本语言如GDScript虽然易于上手,但在处理复杂逻辑和密集计算时可能遇到性能瓶颈。通过C++绑定技术,开发者可以将高性能的本地代码与游戏引擎结合,实现性能的显著提升。这项技术的核心原理是利用动态链接库在运行时扩展引擎功能,既保持了开发灵活性,又提供了接近原生代码的执行效率。其技术价值在于能够复用庞大的C++生态库,实现精细的内存控制,并保护核心算法逻辑。在应用场景上,特别适合性能敏感型游戏、已有C++代码库的整合、以及引擎深度定制需求。本文聚焦于Godot Engine的GDExtensi

weixin_34174105的博客 462

Godot引擎如何破解AI编程在游戏开发中的困境

AI编程助手在通用软件开发中表现出色,但在游戏开发这一垂直领域常面临挑战。游戏开发高度依赖特定引擎的API、生命周期和架构范式,这构成了AI的“知识盲区”。其核心原理在于,通用AI训练数据主要来自Web和算法项目,缺乏对游戏引擎特有工作流的深度理解。技术价值在于,通过领域适配,AI能更高效地辅助复杂交互逻辑的实现。应用场景包括独立游戏原型开发、玩法逻辑快速迭代等。Godot引擎以其极简API、节点化场景系统和GDScript语言,为AI提供了结构化框架,有效解决了资源管理、性能优化等难题。本文结合**Cur

weixin_34367257的博客 472

Godot-CPP 开源项目指南

### 简介 Godot-CPP 是一个用于 Godot 游戏引擎的 C++ 绑定库,允许开发者以 C++ 编写游戏逻辑或扩展 Godot 的功能。这个绑定库通过 GDExtension 接口提供,使得 C++ 开发者可以无缝地集成到 Godot 引擎中。 ### 特点 - **高效性**: 直接在底层实现性能关键的游戏逻辑。 - **灵活性**: 利用 C++ 的强大功能来开发复杂的游戏系统。...

gitblog_00647的博客 1507

Godot与Unreal Engine对比:轻量级游戏引擎的优势与应用场景

游戏引擎是游戏开发的核心工具,它负责渲染图形、处理物理、管理资源等底层功能。不同的引擎设计哲学决定了其技术架构和适用场景。开源引擎以其灵活性和低成本优势,在独立开发和教育领域展现出独特价值。Godot作为一款轻量级开源引擎,采用MIT许可证,允许开发者自由修改和分发,其模块化设计和GDScript脚本语言降低了学习门槛。在工程实践中,Godot的快速迭代和低硬件要求使其特别适合2D游戏开发和快速原型验证。对于追求极致画质和复杂物理模拟的3A大作,Unreal Engine的完整工具链更具优势。本文通过对比分

weixin_34049948的博客 428

如何快速上手Godot-CPP:10个步骤掌握C++高性能游戏开发

想要为你的Godot游戏项目注入C++级别的性能动力吗?🚀 **Godot-CPP** 是专为Godot引擎设计的C++绑定库,让开发者能够轻松实现**C++高性能游戏开发**,同时享受Godot引擎的便捷开发体验。本文将为你提供完整的入门指南,帮助你在10个步骤内掌握这个强大的工具! ## 为什么选择Godot-CPP进行游戏开发? ### 🎯 性能优势明显 - **原生C++执行效率*

gitblog_00060的博客 363

Godot C++开发指南:正确获取节点的Forward/Up/Right方向向量

本文详细解析了Godot C++中获取节点方向向量(Forward/Up/Right)的方法,澄清了常见的API误解。文章指出,虽然语法与GDScript/C#不同,但C++中可通过-basis[2]获取前向向量的逻辑完全一致。文中还介绍了Godot的右手坐标系特性,解释了为何使用-Z轴表示前向,并提供了Basis矩阵的结构说明。最后给出了两种实用的代码示例:基础角色控制器和第一人称相机控制,演示了方向向量的实际应用场景。该内容对从其他语言迁移到Godot C++的开发者具有重要参考价值。

电脑管家 1308

Godot积木编程插件:零代码游戏开发入门与实现原理

可视化编程通过图形化界面封装编程逻辑和API调用,降低了编程的入门门槛。其核心原理是将代表不同功能的图形模块(积木)通过拖拽、拼接的方式组合成逻辑流程图,再通过编译器或解释器将其转换为可执行的代码。这项技术的价值在于将抽象的语法和算法转化为直观的视觉操作,极大地提升了原型构建、逻辑梳理和教育演示的效率。在游戏开发、教育科技和快速原型设计等应用场景中,它成为连接创意与实现的有效工具。本文以Godot游戏引擎为例,深入探讨了如何设计并实现一个类似Scratch的积木编程插件,详细解析了其编辑器扩展、积木编译与动

weixin_30520015的博客 404

Godot-CPP开源项目实战指南:从绑定到部署的全流程解析

在游戏开发领域,C++因其高性能和系统级控制能力,常被用于核心模块开发。通过绑定技术,C++代码能够与高级脚本语言交互,扩展引擎功能并优化性能。Godot-CPP作为Godot引擎的C++绑定库,通过自动生成胶水代码,将GDExtension API暴露给C++,实现了原生性能与引擎生态的无缝集成。其技术价值在于为复杂算法、高性能逻辑和第三方库封装提供了底层支持,同时保持编辑器可视化工作流的便捷性。在应用场景上,它特别适合需要精细性能调优、模块化架构和团队协作的中大型开源项目。本文聚焦于Godot-CPP项

weixin_30731305的博客 612

Godot引擎与Go语言集成开发指南:GDExtension绑定与高性能游戏开发实践

游戏引擎扩展开发是现代游戏开发中的关键技术,它允许开发者突破引擎原生脚本的限制,引入更强大的编程语言和工具链。其核心原理是通过稳定的应用程序二进制接口(ABI),在引擎运行时加载外部编译的动态链接库,实现跨语言调用与功能扩展。这种技术能显著提升开发效率与运行时性能,尤其在需要处理高并发网络服务、复杂后台逻辑或集成特定技术栈的场景中价值突出。通过GDExtension等标准接口,开发者可以将像Go这类以高并发和简洁语法著称的系统级语言集成到Godot引擎中。本文聚焦于Godot-Go这一具体实践,详细解析了如

weixin_30420305的博客 298

Windows下Visual Studio配置Godot GDExtension C++开发环境全攻略

GDExtension是Godot引擎提供的原生扩展接口,允许开发者使用C++、Rust等高性能语言编写游戏模块,以突破脚本语言在计算密集型任务上的性能瓶颈。其核心原理是通过动态链接库(DLL)的形式,将编译后的本地代码与Godot运行时进行绑定,从而实现对引擎功能的深度扩展和现有C/C++生态库的无缝集成。这项技术的核心价值在于,它让开发者能够在享受Godot高效开发工作流的同时,在关键性能路径上获得接近原生的执行效率,并复用庞大的现有C++代码资产。典型的应用场景包括:实现复杂的物理模拟、进行密集的数学

weixin_34220179的博客 363

神庙逃亡(C语言全支持),神庙逃亡源代码,C,C++

C语言实现神庙逃亡游戏,命令行界面,可以运行

STM32_WS2811驱动

STM32的WS2811驱动

健身房 2840

如何快速掌握Godot游戏引擎:面向初学者的完整指南

你是否曾梦想制作自己的游戏,却被复杂的游戏引擎吓退?或者想要一个轻量级但功能强大的2D/3D游戏开发工具?Godot Engine正是为你量身打造的多平台游戏引擎解决方案。本文将带你从零开始,10分钟快速上手Godot,掌握这个开源游戏引擎的核心功能和使用技巧。 ## 🎯 Godot引擎核心概念快速概览 Godot Engine是一个免费开源的跨平台游戏引擎,支持2D和3D游戏开发。与Uni

gitblog_00442的博客 715

数字电路复位信号设计(异步复位、同步释放)笔记详解

复位信号设计(异步复位、同步释放)笔记详解 1 时序设计 1.1 复位信号设计(异步复位、同步释放) 1.1.0 复位恢复时间和清除时间 首先解释一下恢复时间(recovery time)和清除时间(removal time):(以复位信号低电平有效为例) 复位恢复时间(recovery time):撤销复位时,复位信号无效到时钟有效沿来临之间需要维持的最小的时间,类似于同步时钟的建立时间setup time; 复位清除时间(removal time):复位时,在时钟有效沿来临之后复位信号还需要保持复位

weixin_42294124的博客 1万+

Godot引擎简介,特点,用途及历史

Godot是一款,特别适合独立开发者。它对刘婉婷在简历中提到的“48小时独立游戏开发”等项目起到了核心支撑作用,其特点也与她在方面的优势高度契合。

小宝哥Code的专栏 1595

SAP传输请求全流程详解:从SE10创建到STMS跨环境部署(附常见错误排查)

本文详细解析了SAP传输请求的全流程,从SE10创建到STMS跨环境部署,并提供了常见错误排查方法。涵盖工作台请求、定制请求等类型,以及STMS传输原理和实战操作技巧,帮助用户高效管理SAP系统变更。

weixin_29214335的博客 143

从零到一:鲲鹏920服务器上的DeepSeek模型部署全流程解析

本文详细解析了在华为鲲鹏920服务器上部署DeepSeek模型的全流程,包括环境准备、模型服务框架部署、性能优化及生产环境方案。通过ARM架构优化和量化技术,显著提升模型推理效率,适用于企业级AI应用场景,助力国产化技术落地。

weixin_29094775的博客 599

Godot引擎C++模块开发实战:从模板搭建到自定义节点实现

C++模块开发是游戏引擎深度定制与性能优化的核心技术路径。其原理在于通过原生代码扩展引擎功能,直接与底层API交互,从而突破脚本语言的性能瓶颈,实现更高效的计算、更复杂的数据结构与更精细的资源管理。在Godot引擎中,这一技术价值尤为突出,开发者可以构建自定义节点(Node)、资源(Resource)或服务器(Server),将关键业务逻辑下沉至C++层,显著提升运行时效率。典型的应用场景包括高性能游戏系统、复杂的工具链开发以及团队内部可复用的核心框架搭建。本文聚焦于**Godot C++模板项目**,它通

weixin_30859423的博客 432

Simulink仿真状态空间中状态变量时域响应

起因 今天老师留了一道关于状态空间变量响应的题,于是想用simulink仿真,一开始使用state-space模块,但是发现它只能输出y,没办法显示状态变量的变化情况。然而状态变量之间的关系都可以表示为一阶微分方程组,因此我们考虑在Simulink中搭建微分方程组来求解这个问题。 微分方程组 x1˙=x2x2˙=−4x1−5x2+1(1)\dot{x_1}=x_2\\ \dot{x_2}=-4x_...

ZYunfei的博客 7448
上一篇: 游戏编程模式-享元模式(Flyweight)
下一篇: Godot4.x的整体架构图解析-源码阅读
慷仔
博客等级 码龄9年 88粉丝 46原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值