10-单向碰撞检测和重力模拟
1 玩家的水平移动
首先在Player中定义速度跑动速度常量
protected:
const float run_velocity = 0.55f; //跑动速度
之后在on_update方法中在
direction!=0时,计算水平移动的位置float distance = direction * run_velocity * delta;
on_run(distance);
我们希望把玩家跑动的具体逻辑抽象为函数,方便在后续过程中进行统一管理
所以在Player中定义on_run方法来实现移动
virtual void on_run(float distance)
{
position.x += distance;
}
2 重力模拟
在整个游戏场景中的物体始终都在收到重力加速度的牵引,并且有着竖直方向上加速的重力。
我们在Player类中定义重力常量,以及速度矢量。
protected:
const float gravity = 1.6e-3f;
Vector2 velocity;
void move_and_collide(int delta)
{
velocity.y += gravity * delta;
position += velocity * (float)delta;
}
在on_update中调用这个函数
3 单向平台碰撞
由于玩家可以从平台下访跳跃到平台上,所以和平台的碰撞我们只需要在玩家下坠时,也就是玩家速度向下时进行检测就可以了。
由于需要获取场景中所有的平台对象,所以先引入头文件和外部变量
#include "platform.h"
extern std::vector<Platform> platform_list;
if( velocity.y > 0)
{
for(const Platform& platform : platform_list) {
const Platform::CollisionShape &shape = platform.shape;
}
}
这时我们遇到了一个问题,即玩家的碰撞外形还没有被定义。
我们已经定义了玩家位置的二维向量,那么只需要再定义一个size的二维向量,就能实现玩家的矩形碰撞箱了。
先检测水平方向上是否发生碰撞,如果
则说明,两个形状在水平方向上发生了重合(碰撞)

而当碰撞线的y值坐标位于矩形碰撞体的y值范围内,则说明在竖直方向上发生了碰撞

bool is_collide_x = (std::max(position.x + size.x, shape.right) - std::min(position.x, shape.left))
<= (size.x + shape.right - shape.left);
bool is_collide_y = shape.y >= position.y && shape.y <= position.y + size.y;
检测到发生碰撞,我们就要进行后续操作了。即对玩家的坐标进行修正。
我们可以直接把玩家坐标修正到平台上方吗?不可以。让我们考虑下面这种情况:
当玩家向上跳起,但是只有身体的一部分穿过平台,然后开始下落,此时我们检测到它与平台发生碰撞。如果我们把它设置到平台上方,就会看到:刚刚下落的玩家被瞬移到了平台上方,这不是我们想看到的。
为了应对这种情况。我们需要确保只有玩家整个身体都穿过了平台,随后开始下落时,才能判断玩家和平台发生碰撞。所以说我们应该判断上一帧玩家位于平台上方,并且这一帧满足发生碰撞的条件,才会执行坐标修正的逻辑。
if (is_collide_x && is_collide_y) {
float delta_pos_y = velocity.y * delta;
float last_tick_foot_pos_y = position.y - delta_pos_y + size.y;
if(last_tick_foot_pos_y <= shape.y) {
position.y = shape.y - size.y;
velocity.y = 0;
break;
}
}
在完成这部分内容后,我们不要忘记在角色子类的构造函数中,对玩家尺寸size变量的赋值.两种角色的尺寸都如下所示:
size.x = 96, size.y = 96;
为了看到单向碰撞检测的完整效果,我们还需要实现玩家跳跃的逻辑。
首先定义跳跃速度常量
const float jump_velocity = -0.85f; // 跳跃速度
我们类比on_run方法,定义on_jump方法。
virtual void on_jump()
{
velocity.y += jump_velocity;
}
在on_input方法中,读取并处理跳跃按键。在 P1 的 W 按下,以及 P2 的上方向键按下时,调用on_jump()方法
case 'W':
on_jump();
break;
...
case VK_UP:
on_jump();
break;
但是现在,玩家可以“左脚踩右脚”一直跳跃飞上天。所以我们需要给跳跃次数添加限制。我们想让玩家角色重新回到地面上时,才可以在此执行跳跃的逻辑。
为了简单起见,我们可以当竖直方向速度为0时才允许跳跃。
virtual void on_jump()
{
if (velocity.y != 0) {
return;
}
velocity.y += jump_velocity;
}
这只是一个偷懒的做法,并不完美。因为理论上在最顶端时,玩家的竖直速度可能也为0。不过玩家竖直方向的速度与重力有关,有一定跨度,很可能始终无法让竖直速度为0;
评论
如果你已登录 GitHub,就可以直接在这里评论。