From 17a8ee9cd442d7d34d4db124949da2ecb7371e76 Mon Sep 17 00:00:00 2001 From: Takumi Hara <69781798+takumihara@users.noreply.github.com> Date: Sun, 12 Nov 2023 20:18:47 -0800 Subject: [PATCH] Copy to non-playground fe (#45) --- web/html/online-pong-v1/index.html | 32 +++ web/html/online-pong-v1/pong.js | 394 +++++++++++++++++++++++++++++ web/nginx.conf | 11 + 3 files changed, 437 insertions(+) create mode 100644 web/html/online-pong-v1/index.html create mode 100644 web/html/online-pong-v1/pong.js diff --git a/web/html/online-pong-v1/index.html b/web/html/online-pong-v1/index.html new file mode 100644 index 00000000..2f8ac7de --- /dev/null +++ b/web/html/online-pong-v1/index.html @@ -0,0 +1,32 @@ + + + + + + Canvas tutorial + + + + + + + +
+ + + +
FPS: 0
+
Speed: 0
+
player1: 0
+
player2: 0
+
+ + + + diff --git a/web/html/online-pong-v1/pong.js b/web/html/online-pong-v1/pong.js new file mode 100644 index 00000000..c4b3d1bb --- /dev/null +++ b/web/html/online-pong-v1/pong.js @@ -0,0 +1,394 @@ +let game; +let keyName = ""; + +const CANVAS_WIDTH = 256; +const CANVAS_HEIGHT = 512; +const PADDLE_WIDTH = 100; +const PADDLE_HEIGHT = 10; +const BALL_RADIUS = 5; +const PADDLE_COLOR = "#000000"; +const BALL_COLOR = "#000000"; +const INITIAL_BALL_SPEED = 10; +const TARGET_FPS = 60; +const TARGET_FRAME_MS = 1000 / TARGET_FPS; + +const socket = io("http://localhost:4242"); +socket.on("connection"); +socket.on("connect", () => { + console.log(`Connected: ${socket.id}`); + + socket.emit("join"); +}); + +socket.on("start", (data) => { + console.log(`Start: ${JSON.stringify(data)}`); + game.start(data); +}); + +socket.on("opponentLeft", () => { + const canvas = document.getElementById("tutorial"); + const ctx = canvas.getContext("2d"); + game = new PongGame(ctx); + game.draw_canvas(); +}); + +socket.on("right", () => { + game.player2.clear(game.ctx); + game.player2.move_left(game.elapsed); + game.player2.draw(game.ctx); +}); + +socket.on("left", () => { + game.player2.clear(game.ctx); + game.player2.move_right(game.elapsed); + game.player2.draw(game.ctx); +}); + +// Key Events +document.addEventListener( + "keydown", + (event) => { + game.keypress[event.key] = true; + }, + false +); + +document.addEventListener( + "keyup", + (event) => { + game.keypress[event.key] = false; + }, + false +); + +const start = () => { + game.start(); + params = { + vx: -game.ball.vx, + vy: -game.ball.vy, + }; + socket.emit("start", params); +}; + +function init() { + const canvas = document.getElementById("tutorial"); + if (canvas.getContext) { + const ctx = canvas.getContext("2d"); + game = new PongGame(ctx); + game.draw_canvas(); + setInterval(game.update, TARGET_FRAME_MS); + } +} + +function clamp(num, min, max) { + return num <= min ? min : num >= max ? max : num; +} + +class Paddle { + constructor(x, y, width, height, color) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.color = color; + } + + clear = (ctx) => { + ctx.clearRect(this.x, this.y, this.width, this.height); + }; + + draw = (ctx) => { + ctx.fillStyle = this.color; + ctx.beginPath(); + ctx.fillRect(this.x, this.y, this.width, this.height); + }; + + move_left = () => { + if (this.x > 0) { + this.x -= (CANVAS_WIDTH / 100) * 3; + this.x = Math.round(this.x); + this.x = clamp(this.x, 0, CANVAS_WIDTH); + } + }; + + move_right = () => { + if (this.x + this.width < CANVAS_WIDTH) { + this.x += (CANVAS_WIDTH / 100) * 3; + this.x = Math.round(this.x); + this.x = clamp(this.x, 0, CANVAS_WIDTH); + } + }; + + collide_with = (ball) => { + // Ball is in the same x-axis + if (ball.x >= this.x && ball.x + ball.radius * 2 <= this.x + this.width) { + // Ball is actually colliding with paddle + if ( + (ball.y <= this.height && this.y == 0) || + (ball.y + ball.radius * 2 >= this.y && this.y != 0) + ) { + return true; + } + } + return false; + }; +} + +class Ball { + constructor(x, y, vx, vy, radius, color) { + this.x = x; + this.y = y; + this.vx = vx; + this.vy = vy; + this.radius = radius; + this.color = color; + } + + clear = (ctx) => { + ctx.clearRect(this.x, this.y, this.radius * 2, this.radius * 2); + }; + + draw = (ctx) => { + ctx.fillStyle = this.color; + ctx.beginPath(); + ctx.fillRect(this.x, this.y, this.radius * 2, this.radius * 2); + }; + + speed = () => { + return Math.sqrt(this.vx * this.vx + this.vy * this.vy); + }; + + // 0.4 ~ 2.5 + generate_random_scale = () => { + // 0.2 ~ 1.0 + let scale = 0.4 + Math.random() * 0.6; + // 50% chance to be 1 / scale + if (Math.random() < 0.5) { + return 1 / scale; + } else { + return scale; + } + }; + + fluctuate_velocity_vector = () => { + let radian = Math.atan2(this.vy, this.vx); + // Add random fluctuation (5 degree) + radian += ((Math.random() - 0.5) * 5 * Math.PI) / 180; + // clamp absolute value of radian to 45 degree + if (Math.abs(radian) <= Math.PI / 2) { + radian = clamp(radian, -Math.PI / 4, Math.PI / 4); + } else if (radian > Math.PI / 2) { + radian = clamp(radian, (Math.PI * 3) / 4, Math.PI); + } else { + radian = clamp(radian, -Math.PI, (-Math.PI * 3) / 4); + } + + let speed = this.speed(); + speed = clamp( + speed * this.generate_random_scale(), + CANVAS_HEIGHT / 100, + CANVAS_HEIGHT / 10 + ); + this.vx = speed * Math.cos(radian); + this.vy = speed * Math.sin(radian); + }; + + reset = () => { + this.x = CANVAS_WIDTH / 2 - this.radius / 2; + this.y = CANVAS_HEIGHT / 2 - this.radius / 2; + this.vx = 0; + this.vy = 0; + }; + + bounce_off_paddle = (paddle) => { + this.y = clamp( + this.y, + paddle.height, + CANVAS_HEIGHT - paddle.height - this.radius * 2 + ); + this.vy = -this.vy; + // this.fluctuate_velocity_vector(); + }; + + collide_with_side = () => { + return this.x < 0 || this.x + this.radius * 2 > CANVAS_WIDTH; + }; + + bounce_off_side = () => { + this.x = clamp(this.x, 0, CANVAS_WIDTH - this.radius * 2); + this.vx = -this.vx; + }; + + move = (elapsed) => { + this.x += (this.vx * elapsed) / TARGET_FRAME_MS; + this.y += (this.vy * elapsed) / TARGET_FRAME_MS; + this.x = Math.round(this.x); + this.y = Math.round(this.y); + }; +} + +class PongGame { + constructor(ctx) { + this.ctx = ctx; + this.ctx.textAlign = "center"; + this.ctx.font = "48px serif"; + this.player1 = new Paddle( + CANVAS_WIDTH / 2 - PADDLE_WIDTH / 2, + CANVAS_HEIGHT - PADDLE_HEIGHT, + PADDLE_WIDTH, + PADDLE_HEIGHT, + PADDLE_COLOR + ); + this.player2 = new Paddle( + CANVAS_WIDTH / 2 - PADDLE_WIDTH / 2, + 0, + PADDLE_WIDTH, + PADDLE_HEIGHT, + PADDLE_COLOR + ); + this.ball = new Ball( + CANVAS_WIDTH / 2 - BALL_RADIUS / 2, + CANVAS_HEIGHT / 2 - BALL_RADIUS / 2, + 0, + 0, + BALL_RADIUS, + BALL_COLOR + ); + this.score = { + player1: 0, + player2: 0, + }; + this.updated_at = undefined; + this.fps_updated_at = undefined; + this.elapsed = 0; + this.frame_count = 0; + this.is_playing = false; + this.keyName = ""; + this.keypress = {}; + } + + update_fps = () => { + this.frame_count++; + if (this.fps_updated_at === undefined) { + this.fps_updated_at = this.updated_at; + } + const elapsed_since_last_update = this.updated_at - this.fps_updated_at; + if (elapsed_since_last_update > 500) { + const fps = Math.round( + this.frame_count / (elapsed_since_last_update / 1000), + 1 + ); + document.getElementById("fps").innerHTML = "FPS: " + fps; + this.frame_count = 0; + this.fps_updated_at = this.updated_at; + } + }; + + update_speed(speed) { + document.getElementById("speed").innerHTML = "Speed: " + Math.round(speed); + } + + update_players() { + document.getElementById("player1").innerHTML = this.player1.x; + document.getElementById("player2").innerHTML = this.player2.x; + } + + draw_canvas = () => { + // Clear objects + this.ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); + + // Draw objects + this.ball.move(this.elapsed); + if (this.player1.collide_with(this.ball)) { + this.ball.bounce_off_paddle(this.player1); + } else if (this.player2.collide_with(this.ball)) { + this.ball.bounce_off_paddle(this.player2); + } else if (this.ball.y <= 0) { + console.log("collide with top"); + this.ball.reset(); + this.score.player2++; + } else if (this.ball.y + this.ball.radius * 2 >= CANVAS_HEIGHT) { + console.log("collide with bottom"); + this.ball.reset(); + this.score.player1++; + } else if (this.ball.collide_with_side()) { + this.ball.bounce_off_side(); + } + this.ball.draw(this.ctx); + this.player1.draw(this.ctx); + this.player2.draw(this.ctx); + this.ctx.fillText(this.score.player1, (CANVAS_WIDTH * 1) / 4, 100); + this.ctx.fillText(this.score.player2, (CANVAS_WIDTH * 3) / 4, 100); + }; + + update = () => { + const now = Date.now(); + this.elapsed = this.updated_at === undefined ? 0 : now - this.updated_at; + this.updated_at = now; + this.update_fps(); + this.update_speed(this.ball.speed()); + this.update_players(); + if (this.keypress["ArrowLeft"]) { + this.player1.clear(this.ctx); + this.player1.move_left(this.elapsed); + this.player1.draw(this.ctx); + socket.emit("left"); + } else if (this.keypress["ArrowRight"]) { + this.player1.clear(this.ctx); + this.player1.move_right(this.elapsed); + this.player1.draw(this.ctx); + socket.emit("right"); + } + if (this.is_playing) { + this.draw_canvas(); + } + }; + + start = ({ vx, vy } = { vx: undefined, vy: undefined }) => { + this.is_playing = true; + if (vx && vy) { + this.ball.vx = vx; + this.ball.vy = vy; + return; + } + // Initialize initial velocity of the ball + while (true) { + let random_radian = Math.random() * 2 * Math.PI; + this.ball.vx = INITIAL_BALL_SPEED * Math.cos(random_radian); + this.ball.vy = INITIAL_BALL_SPEED * Math.sin(random_radian); + if ( + Math.abs(Math.cos(random_radian)) >= 0.5 && + Math.abs(Math.sin(random_radian)) >= 0.5 + ) { + break; + } + } + }; + + stop = () => { + this.updated_at = undefined; + this.ball.vx = 0; + this.ball.vy = 0; + this.is_playing = false; + }; + + switch_battle_mode = () => { + // Make the left player a paddle + this.player2.clear(this.ctx); + this.player2 = new Paddle( + CANVAS_WIDTH / 2 - PADDLE_WIDTH / 2, + 0, + PADDLE_WIDTH, + PADDLE_HEIGHT, + PADDLE_COLOR + ); + this.player2.draw(this.ctx); + }; + + switch_practice_mode = () => { + // Make the left player a wall + this.player2.clear(this.ctx); + this.player2 = new Paddle(0, 0, CANVAS_WIDTH, PADDLE_HEIGHT, PADDLE_COLOR); + this.player2.draw(this.ctx); + }; +} diff --git a/web/nginx.conf b/web/nginx.conf index eccba0d8..61705fd4 100644 --- a/web/nginx.conf +++ b/web/nginx.conf @@ -32,5 +32,16 @@ http { proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; } + location /socket.io { + proxy_pass http://backend:3000; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + } } }