#define OSH_H_IMPLEMENTATION #include "osh.h" #include #include #define GRAVITY 0.05 #define FLOOR_HEIGHT -0.9 #define JUMP_TIMEOUT 0.14 #define JUMP_FORCE 0.3 #define DRAG 0.98 #define PLAYER_RADIUS 0.02 #define PLAYER_COLOR 1.0, 1.0, 1.0 #define FLOOR_COLOR 0.15, 0.05, 0.05 #define BACKGROUND_COLOR 0.05, 0.05, 0.05, 1.0 void onError(const int error, const char* description) { (void) error; osh::panic("GLFW error:", description); } struct Vector2 { double x, y; Vector2(double x = 0, double y = 0) : x(x), y(y) {} Vector2 conjugate() { return {x, -y}; } Vector2 operator*(double d) { return {x*d, y*d}; } Vector2 operator*=(double d) { x *= d; y *= d; return *this; } Vector2 operator+(Vector2 v) { return {x+v.x, y+v.y}; } Vector2 operator+=(Vector2 v) { x += v.x; y += v.y; return *this; } Vector2 operator-(Vector2 v) { return {x-v.x, y-v.y}; } Vector2 operator-=(Vector2 v) { x -= v.x; y -= v.y; return *this; } }; void print1(osh::Formatter auto& fmt, const Vector2& v) { osh::printp(fmt, '(', v.x, ", ", v.y, ')'); } void onResize(GLFWwindow* window, int width, int height) { glfwGetFramebufferSize(window, &width, &height); glViewport(0, 0, width, height); glLoadIdentity(); glOrtho( -double(width) / height, double(width) / height, -1, 1, 0, 1); } void fillRect(Vector2 pos, Vector2 size) { glBegin(GL_POLYGON); glVertex2d(pos.x, pos.y); glVertex2d(pos.x + size.x, pos.y); glVertex2d(pos.x + size.x, pos.y - size.y); glVertex2d(pos.x, pos.y - size.y); glEnd(); } void drawPoint(Vector2 pos, double radius) { glPointSize(radius); glBegin(GL_POINTS); glVertex2d(pos.x, pos.y); glEnd(); } struct Player { double jumpTimeout = 0; Vector2 pos, vel, acc, size; Player() : size(PLAYER_RADIUS, PLAYER_RADIUS) {} void push(Vector2 force) { acc += force; } void jump() { vel.y = JUMP_FORCE; jumpTimeout = JUMP_TIMEOUT; } void update(double dt) { if (jumpTimeout > 0) { push({0, JUMP_FORCE}); jumpTimeout -= dt; } vel += acc; vel *= DRAG; acc = {0, 0}; pos += vel * dt; pos.y = osh::max(pos.y, FLOOR_HEIGHT + size.y); } void draw() { glColor3d(PLAYER_COLOR); fillRect(pos - size.conjugate(), size*2); glColor3d(1.0, 0, 0); drawPoint(pos, 10); } }; Player player; void onKey(GLFWwindow* window, int key, int scancode, int action, int mods) { (void) window; (void) scancode; (void) mods; if (action == GLFW_PRESS) { if (key == GLFW_KEY_SPACE) { player.jump(); } } } int main(void) { glfwSetErrorCallback(onError); glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); GLFWwindow* window = glfwCreateWindow(640, 480, "HopHop", NULL, NULL); glfwSetKeyCallback(window, onKey); glfwSetWindowSizeCallback(window, onResize); glfwMakeContextCurrent(window); if (glewInit() != GLEW_OK) { osh::panic("glewInit() failed"); } double last = glfwGetTime(); while (!glfwWindowShouldClose(window)) { double now = glfwGetTime(); double dt = now - last; glfwPollEvents(); glClearColor(BACKGROUND_COLOR); glClear(GL_COLOR_BUFFER_BIT); player.push({0, -GRAVITY}); player.update(dt); player.draw(); glColor3d(FLOOR_COLOR); fillRect({ -1, FLOOR_HEIGHT }, {2, 2}); glfwSwapBuffers(window); last = now; } glfwDestroyWindow(window); glfwTerminate(); return 0; }