FreshRSS

Normální zobrazení

Jsou dostupné nové články, klikněte pro obnovení stránky.
PředevčíremHlavní kanál
  • ✇Recent Questions - Game Development Stack Exchange
  • Regarding with pixel arrays and textures in SDL2Scene
    Currently, the way I render my frames is as follows. I have two arrays of pixels (in SDL_PIXELFORMAT_ABGR8888) called frame_pixels (which represents the pixel data for the current frame) and clear_frame_pixels (which is a constant array of black pixels). First I clear the renderer and reset frame_pixels by copying clear_frame_pixels to it. During my draw calls, I write to frame_pixels and then copy the data into a texture called frame_texture (with access SDL_TEXTUREACCESS_STREAMING), which is t
     

Regarding with pixel arrays and textures in SDL2

Currently, the way I render my frames is as follows. I have two arrays of pixels (in SDL_PIXELFORMAT_ABGR8888) called frame_pixels (which represents the pixel data for the current frame) and clear_frame_pixels (which is a constant array of black pixels). First I clear the renderer and reset frame_pixels by copying clear_frame_pixels to it. During my draw calls, I write to frame_pixels and then copy the data into a texture called frame_texture (with access SDL_TEXTUREACCESS_STREAMING), which is then copied onto the renderer and presented.

SDL_RenderClear(renderer)
memcpy(frame_pixels, clear_frame, size_of_frame_pixels);

// Draw functions go here.

// Lock frame texture and copy the pixel array onto it.
unsigned char *locked_pixels;
int pitch;
SDL_LockTexture(frame_texture, NULL, locked, &pitch);
memcpy(locked, frame_pixels, size_of_frame_pixels);
SDL_UnlockTexture(frame_texture);
SDL_RenderCopy(renderer, frame_texture, NULL, NULL);

SDL_Present(renderer);

I have a few questions, the first of which concerns the top 2 lines:

  • Do I need SDL_RenderClear anymore? Without it, my program runs with no difference, but I'm worried that there are underlying side effects that have not hit me yet.
  • Is there a faster way to do this? The reason I'm rendering using textures and pixel arrays is because it is much faster than the standard SDL_RenderDrawX functions. So any further optimization is appreciated.
  • In terms of terminology, would frame_pixels be called a "frame buffer"? What would frame_texture be called, then?
  • ✇Recent Questions - Game Development Stack Exchange
  • How to render a bevel effect with SDL2 Texture?manudicri
    I develop 2D games using CodeBlocks (C++) with SDL2. I can load a texture (ex. a gray square) and render it to the screen. All is fine. If I want to change the color of the square? Yes, I can do it. I can rotate too, without an issue. But here's what I'd like to know: I'd like to render the square border with a bevel effect. This is for displaying icons on the screen: I'd like to avoid having to load 2 images, one for the icon picture (with a transparent background) and one for the bevel; I'
     

How to render a bevel effect with SDL2 Texture?

I develop 2D games using CodeBlocks (C++) with SDL2.

I can load a texture (ex. a gray square) and render it to the screen. All is fine. If I want to change the color of the square? Yes, I can do it. I can rotate too, without an issue.

But here's what I'd like to know:

I'd like to render the square border with a bevel effect. This is for displaying icons on the screen: I'd like to avoid having to load 2 images, one for the icon picture (with a transparent background) and one for the bevel; I'd like to add the bevel effect on some parts, dynamically in the program.

How can I do it?

  • ✇Recent Questions - Game Development Stack Exchange
  • How to stop scenes from running before switching between them?reallySmooll
    So, I've been working on a game dev library using C++ and SDL2 (to test the library out I'm making a Pong clone). And now I'm writing a Scene Manager which is going good, I can switch between scenes no problem but when I first run the game and switch from main menu scene to game scene, the latter is already processing all events (collisions, movement of the ball, scoring, etc.). For example, I'd sit in the main menu for a couple seconds then switch to the game and the ball is in the bottom righ
     

How to stop scenes from running before switching between them?

So, I've been working on a game dev library using C++ and SDL2 (to test the library out I'm making a Pong clone). And now I'm writing a Scene Manager which is going good, I can switch between scenes no problem but when I first run the game and switch from main menu scene to game scene, the latter is already processing all events (collisions, movement of the ball, scoring, etc.).

For example, I'd sit in the main menu for a couple seconds then switch to the game and the ball is in the bottom right corner of the screen (it should be in the center at the beginning) and the score is 2-1 or something.

I thought this had something to do with how I store my scenes so I switched from an unordered_map to a vector but that didn't fix it. I honestly don't know what to do.

Below is the code to the scene manager, scenes and the main file.

Scene Manager header:

#ifndef CC_SCENEMANAGER
#define CC_SCENEMANAGER

#include "CCScene.hpp"

#include <cstdlib>
#include <unordered_map>

namespace cc
{
    class SceneManager
    {
        public:
            ~SceneManager();

            void Add(Scene *scene);
            void Run();

            void GetScene(int id);
        private:
            void GetNextSceneID();

            std::unordered_map<unsigned int, Scene *> mScenes;

            Scene *mCurrentScene = nullptr;

            int mSceneIDs = 0;
            int mActiveSceneID = 0;
            int mNextSceneID = 0;
    };
}

#endif // CC_SCENEMANAGER

Scene Manager source:

#include "CCSceneManager.hpp"
#include "CCKeyboard.hpp"
#include "CCLog.hpp"
#include "CCScene.hpp"
#include "CCWindow.hpp"
#include <SDL2/SDL.h>
#include <SDL2/SDL_render.h>

namespace cc
{
    SceneManager::~SceneManager()
    {
        for (int i = 0; i < mSceneIDs; i++)
        { mScenes[i]->OnDestroy(); }
    }

    void SceneManager::Add(Scene *scene)
    {
        mScenes[mSceneIDs] = scene;
        if (scene->mSceneID == 0)
        {
            scene->mSceneID = mSceneIDs;
            scene->SetNextSceneID();
            scene->OnCreate();
            LOG("sceneid, nextsceneid = ", scene->mSceneID, ", ", scene->mNextSceneID);
        }

        mSceneIDs++;
    }

    void SceneManager::Run()
    {
        bool running = true;

        while (running)
        {
            GetNextSceneID();

            if (mNextSceneID != -1)
            {
                if (mActiveSceneID != mNextSceneID)
                {
                    mActiveSceneID = mNextSceneID;
                    mCurrentScene->SetNextSceneID();
                }

                GetScene(mActiveSceneID);
                if (!mCurrentScene->mStarted)
                {
                    mCurrentScene->Start();
                    mCurrentScene->mStarted = true;
                    mCurrentScene->mQuitting = false;
                }
                else
                {
                    mCurrentScene->HandleInput();

                    mCurrentScene->keyStates = Keyboard::GetKeyboardState();
                    if (!mCurrentScene->handleKeyStatesAlone)
                    { mCurrentScene->HandleKeyStates(); }

                    mCurrentScene->Update();

                    SDL_RenderClear(Window::GetRenderer());

                    mCurrentScene->Render();

                    SDL_RenderPresent(Window::GetRenderer());
                }

                if (mCurrentScene->mQuitting)
                {
                    mCurrentScene->mStarted = false;
                    mCurrentScene->Exit();
                }
            }
            else
            { running = false; }
        }
    }

    void SceneManager::GetScene(int id)
    {
        LOG("id = ", id);
        auto scene = mScenes.find(id);
        if (scene == nullptr)
        {
            ERROR("There is no scene at ID: ", id);
            exit(-1);
        }
        else
        { mCurrentScene = scene->second; }
    }

    void SceneManager::GetNextSceneID()
    {
        if (mCurrentScene != nullptr)
        { mNextSceneID = mCurrentScene->mNextSceneID; }
    }
}

Scene header:

#ifndef CC_GAME
#define CC_GAME

#include <cstdint>

namespace cc
{
    class Scene
    {
        public:
            virtual void OnCreate() { }
            virtual void OnDestroy() { }
            virtual void Start() = 0;
            virtual void Exit() = 0;
            virtual void HandleInput() = 0;
            virtual void HandleKeyStates() = 0;
            virtual void Update() = 0;
            virtual void Render() = 0;

            void SwitchScenes(int id);
            void Quit();

            const uint8_t *keyStates = nullptr;

            bool handleKeyStatesAlone = false;
        private:
            friend class SceneManager;

            void SetNextSceneID();

            bool mStarted = false;
            bool mQuitting = false;

            int mSceneID = 0;
            int mNextSceneID = 0;
    };
}

#endif // CC_GAME

Scene source:

#include "CCScene.hpp"

namespace cc
{
    void Scene::SwitchScenes(int id)
    {
        mNextSceneID = id;
        mQuitting = true;
    }

    void Scene::Quit()
    { SwitchScenes(-1); }

    void Scene::SetNextSceneID()
    { mNextSceneID = mSceneID; }
}

main.cpp:

#include "MainMenu.cpp"
#include "Game.cpp"
#include "Globals.cpp"

int main()
{
    cc::Window *window = cc::Window::GetInstance();
    window->Create("Pong", SCREEN_WIDTH, SCREEN_HEIGHT);

    cc::SceneManager sm;

    MainMenuScene mainMenu;
    sm.Add(&mainMenu);

    GameScene game;
    sm.Add(&game);

    sm.Run();

    return 0;
}

Here's also the code for the individual scenes (main menu, game) if it's relevant:

Main Menu:

#ifndef MAIN_MENU
#define MAIN_MENU

#include "Globals.cpp"

struct MainMenuScene : public cc::Scene
{
    cc::Text mainMenuText;

    Gamevars *gv= Gamevars::GetInstance();

    cc::Window *window = cc::Window::GetInstance();

    void OnCreate() override
    {
        gv->fontSmall.Create("./resources/fonts/pixel-coleco-font/PixelColeco.ttf", 36);
        gv->fontBig.Create("./resources/fonts/pixel-coleco-font/PixelColeco.ttf", 72);

        gv->DebugCreate();
    }

    void Start() override
    { mainMenuText.Create(window->GetRenderer(), "PONG", gv->fontBig.font); }

    void Exit() override
    { mainMenuText.Destroy(); }

    void OnDestroy() override
    {
        gv->fontSmall.Destroy();
        gv->fontBig.Destroy();
    }

    void HandleInput() override
    {
        while (cc::Event::PollEvent(&gv->e))
        {
            if (gv->e.type == cc::Event::CC_QUIT)
            { Quit(); }
            if (gv->e.type == cc::Event::CC_KEYDOWN)
            {
                switch (gv->e.key.keysym.sym)
                {
                    case cc::Keyboard::CCK_ESCAPE:
                        Quit();
                        break;

                    case cc::Keyboard::CCK_RETURN:
                        SwitchScenes(1);
                        break;

                    case cc::Keyboard::CCK_F:
                        window->SetFullscreenOrWindowed();
                        break;

                    case cc::Keyboard::CCK_F3:
                        gv->showDebug = !gv->showDebug;
                        break;

                    case cc::Keyboard::CCK_V:
                        window->EnableVSync();
                        break;

                    default:
                        break;
                }
            }
        }
    }

    void HandleKeyStates() override {}

    void Update() override
    {
        //gv->gTime.UpdateClock();

        gv->DebugUpdate();
    }

    void Render() override
    {
        mainMenuText.Render();

        gv->DebugRender();
    }
};

#endif // MAIN_MENU

Game:

#ifndef GAME
#define GAME

#include "Globals.cpp"

#include <algorithm>
#include <cmath>
#include <string>

struct Ball
{
    cc::Texture texture;

    int posX = 0;
    int posY = 0;

    float baseSpeed = 500.0f;

    float velX = baseSpeed;
    float velY = baseSpeed;

    float centerX = 0.0f;
    float centerY = 0.0f;

    bool moveVertically = false;
};

struct Player
{
    cc::Texture texture;

    int posX = 0;
    int posY = 0;

    int score = 0;

    float velocity = 0.0f;
};

struct Enemy
{
    cc::Texture texture;

    int posX = 0;
    int posY = 0;

    int score = 0;

    float velocity = 0.0f;
};

class GameScene : public cc::Scene {
    Gamevars *gv= Gamevars::GetInstance();

    cc::Text playerScoreText;
    int pTextPos = SCREEN_WIDTH / 4;

    cc::Text enemyScoreText;
    int eTextPos = SCREEN_WIDTH / 4 + SCREEN_WIDTH / 2;

    cc::Text winText;
    int wTextPosX = 0;
    int wTextPosY = 0;

    cc::Text restartText;
    int rTextPosX = 0;
    int rTextPosY = SCREEN_HEIGHT / 4 + SCREEN_HEIGHT / 2;

    Ball ball;
    Player player;
    Enemy enemy;

    float minForceFactor = 0.5f;
    float maxForceFactor = 1.0f;
    float speedMultiplier = 3.5f;

    bool playerServing = false;
    bool gameOver = false;

    int maxScore = 11;
    int consecutiveServes = 1;

    cc::Window *window = cc::Window::GetInstance();

    void HandleBallCollisions(Ball &ball, Player &player, Enemy &enemy)
    {
        cc::Texture paddle;

        ball.centerX = ball.texture.textureQuad.x + ball.texture.textureQuad.w;
        ball.centerY = ball.texture.textureQuad.y + ball.texture.textureQuad.h;

        bool collisionPaddle = (ball.centerX < (SCREEN_WIDTH / 2.0f));

        paddle.textureQuad = (collisionPaddle ? player.texture.textureQuad : enemy.texture.textureQuad);

        if (ball.centerX >= paddle.textureQuad.x &&
            ball.centerX <= paddle.textureQuad.x + paddle.textureQuad.w &&
            ball.centerY >= paddle.textureQuad.y &&
            ball.centerY <= paddle.textureQuad.y + paddle.textureQuad.h)
        {
            ball.moveVertically = true;

            float relativeY = ball.centerY - (paddle.textureQuad.y + paddle.textureQuad.h / 2.0f);

            float forceFactor = std::lerp(minForceFactor, maxForceFactor,
                                          (relativeY + paddle.textureQuad.h / 2.0f) / paddle.textureQuad.h);
            float speedFactor = std::abs((collisionPaddle ? player.velocity : enemy.velocity) * gv->timeStep);

            forceFactor *= speedFactor;

            ball.velX *= -1.0f;

            if (collisionPaddle)
            {
                if (ball.velY == 0.0f)
                { ball.velY = -ball.baseSpeed; }
                else if (ball.velY != 0.0f && std::abs(player.velocity) > 0.01f)
                { ball.velY = std::clamp(player.velocity * gv->timeStep, -ball.baseSpeed, ball.baseSpeed) * forceFactor * speedMultiplier; }
            }
            else
            {
                if (ball.velY == 0.0f)
                { ball.velY = -ball.baseSpeed; }
                else if (ball.velY != 0.0f && std::abs(enemy.velocity) > 0.01f)
                { ball.velY = std::clamp(enemy.velocity * gv->timeStep, -ball.baseSpeed, ball.baseSpeed) * forceFactor * speedMultiplier; }
            }
        }

        if (ball.texture.textureQuad.y + ball.texture.textureQuad.h <= 0.0f)
        { ball.velY = -ball.velY; }
        else if (ball.texture.textureQuad.y + ball.texture.textureQuad.h >= SCREEN_HEIGHT)
        { ball.velY = -ball.velY; }

        if (ball.texture.textureQuad.x + ball.texture.textureQuad.w <= 0.0f)
        {
            enemy.score++;

            enemyScoreText.Update(std::to_string(enemy.score).c_str(), gv->fontBig.font, { 255, 255, 255 }, eTextPos, 0);

            ball.texture.SetPosition(ball.posX, ball.posY);
            ball.moveVertically = false;

            HandleServing(ball, player, enemy);
        }
        else if (ball.texture.textureQuad.x + ball.texture.textureQuad.w >= SCREEN_WIDTH)
        {
            player.score++;

            playerScoreText.Update(std::to_string(player.score).c_str(), gv->fontBig.font, { 255, 255, 255 }, pTextPos, 0);

            ball.texture.SetPosition(ball.posX, ball.posY);
            ball.moveVertically = false;

            HandleServing(ball, player, enemy);
        }

        LOG("This isn't supposed to be printed.");

        ball.texture.textureQuad.x += ball.velX * gv->timeStep;
        ball.texture.textureQuad.y += (ball.moveVertically ? ball.velY : 0.0f) * gv->timeStep;
    }

    void HandleServing(Ball &ball, Player &player, Enemy &enemy)
    {
        if (consecutiveServes == 2 && player.score + enemy.score < 20)
        {
            consecutiveServes = 1;
            playerServing = !playerServing;
        }
        else if (consecutiveServes >= 0 && player.score + enemy.score >= 20)
        {
            consecutiveServes = 0;
            playerServing = !playerServing;
        }
        else
        { consecutiveServes++; }

        ball.velX = playerServing ? -ball.baseSpeed : ball.baseSpeed;
    }

    void HandleScore(Ball &ball, Player &player, Enemy &enemy)
    {
        if (std::abs(player.score - enemy.score) >= 2)
        {
            if (player.score >= maxScore && player.score > enemy.score)
            {
                gameOver = true;

                winText.Update("PLAYER 1 WINS!", gv->fontBig.font, { 255, 255, 255 }, wTextPosX, wTextPosY);
                wTextPosX = SCREEN_WIDTH / 2 - winText.TEXT_CENTER_W;
                wTextPosY = SCREEN_HEIGHT / 2 - winText.TEXT_CENTER_H;
                winText.SetX(wTextPosX);
                winText.SetY(wTextPosY);

                restartText.Update("PRESS R TO RESTART", gv->fontSmall.font, { 255, 255, 255 }, rTextPosX, rTextPosY);
                rTextPosX = SCREEN_WIDTH / 2 - restartText.TEXT_CENTER_W;
                restartText.SetX(rTextPosX);
                restartText.SetY(rTextPosY);

                ball.velX = 0.0f;
                gv->speed = 0.0f;

                ball.texture.SetAlpha(0);
                player.texture.SetAlpha(0);
                enemy.texture.SetAlpha(0);

                player.texture.SetPosition(player.posX, player.posY);
                enemy.texture.SetPosition(enemy.posX, enemy.posY);

                playerScoreText.Update("", gv->fontBig.font, { 0, 0, 0 }, -100, -100);
                enemyScoreText.Update("", gv->fontBig.font, { 0, 0, 0 }, -100, -100);
            }
            else if (enemy.score >= maxScore && enemy.score > player.score)
            {
                gameOver = true;

                winText.Update("PLAYER 2 WINS!", gv->fontBig.font, { 255, 255, 255 }, wTextPosX, wTextPosY);
                wTextPosX = SCREEN_WIDTH / 2 - winText.TEXT_CENTER_W;
                wTextPosY = SCREEN_HEIGHT / 2 - winText.TEXT_CENTER_H;
                winText.SetX(wTextPosX);
                winText.SetY(wTextPosY);

                restartText.Update("PRESS R TO RESTART", gv->fontSmall.font, { 255, 255, 255 }, rTextPosX, rTextPosY);
                rTextPosX = SCREEN_WIDTH / 2 - restartText.TEXT_CENTER_W;
                restartText.SetX(rTextPosX);
                restartText.SetY(rTextPosY);

                ball.velX = 0.0f;
                gv->speed = 0.0f;

                ball.texture.SetAlpha(0);
                player.texture.SetAlpha(0);
                enemy.texture.SetAlpha(0);

                player.texture.SetPosition(player.posX, player.posY);
                enemy.texture.SetPosition(enemy.posX, enemy.posY);

                playerScoreText.Update("", gv->fontBig.font, { 0, 0, 0 }, -100, -100);
                enemyScoreText.Update("", gv->fontBig.font, { 0, 0, 0 }, -100, -100);
            }
        }
    }

    void Start() override
    {
        ball.texture.Create(window->GetRenderer(), "./resources/textures/ball.png");
        ball.posX = SCREEN_WIDTH / 2 - ball.texture.textureCenterX;
        ball.posY = SCREEN_HEIGHT / 2 - ball.texture.textureCenterY;
        ball.texture.SetPosition(ball.posX, ball.posY);
        ball.texture.SetColor({ 255, 0, 0 });

        player.texture.Create(window->GetRenderer(), "./resources/textures/paddle.png");
        player.posX = 0;
        player.posY = SCREEN_HEIGHT / 2 - player.texture.textureCenterY;
        player.texture.SetPosition(player.posX, player.posY);

        enemy.texture.Create(window->GetRenderer(), "./resources/textures/paddle.png");
        enemy.posX = SCREEN_WIDTH - enemy.texture.textureQuad.w;
        enemy.posY = SCREEN_HEIGHT / 2 - enemy.texture.textureCenterY;
        enemy.texture.SetPosition(enemy.posX, enemy.posY);

        playerScoreText.Create(window->GetRenderer(), std::to_string(player.score).c_str(), gv->fontBig.font, { 255, 255, 255 }, pTextPos, 0, playerScoreText.CENTER);
        enemyScoreText.Create(window->GetRenderer(), std::to_string(enemy.score).c_str(), gv->fontBig.font, { 255, 255, 255 }, eTextPos, 0, playerScoreText.CENTER);

        winText.Create(window->GetRenderer(), "WINS!", gv->fontBig.font);

        restartText.Create(window->GetRenderer(), "PRESS R TO RESTART", gv->fontSmall.font);

        handleKeyStatesAlone = true;
    }

    void Exit() override
    {
        ball.texture.Destroy();
        player.texture.Destroy();
        enemy.texture.Destroy();

        playerScoreText.Destroy();
        enemyScoreText.Destroy();

        winText.Destroy();

        restartText.Destroy();
    }

    void HandleInput() override
    {
        while (cc::Event::PollEvent(&gv->e))
        {
            if (gv->e.type == cc::Event::CC_QUIT)
            { Quit(); }
            if (gv->e.type == cc::Event::CC_KEYDOWN)
            {
                switch (gv->e.key.keysym.sym)
                {
                    case cc::Keyboard::CCK_ESCAPE:
                        SwitchScenes(0);
                        break;

                    case cc::Keyboard::CCK_F:
                        window->SetFullscreenOrWindowed();
                        break;

                    case cc::Keyboard::CCK_F3:
                        gv->showDebug = !gv->showDebug;
                        break;

                    case cc::Keyboard::CCK_V:
                        window->EnableVSync();
                        break;

                    case cc::Keyboard::CCK_R:
                        player.score = 0;
                        enemy.score = 0;

                        ball.velX = ball.baseSpeed;
                        gv->speed = 800.0f;

                        playerScoreText.Update(std::to_string(player.score).c_str(), gv->fontBig.font, { 255, 255, 255 }, pTextPos, 0);
                        enemyScoreText.Update(std::to_string(enemy.score).c_str(), gv->fontBig.font, { 255, 255, 255 }, eTextPos, 0);

                        ball.texture.SetAlpha(255);
                        player.texture.SetAlpha(255);
                        enemy.texture.SetAlpha(255);

                        gameOver = false;
                        break;

                    default:
                        break;
                }
            }
        }
    }

    void HandleKeyStates() override
    {
        if (keyStates[cc::Scancode::CCS_W] && !keyStates[cc::Scancode::CCS_S])
        {
            if (player.texture.textureQuad.y <= 0)
            { player.texture.textureQuad.y = 0; }
            else
            { player.velocity = -gv->speed; }
        }
        if (keyStates[cc::Scancode::CCS_S] && !keyStates[cc::Scancode::CCS_W])
        {
            if (player.texture.textureQuad.y >= SCREEN_HEIGHT - player.texture.textureQuad.h)
            { player.texture.textureQuad.y = SCREEN_HEIGHT - player.texture.textureQuad.h; }
            else
            { player.velocity = gv->speed; }
        }
        if (keyStates[cc::Scancode::CCS_UP] && !keyStates[cc::Scancode::CCS_DOWN])
        {
            if (enemy.texture.textureQuad.y <= 0)
            { enemy.texture.textureQuad.y = 0; }
            else
            { enemy.velocity = -gv->speed; }
        }
        if (keyStates[cc::Scancode::CCS_DOWN] && !keyStates[cc::Scancode::CCS_UP])
        {
            if (enemy.texture.textureQuad.y >= SCREEN_HEIGHT - enemy.texture.textureQuad.h)
            { enemy.texture.textureQuad.y = SCREEN_HEIGHT - enemy.texture.textureQuad.h; }
            else
            { enemy.velocity = gv->speed; }
        }
    }

    void Update() override
    {
        gv->gTime.UpdateClock();

        gv->accumulatedTime += gv->gTime.GetDeltaTime();

        while (gv->accumulatedTime >= gv->timeStep)
        {
            player.velocity = 0.0f;
            enemy.velocity = 0.0f;

            HandleKeyStates();

            player.texture.textureQuad.y += player.velocity * gv->timeStep;
            enemy.texture.textureQuad.y += enemy.velocity * gv->timeStep;

            HandleBallCollisions(ball, player, enemy);

            HandleScore(ball, player, enemy);

            gv->accumulatedTime -= gv->timeStep;
        }

        gv->DebugUpdate();
    }

    void Render() override
    {
        ball.texture.Render();

        player.texture.Render();
        enemy.texture.Render();

        playerScoreText.Render();
        enemyScoreText.Render();

        if (gameOver)
        {
            winText.Render();
            restartText.Render();
        }

        gv->DebugRender();
    }
};

#endif // GAME

Any help (even a point in the right direction to solving this problem) is greatly appreciated.

❌
❌