3

我觉得这是一个很难说清楚的问题,所以我在这张图上做了说明(我在 C++ 中使用 SDL)。每个方块代表屏幕上的一个像素,我希望红色像素以相同的速度移动而不管方向。

如果速度为 8 像素/秒,则 1 秒后:

  • 如果用户输入正确或向下,像素将到达标记为蓝色的位置
  • 如果用户输入正确且向下,它将到达标记为绿色的位置。

像素

然而,在这两种情况下,像素都移动了 8 个像素。红色和蓝色之间的欧几里得距离 = 8.00,红色和绿色 = 11.31。我希望像素变为黄色。

所以我试图通过声明一个恒定的速度来纠正这个问题,然后我将它除以实际位移,给我一个用于将 X 和 Y 坐标相乘并沿着轨迹返回的数字,从而限制了我的速度。

代码看起来有点像这样(我已经评论了感兴趣的领域):

float velX = 0, velY = 0, currentX, currentY;
int time = 0, speed = 300;

//Events
void handleInput(){
    if( event.type == SDL_KEYDOWN ){
        switch( event.key.keysym.sym ){
            case SDLK_UP: {velY -= speed;} break;
            case SDLK_DOWN: {velY += speed;} break;
            case SDLK_LEFT: {velX -= speed;} break;
            case SDLK_RIGHT: {velX += speed;} break;
        }
    }
    else if( event.type == SDL_KEYUP ){
        //do the opposite
    }
}

//Logic
void move(){

    //float dist = sqrt( (velX*velX) + (velY*velY) );
    //
    //if(dist > 0){
    //        velX *= speed / dist;
    //        velY *= speed / dist;
    //}

    currentX += velX * (get_delta_ticks(&time) / 1000.f);
    currentY += velY * (get_delta_ticks(&time) / 1000.f);
    set_delta_ticks(&time);
}

//Render
void Player::render(){
    apply_surface(currentX, currentY, spriteSheet, screen, &currentClip);
}

所以这是我的问题,我是编程游戏的新手,我不确定这是否是进行运动的正确方式。在方式上似乎有点低效,我是否应该尝试根据角度推断位置和而是斜边的长度?我对三角学不太了解,但我当然很想学习。

4

5 回答 5

1

将逻辑位置与显示位置分开。

逻辑位置可能需要使用浮点坐标,您会将它们四舍五入为显示位置的整数像素坐标。如果你想平滑运动,你甚至可以用它来做抗锯齿。

所以:

  • 权利将有逻辑单位向量(x,y)=(1.0,0.0)
  • down 会有逻辑单位向量(x,y)=(0.0,-1.0)
  • down+right 将有逻辑单位向量(x,y)=(1/sqrt(2),-1/sqrt(2))

每 1/8 秒,将单位向量添加到当前逻辑位置,然后选择要绘制的像素。显然你可以选择不同的单位和更新频率,但这会给出你要求的数字。

于 2013-07-17T12:13:36.813 回答
0

关键是:您计算了 8-x 和 8-y 按键事件,这导致与 v=sqrt(8*8+8*8)=11.31 的原点的距离最短,与您观察到的完全一样。

您应该知道,在您测量的时间内,可能会采样 8 个(仅 x OR y)或 16 个(x 加 y)按键事件,从而产生不同的“速度”,其中 speed=number_of_key_events/period_of_time

如果您想前往“黄色”点,在您对基本方向之一的 8 次按键进行采样的同一时间段内,应该只有 6 个 X 按键事件加上 6 个 Y 按键事件。

因此,您的代码没有任何问题,并且正如其他张贴者所指出的,您的欧几里德速度可以使用欧几里德距离除以采样周期来计算,分别得到 v=8 或 v=11.31。

于 2013-07-17T11:45:20.750 回答
0

我将从不同的用户控件开始:即绝对速度和方向

给定速度 velAbs 和角度 theta,你有

velX = velAbs * cos(theta);
velY = velAbs * sin(theta);

更新位置时,通常最方便的是将绝对速度分解为其 X 和 Y 分量,更新给定时间间隔 dt 的 X 和 Y 位置

currentX = velX * dt;
currentY = velY * dt;

而对于碰撞影响计算,绝对速度更相关。

这将避免您的黄色/绿色问题,因为 X 和 Y 方向的最大油门将使您进入绿色。只需让用户将油门从 0 设置为 8 并设置方向,然后您将获得黄色或蓝色。

于 2013-07-17T11:13:35.967 回答
0

好吧,看起来大多数人都忘记了模拟输入......

无论如何,它应该像这样工作:

velX,velY是 [-1.0..1.0] 范围内的浮点数。在数字输入(键盘、dpad)的情况下,按“左”将 velX 设置为 -1,按“右”将 velX 设置为 1,等等。

但是,如果您使用模拟摇杆,则输入浮点值,其中velX == 1.0对应于模拟摇杆的最右侧位置,velX == -1.0对应于最左侧位置,依此类推。

maxSpeed是最大游戏移动速度,也是浮动的。

考虑到所有这些,您可以像这样计算对象的下一个位置:

void move(){
    float curVelX = velX, curVelY = velY;
    float moveSquared = (curVelX*curVelX + curVelY*curVelY);
    if (moveSquared > 1.0f){
         float d = sqrtf(moveSquared);
         curVelX /= d;
         curVelY /= d;
    }
    currentX += curVelX * maxSpeed * (get_delta_ticks(&time) / 1000.f);
    currentY += curVelY * maxSpeed * (get_delta_ticks(&time) / 1000.f);
    set_delta_ticks(&time);
}

在某些方面似乎有点效率低下,

看,你有一个对象。当你有几十万个时,你就可以开始担心效率了。

我应该尝试根据角度和斜边的长度来推断位置吗?

如果你的物体像鱼雷一样,可以缓慢地向左/向右转和加速/减速(你可以稍微控制一下,让它更快/更慢),那么你可能需要存储运动方向和线性运动速度。

如果您的对象是某种可以向任何方向移动的飞行球或滚动球,那么您应该使用类似于我描述的方法。对 x/y 有单独的速度,并使用 sqrtf 限制最大线速度。

于 2013-07-19T10:34:47.767 回答
0

您需要在 2D 空间中获得速度。要获得它,您必须以两种速度进行 sqrt。

curSpeed = sqrt( ( velX * velX ) + (velY * velY ) );
于 2013-07-17T11:14:25.343 回答