我正在使用SDL2和Crystal制作一款基于16位RPG风格的瓷砖游戏。我看到这个问题被问了很多,但即使有了所有的答案,我仍然没有得到我想要的运动。你曾经在SNES上玩过《最终幻想IV》、《最终幻想V》或《最终幻想VI》吗?我在寻找这样的动作。没有对角线,字符总是在一个瓦片上,并且从不停止在两个瓦片之间。
# main game loop
loop do
ticks = Time.monotonic.milliseconds / 1000.0
case event = SDL::Event.poll
when SDL::Event::Keyboard
case event.sym
when .right?
character.move_right(ticks)
end
end
character.draw(renderer)
renderer.present
#other code handling break and stuff omitted
end
# character.cr
VELOCITY = 100
def move_right(delta_ticks)
@direction_facing = "east"
@x += VELOCITY * delta_ticks
end
def draw(renderer)
sprite = @directions[@direction_facing]
renderer.copy(sprite, dstrect: SDL::Rect[@x.to_i, @y.to_i, 64, 64])
end
按照我目前的动作方式,角色开始慢慢走,然后加快速度,然后又回落到慢慢走,就像换挡一样。我知道我的线路@x += VELOCITY * delta_ticks
是错误的,但我没能找到一个符合我要求的线路。这也没有考虑到直接在磁贴上停止(在本例中为64x64(。
编辑:我已经尝试转换@genpfault给出的建议。它仍然不能实现我想要的,但由于我不懂C++,我可能错过了一些东西。代码更新在这里
- 制作一个小的"tasklet"助手(我知道zero关于Crystal;在C++中,我只想让它成为一个包含成员数据和函数的类/结构(,封装角色的当前tile x/y位置(以及精细的子tile x/y位置(
- 当您处理左/右/上/下输入时,请检查当前tasklet是否仍在执行其任务;如果没有,则创建一个具有所需方向的新任务集
- 在小任务处于活动状态时,处理每一帧:递增/递减(1px/帧?由您决定(角色的精细x/y位置,直到它到达目标瓦片位置;如果小任务在本帧中到达目标位置,请将其移除(并更新角色的平铺位置(
这样可以防止新输入在角色运动进行时干扰角色运动,并平滑地设置平铺过渡的动画。
类似这样的东西:
#include <SDL2/SDL.h>
#include <memory>
struct Character
{
int m_TileX;
int m_TileY;
int m_FineX; // in 16ths of a tile
int m_FineY; // in 16ths of a tile
};
class ITask
{
public:
virtual ~ITask() {};
// override & return true to indicate this task is done
virtual bool Run() = 0;
};
class CharacterAnimator : public ITask
{
public:
CharacterAnimator( Character& c, int dx, int dy )
: m_C( c )
, m_Dx( dx )
, m_Dy( dy )
{}
~CharacterAnimator() override {}
bool Run() override
{
m_C.m_FineX += m_Dx;
m_C.m_FineY += m_Dy;
bool done = false;
if( m_C.m_FineX <= -16 ) { m_C.m_TileX--; m_C.m_FineX = 0; done = true; }
if( m_C.m_FineY <= -16 ) { m_C.m_TileY--; m_C.m_FineY = 0; done = true; }
if( m_C.m_FineX >= 16 ) { m_C.m_TileX++; m_C.m_FineX = 0; done = true; }
if( m_C.m_FineY >= 16 ) { m_C.m_TileY++; m_C.m_FineY = 0; done = true; }
return done;
}
private:
Character& m_C;
int m_Dx;
int m_Dy;
};
int main( int argc, char** argv )
{
SDL_Init( SDL_INIT_EVERYTHING );
SDL_Window * window = SDL_CreateWindow
(
"SDL2",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480,
SDL_WINDOW_SHOWN
);
SDL_Renderer* renderer = SDL_CreateRenderer
(
window,
0,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
);
SDL_RenderSetLogicalSize( renderer, 320, 240 );
Character c;
c.m_TileX = 9;
c.m_TileY = 7;
c.m_FineX = 0;
c.m_FineY = 0;
std::unique_ptr< ITask > movementTask;
bool running = true;
while( running )
{
if( movementTask && movementTask->Run() )
{
movementTask.reset();
}
SDL_Event ev;
while( SDL_PollEvent( &ev ) )
{
if ( ev.type == SDL_QUIT )
running = false;
if( ev.type == SDL_KEYUP && ev.key.keysym.sym == SDLK_ESCAPE )
running = false;
if( ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_UP && !movementTask )
movementTask = std::unique_ptr< ITask >( new CharacterAnimator( c, 0, -1 ) );
if( ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_DOWN && !movementTask )
movementTask = std::unique_ptr< ITask >( new CharacterAnimator( c, 0, 1 ) );
if( ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_LEFT && !movementTask )
movementTask = std::unique_ptr< ITask >( new CharacterAnimator( c, -1, 0 ) );
if( ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_RIGHT && !movementTask )
movementTask = std::unique_ptr< ITask >( new CharacterAnimator( c, 1, 0 ) );
}
SDL_SetRenderDrawColor( renderer, 0, 0, 0, 255 );
SDL_RenderClear( renderer );
// draw character
SDL_SetRenderDrawColor( renderer, 255, 0, 0, 255 );
SDL_Rect r =
{
c.m_TileX * 16 + c.m_FineX,
c.m_TileY * 16 + c.m_FineY,
16,
16
};
SDL_RenderFillRect( renderer, &r );
SDL_RenderPresent( renderer );
}
SDL_DestroyRenderer( renderer );
SDL_DestroyWindow( window );
SDL_Quit();
return 0;
}