如何将多类变量传入碰撞检测函数?水果捕捉游戏开发疑问
水果捕捉游戏:碰撞检测与类变量调用问题解答
嘿,刚入门编程遇到这些问题太正常啦!我来一步步帮你解决碰撞检测和类变量调用的问题,顺便给你提些代码规范的小建议~
一、先理解碰撞检测的核心逻辑
两个圆形发生碰撞的判断标准很简单:两个圆心之间的距离 ≤ 两个圆的半径之和。为了避免开根号的性能损耗,我们可以用「距离的平方」和「半径和的平方」来比较,结果完全一致,还能提升运行效率。
二、如何调用类的变量实现碰撞检测
你的代码里,fruit 继承自 row,x、y 是 public 权限;character 继承自 catcher,catchX、catchY 也是 public 权限,所以可以直接访问这些变量。不过更规范的做法是用封装+getter方法,我后面会给你演示更合理的写法。
快速实现碰撞检测的示例函数
先给你一个基于你现有代码的简单碰撞检测函数:
// 传入fruit和character的实例,返回是否碰撞 bool checkCollision(const fruit& f, const character& c) { // 计算圆心的x、y差值 float dx = f.x - c.catchX; float dy = f.y - c.catchY; // 计算距离的平方(避免开根号) float distanceSquared = dx * dx + dy * dy; // 两个半径之和的平方(水果半径20,捕捉器半径50) float combinedRadiusSquared = (20 + 50) * (20 + 50); // 判断是否碰撞 return distanceSquared <= combinedRadiusSquared; }
在游戏循环里使用这个函数
你可以在每次刷新画面时调用它,处理碰撞逻辑:
// 假设你已经创建了实例 fruit myFruit; character myCatcher; myFruit.setpoint(400, 0); myCatcher.setpoint(400, 900); while (true) { // 清屏、移动、绘制元素... cleardevice(); myFruit.down(); myFruit.draw(); myCatcher.draw(); // 检测碰撞 if (checkCollision(myFruit, myCatcher)) { // 碰撞后的逻辑:比如重置水果位置、加分 myFruit.setpoint(rand() % 800, 0); } // 处理按键移动(示例) if (GetAsyncKeyState(VK_LEFT)) myCatcher.MoveLeft(); if (GetAsyncKeyState(VK_RIGHT)) myCatcher.MoveRight(); Sleep(20); // 控制帧率,避免画面闪烁 }
三、给你的代码规范小建议
刚入门养成好的编码习惯很重要,我帮你调整了一些细节:
- 类名首字母大写:C++的编码规范通常要求类名首字母大写(比如
Row、Fruit、Catcher、Character),方便区分普通变量和类。 - 封装成员变量:把
x、y、catchX这些变量设为private,通过getter方法访问,这样后续修改变量逻辑时,外部代码不用跟着改。 - 避免硬编码数值:把水果和捕捉器的半径设为静态常量,方便统一修改。
- 修复边界判断:原来的
if (y==1000)改成if (y >= 1000),因为downSpeed是浮点数,y可能不会刚好等于1000,导致水果无法重置位置。
调整后的完整示例代码
#include <graphics.h> // 假设你用的是EasyX或类似图形库 #include <cstdlib> // 用于rand() #include <windows.h> // 用于Sleep() #include <ctime> // 用于随机数种子 // 封装后的Row基类 class Row { private: int x, y; float downSpeed = 5; public: void setpoint(int xi, int yi) { x = xi; y = yi; } void down() { y += downSpeed; if (y >= 1000) { // 用>=更稳妥,避免浮点数精度问题 y = 0; } } // getter方法获取私有变量 int getX() const { return x; } int getY() const { return y; } }; class Fruit : public Row { public: static const int RADIUS = 20; // 水果半径设为常量 void draw() { setcolor(11); circle(getX(), getY(), RADIUS); } }; // 封装后的Catcher基类 class Catcher { private: float moveSpeed = 5; float catchX, catchY; public: void setpoint(int xi, int yi) { catchX = xi; catchY = yi; } void MoveLeft() { catchX -= moveSpeed; } void MoveRight() { catchX += moveSpeed; } // getter方法 float getCatchX() const { return catchX; } float getCatchY() const { return catchY; } }; class Character : public Catcher { public: static const int RADIUS = 50; // 捕捉器半径设为常量 void draw() { setcolor(15); circle(getCatchX(), getCatchY(), RADIUS); } }; // 用getter实现的碰撞检测函数,更符合封装原则 bool checkCollision(const Fruit& fruit, const Character& catcher) { float dx = fruit.getX() - catcher.getCatchX(); float dy = fruit.getY() - catcher.getCatchY(); float distanceSq = dx * dx + dy * dy; float combinedRadius = Fruit::RADIUS + Character::RADIUS; float combinedRadiusSq = combinedRadius * combinedRadius; return distanceSq <= combinedRadiusSq; } // 主函数示例 int main() { initgraph(800, 1000); // 初始化800x1000的窗口 srand(time(0)); // 初始化随机数种子 Fruit myFruit; myFruit.setpoint(rand() % 800, 0); // 随机初始位置 Character myCatcher; myCatcher.setpoint(400, 900); // 捕捉器初始在底部中间 while (true) { cleardevice(); // 清屏 // 更新元素状态 myFruit.down(); // 处理按键移动 if (GetAsyncKeyState(VK_LEFT)) { myCatcher.MoveLeft(); } if (GetAsyncKeyState(VK_RIGHT)) { myCatcher.MoveRight(); } // 绘制元素 myFruit.draw(); myCatcher.draw(); // 检测碰撞 if (checkCollision(myFruit, myCatcher)) { myFruit.setpoint(rand() % 800, 0); // 重置水果位置 } Sleep(20); // 控制帧率 } closegraph(); return 0; }
四、总结
- 圆形碰撞的核心是比较圆心距离和半径和,用平方计算更高效;
- 类的变量调用可以直接访问public成员,但封装+getter是更规范、更易维护的做法;
- 刚入门不用追求完美,先实现功能,再慢慢优化代码规范~
内容的提问来源于stack exchange,提问作者Nadila
相关产品推荐
相关产品推荐

