当尝试使用SDL/C++进行与帧无关的移动时,在特定方向上移动缓慢或没有移动

Slow or no movement in specific directions when attempted Frame-Independent Movement with SDL/C++

本文关键字:移动 方向 缓慢 SDL C++      更新时间:2024-09-30

我正在尝试制作一个简单的蛇游戏,并从尝试让一个对象在屏幕上独立于帧速率移动开始。我的问题是,当我将速度设置为负值以在屏幕上向左或向上移动对象时,我会得到移动。然而,当我试图向右或向下移动时,这涉及到正值,我在对象上几乎没有移动。如果我设置的值明显更高,那么是的,物体会左右移动,但我得到的移动速度在正值和负值之间的差异对我来说很奇怪。

这是我的密码。到目前为止,我只尝试过让一个对象移动,所以有一两个奇怪的函数没有定义或需要工作。我只是暂时关心我的运动问题。

Snake.h

#pragma once
#include <vector>
#include <SDL.h>
#include <iostream>
typedef SDL_Rect snakeSegment;
enum orientation { ORIENTATION_LEFT = 0, ORIENTATION_UP = 1, ORIENTATION_RIGHT = 2, 
ORIENTATION_DOWN = 3 };

class Snake
{
private:
snakeSegment head;

std::vector<snakeSegment> segments;
SDL_Surface* snakeSurface;
SDL_Texture* snakeTexture;
const char* filepath = "assets/sprites/snake_body.bmp";

public:

Snake();
void init(SDL_Renderer* renderer);
void move(float delta);
void appendSegment();
void render(SDL_Renderer* renderer);

int orientation;


};

Snake.cpp

#include "Snake.h"

Snake::Snake()
{

} 

void Snake::init(SDL_Renderer *renderer)
{
snakeSurface = SDL_LoadBMP(filepath);
if (snakeSurface == nullptr)
{
throw(SDL_GetError());
}
else {
snakeTexture = SDL_CreateTextureFromSurface(renderer, snakeSurface);
if (snakeTexture == nullptr)
{
SDL_FreeSurface(snakeSurface);
throw(SDL_GetError());
}
SDL_FreeSurface(snakeSurface);
}
SDL_Window* w = SDL_RenderGetWindow(renderer);

int width;
int height;
SDL_GetWindowSize(w, &width, &height);
head.w = 16;
head.h = 32;
head.y = height / 2 - (head.h / 2);
head.x = width / 2 - (head.w / 2);
orientation = ORIENTATION_UP;
}

void Snake::render(SDL_Renderer* renderer)
{
SDL_RenderCopy(renderer, snakeTexture, nullptr, &head);
}

void Snake::move(float delta)
{
float positionYDelta = 0;
float positionXDelta = 0;

switch (orientation) {
case ORIENTATION_UP:
positionYDelta = -3 * delta;
positionXDelta = 0;
break;
case ORIENTATION_DOWN:
positionYDelta = 3 * delta; 
positionXDelta = 0;
break;
case ORIENTATION_RIGHT:
positionYDelta = 0;
positionXDelta = 3 * delta;
break;
case ORIENTATION_LEFT:
positionYDelta = 0;
positionXDelta = -3 * delta;
break;
}
head.y += positionYDelta;
head.x += positionXDelta;

}
void Snake::appendSegment()
{
}

main.cpp:

#include <iostream>
#include <SDL.h>
#include "Snake.h"


int main(int argc, char* args[])
{
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
std::cout << "failed to initialize" << std::endl;
}
SDL_Window* w = SDL_CreateWindow("Snake Clone", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN);
SDL_Renderer* ren = SDL_CreateRenderer(w, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
Snake snake;
try {
snake.init(ren);
}
catch (std::exception e)
{
std::cout << e.what();
}
SDL_Event e;

bool quit = false;
float deltaTime;
float time_seconds = SDL_GetTicks() / 1000.f;

while (!quit)
{
float now;
now = SDL_GetTicks() / 1000.f;
deltaTime = now - time_seconds;
time_seconds = now;
const Uint8* state = SDL_GetKeyboardState(NULL);


while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT)
{
quit = true;
}
if (e.type == SDL_KEYDOWN)
{
if (state[SDL_SCANCODE_W])
{
if (snake.orientation == ORIENTATION_LEFT || snake.orientation == ORIENTATION_RIGHT)
{
snake.orientation = ORIENTATION_UP;
std::cout << "W pressed" << std::endl;
}
}
if (state[SDL_SCANCODE_S])
{
if (snake.orientation == ORIENTATION_LEFT || snake.orientation == ORIENTATION_RIGHT)
{
snake.orientation = ORIENTATION_DOWN;
std::cout << "S pressed" << std::endl;
}
}
if (state[SDL_SCANCODE_D])
{
if (snake.orientation == ORIENTATION_UP || snake.orientation == ORIENTATION_DOWN)
{
snake.orientation = ORIENTATION_RIGHT;
std::cout << "D pressed" << std::endl;
}
}
if (state[SDL_SCANCODE_A])
{
if (snake.orientation == ORIENTATION_UP || snake.orientation == ORIENTATION_DOWN)
{
snake.orientation = ORIENTATION_LEFT;
std::cout << "A pressed" << std::endl;
}
}
}
}
snake.move(deltaTime);

SDL_RenderClear(ren);
snake.render(ren);
SDL_RenderPresent(ren);

}
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(w);
return 0;
}

我在这里所要做的就是让我的精灵以一致的速度向各个方向移动。

您将蛇的位置存储为整数值,但您的移动计算是浮点的。当浮点被分配给整数变量时,它会使用向零取整的约定进行隐式转换。

考虑这个例子:

int x;
float dt = 1.0/60;  // simulating 60 fps
x = 392;
x += 3*dt;          // adding 0.05, and result is converted to int
// what value x have now? Still 392, no matter how many times you add 0.05 to it
x = 392;
x += -3*dt;         // adding -0.05, and result is converted to int
// what value x have now? 391, because implicit cast to integer rounds towards zero

当你每帧移动1个像素时,即使-3*(1.0/60)只是-0.05,你的正向移动不仅没有任何作用,而且负向移动的速度也比指定的速度高得多,而且绝对与帧时间无关。

如何解决这个问题取决于你想要达到什么样的结果。通常蛇游戏以网格步移动蛇;如果这是你想要得到的,你需要在例如浮点变量中积累移动,并且只有当积累的移动高于某个预先指定的网格大小时才执行大的移动步骤。例如

// float accumulated_movement_x, accumulated_movement_y in your snake struct,
// initialised to zero
accumulated_movement_x += positionXDelta;
accumulated_movement_y += positionYDelta;
if(fabs(accumulated_movement_x) >= GRID_STEP_SIZE) {
int int_dx = ((accumulated_movement_x>0) ? 1 : -1) * GRID_STEP_SIZE;
head.x += int_dx;
accumulated_movement_x -= int_dx;
}
if(fabs(accumulated_movement_y) >= GRID_STEP_SIZE) {
int int_dy = ((accumulated_movement_y>0) ? 1 : -1) * GRID_STEP_SIZE;
head.y += int_dy;
accumulated_movement_y -= int_dy;
}

当移动方向改变时,您可能应该将累计移动重置为0,和/或将移动与"移动"勾号挂钩,这取决于您想要得到的结果。

如果你想让你的蛇顺利移动(假设你的游戏逻辑也使用这个值?(,你可以将蛇的位置存储为浮点(例如SDL_FRect或你的自定义结构/变量(。注意浮点错误的累积,但你的屏幕大小不是很大,所以这应该不是问题。

或者您可以将两者结合起来,例如通过移动1像素网格。