← index
libmx2/libmx/pong/pong.cpp
Source: libmx2/libmx/pong/pong.cpp
#include"mx.hpp"

#include"argz.hpp"


#ifdef __EMSCRIPTEN__

#include <emscripten/emscripten.h>

#endif


class Game : public obj::Object {
public:
    Game() = default;
    ~Game() override {}

    constexpr static int tex_width = 640, tex_height = 480;

    virtual void load(mx::mxWindow* win) override {
        bg.loadTexture(win, win->util.getFilePath("data/bg.png"));
        the_font.loadFont(win->util.getFilePath("data/font.ttf"), 14);
        player_paddle = {50, tex_height / 2 - 50, 10, 100};
        ai_paddle = {tex_width - 60, tex_height / 2 - 50, 10, 100};
        ball = {tex_width / 2 - 10, tex_height / 2 - 10, 20, 20};
        ball_vel_x = -5;
        ball_vel_y = -5;
    }

    void update(mx::mxWindow *win) {
        
        if (ai_on) {
            if (ball.y < player_paddle.y + player_paddle.h / 2) {
                player_paddle.y -= paddle_speed;
            } else if (ball.y > player_paddle.y + player_paddle.h / 2) {
                player_paddle.y += paddle_speed;
            }
        } else {
            const Uint8* state = SDL_GetKeyboardState(NULL);     
            if (state[SDL_SCANCODE_UP]) {
                player_paddle.y -= paddle_speed;
            }
            if (state[SDL_SCANCODE_DOWN]) {
                player_paddle.y += paddle_speed;
            }
        }

        if (player_paddle.y < 0)
            player_paddle.y = 0;
        if (player_paddle.y + player_paddle.h > tex_height)
            player_paddle.y = tex_height - player_paddle.h;

        if (ball.y < ai_paddle.y + ai_paddle.h / 2) {
            ai_paddle.y -= paddle_speed;
        } else if (ball.y > ai_paddle.y + ai_paddle.h / 2) {
            ai_paddle.y += paddle_speed;
        }

        if (ai_paddle.y < 0)
            ai_paddle.y = 0;
        if (ai_paddle.y + ai_paddle.h > tex_height)
            ai_paddle.y = tex_height - ai_paddle.h;

        ball.x += ball_vel_x;
        ball.y += ball_vel_y;

        if (ball.y <= 0 || ball.y + ball.h >= tex_height) {
            ball_vel_y = -ball_vel_y;
        }

        if (SDL_HasIntersection(&ball, &player_paddle)) {
            ball_vel_x = -ball_vel_x;
            ball.x = player_paddle.x + player_paddle.w;
        } else if (SDL_HasIntersection(&ball, &ai_paddle)) {
            ball_vel_x = -ball_vel_x;
            ball.x = ai_paddle.x - ball.w;
        }

        if (ball.x <= 0 || ball.x + ball.w >= tex_width) {
            if (ball.x <= 0) {
                player2_score++;
            } else if (ball.x + ball.w >= tex_width) {
                player1_score++;
            }

            ball = {tex_width / 2 - 10, tex_height / 2 - 10, 20, 20};
            ball_vel_x = -ball_vel_x;
        }
    }

    virtual void draw(mx::mxWindow* win) override {
        SDL_RenderCopy(win->renderer, bg.wrapper().unwrap(), nullptr, nullptr);
        static Uint32 last_update_time = SDL_GetTicks();
        Uint32 current_time = SDL_GetTicks();
        Uint32 elapsed_time = current_time - last_update_time;
        if (elapsed_time >= 20) {
            update(win);
            last_update_time = current_time;
        }
        SDL_SetRenderDrawColor(win->renderer, 255, 255, 255, 255); 
        SDL_RenderFillRect(win->renderer, &player_paddle);
        SDL_RenderFillRect(win->renderer, &ai_paddle);
        SDL_RenderFillRect(win->renderer, &ball);

        std::string ai_name;
        if(ai_on == true)
            ai_name = "Ai ";

        win->text.printText_Solid(the_font, 15, 15, ai_name + "Player 1: " + std::to_string(player1_score));
        win->text.printText_Solid(the_font, 15, 35, "Ai Player 2: " + std::to_string(player2_score));
    }

    virtual void event(mx::mxWindow* win, SDL_Event& e) override {
        if(e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SPACE) {
            ai_on = !ai_on;
        }   
    }


private:
    mx::Texture bg;
    mx::Font the_font;
    SDL_Rect player_paddle = {0};
    SDL_Rect ai_paddle = {0};
    SDL_Rect ball = {0};
    int ball_vel_x = 0;
    int ball_vel_y = 0;
    const int paddle_speed = 5;
    int player1_score = 0;
    int player2_score = 0;
    bool ai_on = false;
};

class Intro : public obj::Object {
public:
    Intro() {}
    ~Intro() override {}

    virtual void load(mx::mxWindow *win) override {
        tex.loadTexture(win, win->util.getFilePath("data/logo.png"));
        bg.loadTexture(win, win->util.getFilePath("data/bg.png"));
    }

    virtual void draw(mx::mxWindow *win) override {
        static Uint32 previous_time = SDL_GetTicks();
        Uint32 current_time = SDL_GetTicks();
        static int alpha = 255;
        static bool fading_out = true;
        static bool done = false;

        SDL_RenderCopy(win->renderer, bg.wrapper().unwrap(), nullptr, nullptr);
        SDL_SetTextureAlphaMod(tex.wrapper().unwrap(), alpha);
        SDL_RenderCopy(win->renderer, tex.wrapper().unwrap(), nullptr, nullptr);

        if(done == true) {
            win->setObject(new Game());
            win->object->load(win);
            return;
        }
        
        if (current_time - previous_time >= 15) {
            previous_time = current_time;
            if (fading_out) {
                alpha -= 3;  
                if (alpha <= 0) {
                    alpha = 0;
                    fading_out = false;
                    done = true;
                }
            } else {
                alpha += 3;
                if (alpha >= 255) {
                    alpha = 255;
                    fading_out = true;
                    previous_time = SDL_GetTicks();
                }
            }
        }
    }

    virtual void event(mx::mxWindow *win, SDL_Event &e) override {

    }
private:
    mx::Texture tex;
    mx::Texture bg;
};




class MainWindow : public mx::mxWindow {
public:
    
    MainWindow(std::string path, int tw, int th) : mx::mxWindow("Pong", tw, th, false) {
        tex.createTexture(this, 640, 480);
      	setPath(path);
        setObject(new Intro());
		object->load(this);
    }
    
    ~MainWindow() override {

    }
    
    virtual void event(SDL_Event &e) override {}

    virtual void draw(SDL_Renderer *renderer) override {
        SDL_SetRenderTarget(renderer, tex.wrapper().unwrap());
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);
        // draw

        object->draw(this);
        SDL_SetRenderTarget(renderer,nullptr);
        SDL_RenderCopy(renderer, tex.wrapper().unwrap(), nullptr, nullptr);
        SDL_RenderPresent(renderer);
    }
private:
    mx::Texture tex;
};

MainWindow *main_w = nullptr;

void eventProc() {
    main_w->proc();
}

int main(int argc, char **argv) {
#ifdef __EMSCRIPTEN__

    MainWindow main_window("/", 960, 720);
    main_w =&main_window;
    emscripten_set_main_loop(eventProc, 0, 1);
#else

    Argz<std::string> parser(argc, argv);    
    parser.addOptionSingle('h', "Display help message")
          .addOptionSingleValue('p', "assets path")
          .addOptionDoubleValue('P', "path", "assets path")
          .addOptionSingleValue('r',"Resolution WidthxHeight")
          .addOptionDoubleValue('R',"resolution", "Resolution WidthxHeight");
    Argument<std::string> arg;
    std::string path;
    int value = 0;
    int tw = 960, th = 720;
    try {
        while((value = parser.proc(arg)) != -1) {
            switch(value) {
                case 'h':
                case 'v':
                    parser.help(std::cout);
                    exit(EXIT_SUCCESS);
                    break;
                case 'p':
                case 'P':
                    path = arg.arg_value;
                break;
                case 'r':
                case 'R': {
                    auto pos = arg.arg_value.find("x");
                    if(pos == std::string::npos)  {
                        mx::system_err << "Error invalid resolution use WidthxHeight\n";
                        mx::system_err.flush();
                        exit(EXIT_FAILURE);
                    }
                    std::string left, right;
                    left = arg.arg_value.substr(0, pos);
                    right = arg.arg_value.substr(pos+1);
                    tw = atoi(left.c_str());
                    th = atoi(right.c_str());
                }
                break;

            }
        }
    } catch (const ArgException<std::string>& e) {
        mx::system_err << e.text() << "\n";
    }
    if(path.empty()) {
        mx::system_out << "mx: No path provided trying default current directory.\n";
        path = ".";
    }
    try {
        MainWindow main_window(path, tw, th);
        main_window.loop();
    } catch(const mx::Exception &e) {
        mx::system_err << "mx: Exception: " << e.text() << "\n";
        mx::system_err.flush();
        exit(EXIT_FAILURE);
    } 
#endif

    return 0;
}