乒乓球游戏:C++ GDExtension 实现

这个提交包含在:
2026-08-05 15:10:47 +08:00
父节点 7b7757c002
当前提交 7228d302f5
修改 117 个文件,包含 803 行新增121 行删除
+136
查看文件
@@ -0,0 +1,136 @@
#include "pingpong_ball.h"
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/kinematic_collision2d.hpp>
#include <godot_cpp/classes/node2d.hpp>
#include <godot_cpp/classes/viewport.hpp>
#include <godot_cpp/classes/window.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
using namespace godot;
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("reset"), &PingPongBall::reset);
// 信号
ADD_SIGNAL(MethodInfo("scored", PropertyInfo(Variant::INT, "which")));
}
PingPongBall::PingPongBall() {
speed = 400.0f;
}
PingPongBall::~PingPongBall() {
}
void PingPongBall::_ready() {
reset();
}
void PingPongBall::_draw() {
// 画一个白色圆形表示球
draw_circle(Vector2(0.0f, 0.0f), 12.0f, Color(1.0f, 1.0f, 1.0f));
}
void PingPongBall::_physics_process(double delta) {
queue_redraw();
Vector2 velocity = direction * speed * (float)delta;
Ref<KinematicCollision2D> 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<Node>(collider);
if (collider_node) {
StringName node_name = collider_node->get_name();
// 判断是不是球拍(球拍名字包含 "Paddle"
if (node_name.find("Paddle") != -1) {
// 碰到球拍,微调角度(根据击中位置)
Node2D *collider_2d = Object::cast_to<Node2D>(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();
}
}
}
}
// 确保方向始终有效
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));
direction.y = std::abs(direction.y);
} else if (pos.y > screen_size.y) {
set_global_position(Vector2(pos.x, screen_size.y));
direction.y = -std::abs(direction.y);
}
}
}
void PingPongBall::reset() {
// 回到屏幕中央
Viewport *viewport = get_viewport();
if (viewport) {
Vector2 screen_size = viewport->get_visible_rect().size;
set_global_position(screen_size / 2.0f);
} else {
set_global_position(Vector2(640.0f, 360.0f));
}
// 随机方向(-45° 到 45° 之间,向左或向右随机)
float angle = (UtilityFunctions::randf() - 0.5f) * Math_PI / 2.0f; // -45° ~ 45°
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_speed(float p_speed) {
speed = p_speed;
}
float PingPongBall::get_speed() const {
return speed;
}