Files
mobile-game-core/main.cpp
2026-04-19 22:14:29 +02:00

354 lines
9.3 KiB
C++

// #define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
#include <SDL3/SDL.h>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_keyboard.h>
#include <SDL3/SDL_thread.h>
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <ctime>
#include <math.h>
#include <GLES3/gl3.h>
#if defined(__ANDROID__)
#define APP_MAIN SDL_main
#define SDL_WINDOW_FLAG SDL_WINDOW_FULLSCREEN
#else
#define APP_MAIN main
#define SDL_WINDOW_FLAG 0
#endif
#define SCREEN_WIDTH 1600.f
#define SCREEN_HEIGHT 720.f
#define BALL_SIZE 50.f
#define PLATE_WIDTH 50.f
#define PLATE_HEIGHT 150.f
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define clamp(x, minV, maxV) max((minV), min((maxV), (x)))
static SDL_Window *window = NULL;
static bool running = true;
static Uint8 color = 0x18;
static Uint64 elapsed = 0;
static float ballVelX = -0.5f;
static float ballVelY = 0.5f;
static float ballSpeed = 600.f;
static float ballAccel = 25.f;
static float opponentSpeed = 1200.f;
static float opponentVelY = 0.f;
const char* vShader = "#version 300 es\n"
"layout (location = 0) in vec2 aPos;\n"
"uniform vec4 uColor;\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 0.0, 1.0);\n"
" color = uColor;\n"
"}";
const char* fShader = "#version 300 es\n"
"precision mediump float;\n"
"out vec4 fragColor;\n"
"in vec4 color;\n"
"void main()\n"
"{\n"
" fragColor = color;\n"
"}";
void computeLetterbox(int windowW, int windowH,
int logicalW, int logicalH,
int* x, int* y, int* w, int* h)
{
float scaleX = (float)windowW / logicalW;
float scaleY = (float)windowH / logicalH;
float scale = (scaleX < scaleY) ? scaleX : scaleY;
*w = (int)(logicalW * scale);
*h = (int)(logicalH * scale);
*x = (windowW - *w) / 2;
*y = (windowH - *h) / 2;
}
struct Rect {
public:
float x;
float y;
float w;
float h;
private:
float vertices[12];
GLuint vao, vbo;
public:
Rect(float x, float y, float w, float h) {
this->x = x;
this->y = y;
this->w = w;
this->h = h;
}
void Init() {
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void Draw() {
float xProj = (x / SCREEN_WIDTH) * 2.0f - 1.0f;
float yProj = 1.0f - (y / SCREEN_HEIGHT) * 2.0f;
float wProj = (w / SCREEN_WIDTH) * 2.0f;
float hProj = (h / SCREEN_HEIGHT) * 2.0f;
vertices[0] = xProj;
vertices[1] = yProj;
vertices[2] = xProj + wProj;
vertices[3] = yProj;
vertices[4] = xProj + wProj;
vertices[5] = yProj - hProj;
vertices[6] = xProj;
vertices[7] = yProj;
vertices[8] = xProj;
vertices[9] = yProj - hProj;
vertices[10] = xProj + wProj;
vertices[11] = yProj - hProj;
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
bool CollidesWith(const Rect& b) {
return (x < b.x + b.w &&
x + w > b.x &&
y < b.y + b.h &&
y + h > b.y);
}
};
static Rect ball(
SCREEN_WIDTH / 2 - BALL_SIZE / 2,
SCREEN_HEIGHT / 2 - BALL_SIZE / 2,
BALL_SIZE,
BALL_SIZE
);
static Rect player(
50.f,
SCREEN_HEIGHT / 2 - PLATE_HEIGHT / 2,
PLATE_WIDTH,
PLATE_HEIGHT
);
static Rect opponent(
SCREEN_WIDTH - PLATE_WIDTH - 50.f,
SCREEN_HEIGHT / 2 - PLATE_HEIGHT / 2,
PLATE_WIDTH,
PLATE_HEIGHT
);
bool paused = true;
extern "C" int APP_MAIN(int argc, char **argv) {
srand(time(NULL));
SDL_SetAppMetadata("Example Renderer Primitives", "1.0", "com.example.renderer-primitives");
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
window = SDL_CreateWindow("examples/renderer/primitives", SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_FLAG | SDL_WINDOW_OPENGL);
if (!window) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_GLContext glContext = SDL_GL_CreateContext(window);
SDL_GL_SetSwapInterval(1);
int success;
char infoLog[512];
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vShader, NULL);
glCompileShader(vertexShader);
// print compile errors if any
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
};
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fShader, NULL);
glCompileShader(fragmentShader);
// print compile errors if any
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
};
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
// print linking errors if any
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(program, 512, NULL, infoLog);
printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
ball.Init();
player.Init();
opponent.Init();
int drawableW, drawableH;
SDL_GetWindowSizeInPixels(window, &drawableW, &drawableH);
#if defined(__ANDROID__)
int vx, vy, vw, vh;
computeLetterbox(drawableW, drawableH,
SCREEN_WIDTH, SCREEN_HEIGHT,
&vx, &vy, &vw, &vh);
glViewport(vx, vy, vw, vh);
#else
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
#endif
glEnable(GL_DEPTH_TEST);
while (running) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) {
running = false;
}
if (!paused) {
if (event.type == SDL_EVENT_MOUSE_MOTION) {
player.y = clamp(event.motion.y - PLATE_HEIGHT / 2, 0, SCREEN_HEIGHT - PLATE_HEIGHT);
} else if (event.type == SDL_EVENT_FINGER_MOTION) {
player.y = clamp((event.tfinger.y * SCREEN_HEIGHT) - PLATE_HEIGHT / 2, 0, SCREEN_HEIGHT - PLATE_HEIGHT);
}
}
if (paused && event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_SPACE) {
paused = false;
ball.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2;
ball.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2;
ballVelX = ((float)rand() / (float)RAND_MAX) * 2.f - 1.f;
ballVelY = ((float)rand() / (float)RAND_MAX) * 2.f - 1.f;
} else if (paused && event.type == SDL_EVENT_FINGER_UP) {
paused = false;
ball.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2;
ball.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2;
ballVelX = (float)rand() / (float)RAND_MAX * 2 - 1;
ballVelY = (float)rand() / (float)RAND_MAX * 2 - 1;
}
}
auto now = SDL_GetTicks();
float dt = now - elapsed;
elapsed = now;
dt /= 1000.f;
if (!paused) {
ball.x += ballSpeed * ballVelX * dt;
ball.y += ballSpeed * ballVelY * dt;
ballSpeed += ballAccel * dt;
if (ball.x + BALL_SIZE >= SCREEN_WIDTH || ball.x <= 0) ballVelX *= -1.f;
if (ball.x <= 0) {
ball.x = 1.f;
paused = true;
}
if (ball.x + BALL_SIZE >= SCREEN_WIDTH) {
ball.x = SCREEN_WIDTH - BALL_SIZE - 1.f;
paused = true;
}
if (ball.y + BALL_SIZE >= SCREEN_HEIGHT || ball.y <= 0) ballVelY *= -1.f;
if (ball.y + BALL_SIZE >= SCREEN_HEIGHT) ball.y = SCREEN_HEIGHT - BALL_SIZE - 1.f;
if (ball.y <= 0) ball.y = 1.f;
if (ball.CollidesWith(player)) {
ball.x = player.x + PLATE_WIDTH + 1.f;
ballVelX *= -1;
}
if (ball.CollidesWith(opponent)) {
ball.x = opponent.x - BALL_SIZE - 1.f;
ballVelX *= -1;
}
opponentVelY = clamp(ball.y - (opponent.y + PLATE_HEIGHT / 2 - BALL_SIZE / 2), -1.f, 1.f);
opponent.y = clamp(opponent.y + opponentSpeed * opponentVelY * dt, 0, SCREEN_HEIGHT - PLATE_HEIGHT);
}
glClearColor(0x18/255.f, 0x18/255.f, 0x18/255.f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw your 3D stuff here
glUseProgram(program);
auto colorLoc = glGetUniformLocation(program, "uColor");
float color[4] = {0.8f, 0.8f, 0.8f, 1.0f};
glUniform4fv(colorLoc, 1, color);
ball.Draw();
player.Draw();
color[0] = 0.8f;
color[1] = 0.2f;
color[2] = 0.25f;
color[3] = 1.0f;
glUniform4fv(colorLoc, 1, color);
opponent.Draw();
SDL_GL_SwapWindow(window);
}
return 0;
}