如何创建方向向量实现游戏球体的自主移动与碰撞角度反射
搞定球体自主移动与碰撞反弹的方案
嘿,我来帮你解决这个游戏开发里的问题!咱们一步步来,先把「点击空格就自主移动」搞定,再实现碰撞反弹的效果,顺便修正你现有代码里的小问题。
一、先实现点击空格后的自主移动
你现在是按住空格才移动,要改成点一下就自己跑,核心是给球体加个速度状态,而不是直接硬改位置。
1. 给Entity类加必要的属性
首先得给你的ballEntity(或者Entity基类)加两个成员变量,用来记录移动状态:
// 放到Entity类的成员里 // 如果不用glm向量,就用三个单独的float:vx, vy, vz glm::vec3 velocity = glm::vec3(0.0f); bool isMoving = false; // 标记球体是否在移动中
2. 修改主循环的输入逻辑
把原来的isHeld改成isPressed(只在点击的第一帧触发),点击一次就启动移动:
void main() { // ... 原来的方块初始化、球体生成代码 ... // 输入处理:点一下空格就启动移动 if(Key_(Space).isPressed) { // 这里用isPressed而不是isHeld! if(!ballEntity.isMoving) { // 设置初始移动方向,比如沿Z轴正方向,速率0.3f // 要是你想根据球体朝向调整,后面我可以给你说怎么改 ballEntity.velocity = glm::vec3(0.0f, 0.0f, 0.3f); ballEntity.isMoving = true; } } // 自主移动逻辑:只要isMoving为true,就按速度更新位置 if(ballEntity.isMoving) { ballEntity.SetPosition( ballEntity.GetX() + ballEntity.velocity.x, ballEntity.GetY() + ballEntity.velocity.y, ballEntity.GetZ() + ballEntity.velocity.z ); // 要是你有Move方法的话,也可以写成ballEntity.Move(ballEntity.velocity); } // ... 原来的碰撞检测循环 ... }
二、实现碰撞反弹的效果
你现在碰撞后直接回初始位置,咱们改成物理反弹的效果,用向量反射公式就行。
1. 修正现有碰撞检测的问题
先给你指出现有代码的两个小错误:
- 指针调用成员函数要加
->,比如block.GetX应该是block->GetX() - 原来的碰撞判断逻辑不太准确,咱们换成更靠谱的「球体与AABB方块」碰撞检测
修改后的collisionDetection函数:
void collisionDetection(Entity* ball, Entity* block) { const float ballRadius = 2.0f; // 假设你的方块是边长10的立方体(原来的blockRadius是5,就是半长) const float blockHalfSize = 5.0f; // 获取球和方块的位置(注意调用成员函数的正确方式) glm::vec3 ballPos = glm::vec3(ball->GetX(), ball->GetY(), ball->GetZ()); glm::vec3 blockPos = glm::vec3(block->GetX(), block->GetY(), block->GetZ()); // 计算方块上离球心最近的点(用clamp限制在方块范围内) glm::vec3 closestPoint; closestPoint.x = clamp(ballPos.x, blockPos.x - blockHalfSize, blockPos.x + blockHalfSize); closestPoint.y = clamp(ballPos.y, blockPos.y - blockHalfSize, blockPos.y + blockHalfSize); closestPoint.z = clamp(ballPos.z, blockPos.z - blockHalfSize, blockPos.z + blockHalfSize); // 计算球心到最近点的距离平方(避免开方,提升性能) glm::vec3 diff = ballPos - closestPoint; float distSq = diff.x*diff.x + diff.y*diff.y + diff.z*diff.z; // 如果距离小于球半径的平方,说明碰撞了 if(distSq < ballRadius * ballRadius) { // 计算碰撞法线(从方块指向球的方向) glm::vec3 normal = diff / sqrt(distSq); // 归一化向量 // 用反射公式计算反弹后的速度:v' = v - 2*(v·n)*n float dotProduct = glm::dot(ball->velocity, normal); ball->velocity = ball->velocity - 2.0f * dotProduct * normal; // 把球移出碰撞区域,防止卡进方块里 float pushDistance = ballRadius - sqrt(distSq); glm::vec3 pushOut = normal * pushDistance; ball->SetPosition(ballPos.x + pushOut.x, ballPos.y + pushOut.y, ballPos.z + pushOut.z); // 可选:给速度加衰减,让球体反弹几次后停下 ball->velocity *= 0.9f; // 当速度太小的时候停止移动 float speed = sqrt(ball->velocity.x*ball->velocity.x + ball->velocity.y*ball->velocity.y + ball->velocity.z*ball->velocity.z); if(speed < 0.01f) { ball->isMoving = false; ball->velocity = glm::vec3(0.0f); } } }
要是你没有glm库,自己实现clamp函数就行:
float clamp(float value, float min, float max) { if(value < min) return min; if(value > max) return max; return value; }
2. 整合到主循环
把原来的碰撞检测循环保留,现在调用这个修改后的函数,碰撞后就会反弹而不是回到初始位置了。
三、额外优化:根据朝向设置初始方向
要是你想让球体朝当前朝向移动,而不是固定Z轴,可以用三角函数计算方向向量:
// 假设球体有个rotationY(Y轴旋转角度,单位弧度) float speed = 0.3f; ballEntity.velocity.x = speed * sin(ballEntity.rotationY); ballEntity.velocity.z = speed * cos(ballEntity.rotationY); ballEntity.velocity.y = 0.0f; // 要是不需要上下移动的话
最后整合的主循环示例
void main() { std::vector<Entity*> blocks; float xPosition = -55; for (int i = 0; i < 10; i++) { blocks.push_back(blockEntity.Spawn(xPosition, 5, 120)); xPosition += 12; } ballEntity.Spawn(0, 2, 0); // 输入处理:点击空格启动移动 if(Key_(Space).isPressed) { if(!ballEntity.isMoving) { // 这里可以换成根据朝向计算的方向向量 ballEntity.velocity = glm::vec3(0.0f, 0.0f, 0.3f); ballEntity.isMoving = true; } } // 自主移动 if(ballEntity.isMoving) { ballEntity.SetPosition( ballEntity.GetX() + ballEntity.velocity.x, ballEntity.GetY() + ballEntity.velocity.y, ballEntity.GetZ() + ballEntity.velocity.z ); } // 碰撞检测与反弹 for (Entity* block : blocks) { collisionDetection(&ballEntity, block); } // ... 你的渲染代码 ... }
内容的提问来源于stack exchange,提问作者mozzarella
相关产品推荐
相关产品推荐

