diff --git a/bandwidth-test.bin b/bandwidth-test.bin new file mode 100644 index 0000000..d9d1204 Binary files /dev/null and b/bandwidth-test.bin differ diff --git a/build_output.txt b/build_output.txt index 0f5c296..c62ca6e 100644 --- a/build_output.txt +++ b/build_output.txt @@ -2,8 +2,8 @@ Checking File Globs godot-cpp.vcxproj -> E:\newgame\marry\build\bin\libgodot-cpp.windows.template_debug.x86_64.lib - pingpong_ball.cpp + register_types.cpp + pingpong_game.cpp 正在创建库 E:/newgame/marry/main/bin/Release/pingpang.lib 和对象 E:/newgame/marry/main/bin/Release/pingpang.exp pingpang.vcxproj -> E:\newgame\marry\main\bin\Release\pingpang.dll 复制 pingpang.dll 到 main/bin/ 根目录 - Building Custom Rule E:/newgame/marry/CMakeLists.txt diff --git a/main/scenes/main.tscn b/main/scenes/main.tscn index 1aff08a..9561a8d 100644 --- a/main/scenes/main.tscn +++ b/main/scenes/main.tscn @@ -1,15 +1,16 @@ [gd_scene load_steps=4 format=3] [sub_resource type="CircleShape2D" id="circle_ball"] -radius = 12.0 +radius = 10.0 [sub_resource type="RectangleShape2D" id="rect_paddle"] -size = Vector2(20.0, 120.0) +size = Vector2(18.0, 100.0) [node name="Game" type="PingPongGame"] [node name="Ball" type="PingPongBall" parent="Game"] position = Vector2(576.0, 324.0) +initial_speed = 350.0 [node name="CollisionShape2D" type="CollisionShape2D" parent="Game/Ball"] shape = SubResource("circle_ball") @@ -51,9 +52,17 @@ horizontal_alignment = 1 theme_override_font_sizes/font_size = 48 theme_override_colors/font_color = Color(1.0, 1.0, 1.0, 1.0) -[node name="CenterLine" type="ColorRect" parent="Game/UI"] -offset_left = 574.0 -offset_top = 0.0 -offset_right = 578.0 -offset_bottom = 648.0 -color = Color(1.0, 1.0, 1.0, 0.1) +[node name="Message" type="Label" parent="Game/UI"] +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -200.0 +offset_top = -100.0 +offset_right = 200.0 +offset_bottom = 100.0 +text = "按空格键开始" +horizontal_alignment = 1 +vertical_alignment = 1 +theme_override_font_sizes/font_size = 36 +theme_override_colors/font_color = Color(1.0, 1.0, 1.0, 0.9) diff --git a/src/pingpong_ball.cpp b/src/pingpong_ball.cpp index 24050a5..dd71acd 100644 --- a/src/pingpong_ball.cpp +++ b/src/pingpong_ball.cpp @@ -11,12 +11,24 @@ using namespace godot; +// ═══════════════════════════════════════════════════════ +// 视觉颜色常量 — 以后替换素材时删除本段 +// ═══════════════════════════════════════════════════════ +static const Color BALL_COLOR = Color(1.0f, 1.0f, 1.0f); + void PingPongBall::_bind_methods() { // 属性 ClassDB::bind_method(D_METHOD("get_speed"), &PingPongBall::get_speed); ClassDB::bind_method(D_METHOD("set_speed", "value"), &PingPongBall::set_speed); ClassDB::add_property("PingPongBall", PropertyInfo(Variant::FLOAT, "speed"), "set_speed", "get_speed"); + ClassDB::bind_method(D_METHOD("get_initial_speed"), &PingPongBall::get_initial_speed); + ClassDB::bind_method(D_METHOD("set_initial_speed", "value"), &PingPongBall::set_initial_speed); + ClassDB::add_property("PingPongBall", PropertyInfo(Variant::FLOAT, "initial_speed"), "set_initial_speed", "get_initial_speed"); + + ClassDB::bind_method(D_METHOD("get_active"), &PingPongBall::get_active); + ClassDB::bind_method(D_METHOD("set_active", "value"), &PingPongBall::set_active); + // 方法 ClassDB::bind_method(D_METHOD("reset"), &PingPongBall::reset); @@ -25,7 +37,9 @@ void PingPongBall::_bind_methods() { } PingPongBall::PingPongBall() { - speed = 400.0f; + speed = 350.0f; + initial_speed = 350.0f; + is_active = false; } PingPongBall::~PingPongBall() { @@ -35,78 +49,98 @@ void PingPongBall::_ready() { reset(); } +// ═══════════════════════════════════════════════════════ +// 视觉层 — 球体 + 拖尾(几何体占位) +// 替换方法:删除本方法 → 添加 Sprite2D 子节点 +// ═══════════════════════════════════════════════════════ void PingPongBall::_draw() { - // 画一个白色圆形表示球 - draw_circle(Vector2(0.0f, 0.0f), 12.0f, Color(1.0f, 1.0f, 1.0f)); + // 拖尾:从旧到新画渐隐圆 + int trail_count = (int)trail_positions.size(); + for (int i = 0; i < trail_count; i++) { + float alpha = (float)(i + 1) / (float)BALL_TRAIL_LENGTH * 0.3f; + float radius = BALL_RADIUS * (0.5f + 0.5f * (float)(i + 1) / (float)BALL_TRAIL_LENGTH); + draw_circle(trail_positions[i], radius, Color(1.0f, 1.0f, 1.0f, alpha)); + } + + // 球本体 + draw_circle(Vector2(0.0f, 0.0f), BALL_RADIUS, BALL_COLOR); } void PingPongBall::_physics_process(double delta) { queue_redraw(); + + if (!is_active) { + return; + } + + // 更新拖尾 + trail_positions.push_back(get_position()); + if ((int)trail_positions.size() > BALL_TRAIL_LENGTH) { + trail_positions.erase(trail_positions.begin()); + } + Vector2 velocity = direction * speed * (float)delta; Ref collision = move_and_collide(velocity); if (collision.is_valid()) { - // 计算反弹方向 Vector2 normal = collision->get_normal(); direction = direction.bounce(normal); - // 根据碰撞法线判断碰撞对象类型 - // 法线主要水平 → 碰到球拍(左右反弹) - // 法线主要垂直 → 碰到上下墙(上下反弹) - // 如果法线接近水平且球已经接近左右边界 → 得分 Object *collider = collision->get_collider(); if (collider) { Node *collider_node = Object::cast_to(collider); if (collider_node) { StringName node_name = collider_node->get_name(); - // 判断是不是球拍(球拍名字包含 "Paddle") + // 碰到球拍 if (node_name.find("Paddle") != -1) { - // 碰到球拍,微调角度(根据击中位置) Node2D *collider_2d = Object::cast_to(collider_node); if (collider_2d) { + // 根据击中位置微调角度 float hit_offset = (get_global_position().y - collider_2d->get_global_position().y) / 60.0f; hit_offset = CLAMP(hit_offset, -1.0f, 1.0f); direction.y += hit_offset * 0.5f; direction = direction.normalized(); + + // 速度递增 + speed *= BALL_SPEED_INCREMENT; + if (speed > BALL_MAX_SPEED) { + speed = BALL_MAX_SPEED; + } } } } } - // 确保方向始终有效 direction = direction.normalized(); } - // 检测是否出界(得分判定) + // 得分判定 Viewport *viewport = get_viewport(); if (viewport) { Vector2 screen_size = viewport->get_visible_rect().size; Vector2 pos = get_global_position(); if (pos.x < -50.0f) { - // 球从左边出界,右边得分 emit_signal("scored", 1); reset(); } else if (pos.x > screen_size.x + 50.0f) { - // 球从右边出界,左边得分 emit_signal("scored", 0); reset(); } - // Y 方向边界限制(防止飞出屏幕) - if (pos.y < 0.0f) { - set_global_position(Vector2(pos.x, 0.0f)); + // Y 边界限制 + if (pos.y < BALL_RADIUS) { + set_global_position(Vector2(pos.x, BALL_RADIUS)); direction.y = std::abs(direction.y); - } else if (pos.y > screen_size.y) { - set_global_position(Vector2(pos.x, screen_size.y)); + } else if (pos.y > screen_size.y - BALL_RADIUS) { + set_global_position(Vector2(pos.x, screen_size.y - BALL_RADIUS)); direction.y = -std::abs(direction.y); } } } void PingPongBall::reset() { - // 回到屏幕中央 Viewport *viewport = get_viewport(); if (viewport) { Vector2 screen_size = viewport->get_visible_rect().size; @@ -115,18 +149,32 @@ void PingPongBall::reset() { set_global_position(Vector2(640.0f, 360.0f)); } - // 随机方向(-45° 到 45° 之间,向左或向右随机) - float angle = (UtilityFunctions::randf() - 0.5f) * Math_PI / 2.0f; // -45° ~ 45° + // 重置速度 + speed = initial_speed; + + // 清空拖尾 + trail_positions.clear(); + + // 随机方向 + float angle = (UtilityFunctions::randf() - 0.5f) * Math_PI / 2.0f; int side = (UtilityFunctions::randf() > 0.5f) ? 1 : -1; direction = Vector2((float)side * std::cos(angle), std::sin(angle)).normalized(); - // 确保水平分量足够(避免球过于垂直) + // 确保水平分量足够 if (std::abs(direction.x) < 0.3f) { direction.x = (direction.x > 0 ? 1.0f : -1.0f) * 0.3f; direction = direction.normalized(); } } +void PingPongBall::set_active(bool p_active) { + is_active = p_active; +} + +bool PingPongBall::get_active() const { + return is_active; +} + void PingPongBall::set_speed(float p_speed) { speed = p_speed; } @@ -134,3 +182,11 @@ void PingPongBall::set_speed(float p_speed) { float PingPongBall::get_speed() const { return speed; } + +void PingPongBall::set_initial_speed(float p_speed) { + initial_speed = p_speed; +} + +float PingPongBall::get_initial_speed() const { + return initial_speed; +} diff --git a/src/pingpong_ball.h b/src/pingpong_ball.h index bde96c4..03dbd5a 100644 --- a/src/pingpong_ball.h +++ b/src/pingpong_ball.h @@ -2,15 +2,30 @@ #define PINGPONG_BALL_H #include +#include namespace godot { +// ═══════════════════════════════════════════════════════ +// 视觉常量 — 以后替换为 Sprite2D 时删除本段 +// (颜色值定义在 pingpong_ball.cpp 中) +// ═══════════════════════════════════════════════════════ +static constexpr int BALL_TRAIL_LENGTH = 8; +static constexpr float BALL_RADIUS = 10.0f; +static constexpr float BALL_SPEED_INCREMENT = 1.03f; +static constexpr float BALL_MAX_SPEED = 800.0f; + class PingPongBall : public CharacterBody2D { GDCLASS(PingPongBall, CharacterBody2D) private: - float speed = 300.0f; + float speed = 350.0f; + float initial_speed = 350.0f; Vector2 direction = Vector2(1.0f, 0.0f); + bool is_active = false; + + // 拖尾位置记录(视觉层) + std::vector trail_positions; protected: static void _bind_methods(); @@ -26,10 +41,17 @@ public: // 重置球到屏幕中央,以随机角度发射 void reset(); + // 活跃状态控制 + void set_active(bool p_active); + bool get_active() const; + // 属性 getter/setter void set_speed(float p_speed); float get_speed() const; + void set_initial_speed(float p_speed); + float get_initial_speed() const; + // 信号: scored(int which) — 0=左边得分, 1=右边得分 }; diff --git a/src/pingpong_game.cpp b/src/pingpong_game.cpp index 9316ec2..ad98209 100644 --- a/src/pingpong_game.cpp +++ b/src/pingpong_game.cpp @@ -4,10 +4,21 @@ #include #include #include +#include +#include #include +#include using namespace godot; +// ═══════════════════════════════════════════════════════ +// 视觉颜色常量 — 以后替换素材时删除本段 +// ═══════════════════════════════════════════════════════ +static const Color FIELD_BG_COLOR = Color(0.06f, 0.10f, 0.18f); +static const Color FIELD_BORDER_COLOR = Color(1.0f, 1.0f, 1.0f, 0.25f); +static const Color FIELD_CENTER_LINE_COLOR = Color(1.0f, 1.0f, 1.0f, 0.12f); +static const Color FIELD_CIRCLE_COLOR = Color(1.0f, 1.0f, 1.0f, 0.06f); + void PingPongGame::_bind_methods() { // 属性 ClassDB::bind_method(D_METHOD("get_left_score"), &PingPongGame::get_left_score); @@ -24,6 +35,7 @@ void PingPongGame::_bind_methods() { // 方法 ClassDB::bind_method(D_METHOD("reset_game"), &PingPongGame::reset_game); + ClassDB::bind_method(D_METHOD("start_countdown"), &PingPongGame::start_countdown); ClassDB::bind_method(D_METHOD("_on_ball_scored", "which"), &PingPongGame::_on_ball_scored); // 信号 @@ -34,41 +46,162 @@ PingPongGame::PingPongGame() { left_score = 0; right_score = 0; max_score = 5; + state = WAITING; + state_timer = 0.0f; + countdown_number = COUNTDOWN_START; } PingPongGame::~PingPongGame() { } void PingPongGame::_ready() { - // 查找比分 Label + // 查找子节点 left_score_label = Object::cast_to