SDL3 Sprite Motion Blur

18 hours ago 3
ARTICLE AD BOX

How do I remove motion blur? It's only noticeable at frame rates of 60 and below. The lower the frame rate, the more noticeable the blur.

https://www.youtube.com/watch?v=c8zwehj_HNg - this is what the blur looks like.

Entity.h

#pragma once #include <SDL3/SDL.h> class Entity { public: Entity(float x, float y, float w, float h); virtual ~Entity(); void drawTexture(SDL_Renderer *renderer, SDL_Texture *texture, Uint8 textureAlpha); void drawHitBox(SDL_Renderer *renderer, Uint8 R, Uint8 G, Uint8 B, Uint8 hitBoxAlpha); const SDL_FRect &getHitBox() const; protected: SDL_FRect entity = { 0.0f, 0.0f, 0.0f, 0.0f }; float speed = 0.0f; float dirX = 0.0f; float dirY = 0.0f; };

Entity.cpp

#include "Entity.h" Entity::Entity(float x, float y, float w, float h) : entity{ x, y, w, h } { } Entity::~Entity() { } void Entity::drawTexture(SDL_Renderer *renderer, SDL_Texture *texture, Uint8 textureAlpha) { SDL_SetTextureAlphaMod(texture, textureAlpha); SDL_RenderTexture(renderer, texture, nullptr, &entity); } void Entity::drawHitBox(SDL_Renderer *renderer, Uint8 R, Uint8 G, Uint8 B, Uint8 hitBoxAlpha) { SDL_SetRenderDrawColor(renderer, R, G, B, hitBoxAlpha); SDL_RenderRect(renderer, &entity); } const SDL_FRect& Entity::getHitBox() const { return entity; }

Player.h

#pragma once #include "Entity.h" class Player : public Entity { public: Player(float x, float y, float w, float h); ~Player() override; void movement(const bool *keys, float dt); float getX(); float getY(); const SDL_FRect &getInteractionRadius() const; void drawInteractionRadius(SDL_Renderer *renderer, Uint8 R, Uint8 G, Uint8 B, Uint8 interactionRadiusAlpha); private: SDL_FRect interactionRadius = { 0.0f, 0.0f, 0.0f, 0.0f }; };

Player.cpp

#include "Player.h" Player::Player(float x, float y, float w, float h) : Entity(x, y, w, h), interactionRadius{ x / 2.0f - w / 2.0f, y / 2.0f - h / 2.0f, 64.0f, 64.0f } { speed = 200.0f; } Player::~Player() { } void Player::movement(const bool *keys, float dt) { dirX = 0.0f; dirY = 0.0f; speed = 200.0f; if (keys[SDL_SCANCODE_LSHIFT]) { speed = 100.0f; } if (keys[SDL_SCANCODE_W]) { dirY = -1.0f; } if (keys[SDL_SCANCODE_S]) { dirY = 1.0f; } if (keys[SDL_SCANCODE_A]) { dirX = -1.0f; } if (keys[SDL_SCANCODE_D]) { dirX = 1.0f; } const float length = SDL_sqrtf(dirX * dirX + dirY * dirY); if (length > 0) { entity.x += (dirX / length) * speed * dt; entity.y += (dirY / length) * speed * dt; interactionRadius.x = entity.x + (entity.w / 2.0f) - (interactionRadius.w / 2.0f); interactionRadius.y = entity.y + (entity.h / 2.0f) - (interactionRadius.h / 2.0f); } } float Player::getX() { return this->entity.x; } float Player::getY() { return this->entity.y; } const SDL_FRect& Player::getInteractionRadius() const { return interactionRadius; } void Player::drawInteractionRadius(SDL_Renderer *renderer, Uint8 R, Uint8 G, Uint8 B, Uint8 interactionRadiusAlpha) { SDL_SetRenderDrawColor(renderer, R, G, B, interactionRadiusAlpha); SDL_RenderFillRect(renderer, &interactionRadius); }

Game.h

#pragma once #include <SDL3/SDL.h> #include <SDL3_image/SDL_image.h> #include "Entity.h" #include "Player.h" #include "NPC.h" #include "DialogueBox.h" class Game { public: Game(); ~Game(); void update(); void render(); SDL_AppResult events(SDL_Event *event); private: const int WINDOW_W = 640; const int WINDOW_H = 360; SDL_Window *window = nullptr; SDL_Renderer *renderer = nullptr; enum APP_STATE { GAMEPLAY, DIALOGUE }; APP_STATE currentAppState = GAMEPLAY; bool debugMode = false; bool interaction = false; const bool *keys = nullptr; float dt = 0.0f; float ct = 0.0f; float lt = 0.0f; SDL_Texture *playerTexture = nullptr; Player *player = nullptr; NPC *entity = nullptr; Entity *dialogueEntity = nullptr; //float cameraX = 0.0f; //float cameraY = 0.0f; DialogueBox *dialogueBox = nullptr; };

Game.cpp

#include "Game.h" Game::Game() { SDL_Init(SDL_INIT_VIDEO); window = SDL_CreateWindow("test", WINDOW_W, WINDOW_H, NULL); renderer = SDL_CreateRenderer(window, nullptr); SDL_SetRenderVSync(renderer, 1); SDL_SetRenderLogicalPresentation(renderer, WINDOW_W, WINDOW_H, SDL_LOGICAL_PRESENTATION_LETTERBOX); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); keys = SDL_GetKeyboardState(nullptr); playerTexture = IMG_LoadTexture(renderer, "assets/sprites/spr.png"); SDL_SetTextureScaleMode(playerTexture, SDL_SCALEMODE_NEAREST); player = new Player(0.0f, 0.0f, 32.0f, 32.0f); entity = new NPC(150.0f, 150.0f, 32.0f, 32.0f); dialogueEntity = new Entity(270.0f, 130.0f, 100.0f, 100.0f); dialogueBox = new DialogueBox(150.0f / 3.0f, 750.0f / 3.0f, 1620.0f / 3.0f, 300.0f / 3.0f); SDL_Log(SDL_GetRendererName(renderer)); //SDL_Log("%p %p", &renderer, (void *)renderer); } Game::~Game() { delete dialogueBox; dialogueBox = nullptr; delete dialogueEntity; dialogueEntity = nullptr; delete entity; entity = nullptr; delete player; player = nullptr; SDL_DestroyTexture(playerTexture); playerTexture = nullptr; SDL_DestroyRenderer(renderer); renderer = nullptr; SDL_DestroyWindow(window); window = nullptr; } void Game::update() { //SDL_Delay(32); ct = (float)SDL_GetTicks() / 1000.0f; dt = ct - lt; lt = ct; if (!SDL_HasRectIntersectionFloat(&player->getInteractionRadius(), &entity->getHitBox())) { interaction = false; } if (currentAppState == GAMEPLAY) { player->movement(keys, dt); //cameraX = player->getX() - (WINDOW_W / 2.0f) + (32.0f / 2.0f); //cameraY = player->getY() - (WINDOW_H / 2.0f) + (32.0f / 2.0f); } else if (currentAppState == DIALOGUE) { } } void Game::render() { if (currentAppState == GAMEPLAY) { SDL_SetRenderDrawColor(renderer, 0, 0, 30, 255); SDL_RenderClear(renderer); player->drawTexture(renderer, playerTexture, 255); if (debugMode) { player->drawInteractionRadius(renderer, 0, 0, 255, 128); } if (debugMode) { player->drawHitBox(renderer, 0, 255, 0, 255); } entity->drawTexture(renderer, playerTexture, 255); if (debugMode) { entity->drawHitBox(renderer, 255, 255, 255, 255); } if (interaction and SDL_HasRectIntersectionFloat(&player->getInteractionRadius(), &entity->getHitBox())) { entity->interaction(renderer, keys); } } else if (currentAppState == DIALOGUE) { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); dialogueEntity->drawTexture(renderer, playerTexture, 255); if (debugMode) { dialogueEntity->drawHitBox(renderer, 255, 255, 255, 255); } dialogueBox->draw(renderer); } SDL_RenderPresent(renderer); } SDL_AppResult Game::events(SDL_Event *event) { switch (event->type) { case SDL_EVENT_QUIT: return SDL_APP_SUCCESS; break; case SDL_EVENT_KEY_DOWN: switch (event->key.scancode) { case SDL_SCANCODE_1: { currentAppState = GAMEPLAY; SDL_SetRenderLogicalPresentation(renderer, WINDOW_W, WINDOW_H, SDL_LOGICAL_PRESENTATION_LETTERBOX); break; } case SDL_SCANCODE_2: { currentAppState = DIALOGUE; //SDL_SetRenderLogicalPresentation(renderer, 1920, 1080, SDL_LOGICAL_PRESENTATION_LETTERBOX); break; } case SDL_SCANCODE_E: { //SDL_Log("%d", interaction); if (SDL_HasRectIntersectionFloat(&player->getInteractionRadius(), &entity->getHitBox())) { interaction = !interaction; } break; } case SDL_SCANCODE_F3: { debugMode = !debugMode; break; } case SDL_SCANCODE_F11: SDL_WindowFlags flags = SDL_GetWindowFlags(window); if (flags & SDL_WINDOW_FULLSCREEN) { SDL_SetWindowFullscreen(window, false); } else { SDL_SetWindowFullscreen(window, true); } break; } break; } return SDL_APP_CONTINUE; }

main.cpp

#define SDL_MAIN_USE_CALLBACKS 1 #include <SDL3/SDL.h> #include <SDL3/SDL_main.h> #include "Game.h" Game *game = nullptr; SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { game = new Game(); return SDL_APP_CONTINUE; } SDL_AppResult SDL_AppIterate(void *appstate) { game->update(); game->render(); return SDL_APP_CONTINUE; } SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { return game->events(event); } void SDL_AppQuit(void *appstate, SDL_AppResult result) { delete game; game = nullptr; SDL_Quit(); }

DialogueBox.h

#pragma once #include <SDL3/SDL.h> class DialogueBox { public: DialogueBox(float boxX, float boxY, float boxW, float boxH); ~DialogueBox(); void draw(SDL_Renderer *renderer); private: SDL_FRect dialogueInBox = { 0.0f, 0.0f, 0.0f, 0.0f }; SDL_FRect dialogueOutBox = { 0.0f, 0.0f, 0.0f, 0.0f }; };

NPC.h

#pragma once #include "Entity.h" #include "DialogueBox.h" class NPC : public Entity { public: NPC(float x, float y, float w, float h); ~NPC(); void interaction(SDL_Renderer *renderer, const bool *keys); private: DialogueBox dialogueBox; };

NPC.cpp

#include "NPC.h" NPC::NPC(float x, float y, float w, float h) : Entity(x, y, w, h), dialogueBox(x - 16.0f, y - 36.0f, 64.0f, 32.0f) { } NPC::~NPC() { } void NPC::interaction(SDL_Renderer *renderer, const bool *keys) { dialogueBox.draw(renderer); }

Monitor size 2560x1440, refresh rate 180, logical resolution enabled in-game, game window size 640x360, code written in SDL3 version 3.4.2

Read Entire Article