From 59c7ae2a165e917a6ad89c761d0dcde012700c25 Mon Sep 17 00:00:00 2001 From: LuckyLaszlo Date: Fri, 2 Dec 2022 21:01:06 +0100 Subject: [PATCH] added alternate game mode "movingWalls" --- memo.txt | 4 +- src/client/class/GameComponentsClient.ts | 56 ++++++++++++++++++++---- src/client/constants.ts | 5 ++- src/client/draw.ts | 2 +- src/client/gameLoop.ts | 5 +++ src/client/ws.ts | 5 +++ src/server/class/GameSession.ts | 11 ++++- src/shared_js/class/Event.ts | 6 +++ src/shared_js/class/GameComponents.ts | 31 +++++++++---- src/shared_js/constants.ts | 3 ++ src/shared_js/enums.ts | 3 +- src/shared_js/utils.ts | 9 +++- src/shared_js/wallsMovement.ts | 20 +++++++++ 13 files changed, 136 insertions(+), 24 deletions(-) create mode 100644 src/shared_js/wallsMovement.ts diff --git a/memo.txt b/memo.txt index ff985e79..882a3339 100644 --- a/memo.txt +++ b/memo.txt @@ -13,15 +13,15 @@ Done: (BUG désynchronisation: revenu à un traitement immédiat en attendant) - Détruire les GameSession une fois finies. - mode multi-balles + - mode murs mouvant (la zone de jeu rétréci / agrandi en continu) TODO: - mode spectateur -- un ou deux modes de jeu alternatif. - certaines utilisations de Math.floor() superflu ? Vérifier les appels. (éventuellement Math.round() ?) +- un autre mode de jeu alternatif ? ----------- idées modes de jeu : - - mode murs mouvant (la zone de jeu rétréci / agrandi en continu) - mode 2 raquettes (un joueur haut/gauche et bas/droite) - skin patate ??? - (prediction de l'avancement de la balle basé sur la latence serveur ?) diff --git a/src/client/class/GameComponentsClient.ts b/src/client/class/GameComponentsClient.ts index 2ab6aefe..00fce3c1 100644 --- a/src/client/class/GameComponentsClient.ts +++ b/src/client/class/GameComponentsClient.ts @@ -3,28 +3,68 @@ import * as c from "../constants.js" import * as en from "../../shared_js/enums.js" import { Vector, VectorInteger } from "../../shared_js/class/Vector.js"; import { TextElem, TextNumericValue } from "./Text.js"; -import { RectangleClient, RacketClient, BallClient, Line } from "./RectangleClient.js"; +import { RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line } from "./RectangleClient.js"; import { GameComponents } from "../../shared_js/class/GameComponents.js"; +import { assertMovingRectangle } from "../utils.js"; class GameComponentsExtensionForClient extends GameComponents { - wallTop: RectangleClient; - wallBottom: RectangleClient; + wallTop: RectangleClient | MovingRectangleClient; + wallBottom: RectangleClient | MovingRectangleClient; playerLeft: RacketClient; playerRight: RacketClient; ballsArr: BallClient[]; constructor(options: en.MatchOptions, ctx: CanvasRenderingContext2D) { super(options); - this.wallTop = new RectangleClient(this.wallTop.pos, this.wallTop.width, this.wallTop.height, ctx, "grey"); - this.wallBottom = new RectangleClient(this.wallBottom.pos, this.wallBottom.width, this.wallBottom.height, ctx, "grey"); - this.playerLeft = new RacketClient(this.playerLeft.pos, this.playerLeft.width, this.playerLeft.height, this.playerLeft.baseSpeed, ctx, "white"); - this.playerRight = new RacketClient(this.playerRight.pos, this.playerRight.width, this.playerRight.height, this.playerRight.baseSpeed, ctx, "white"); + // Rackets + this.playerLeft = new RacketClient( + this.playerLeft.pos, this.playerLeft.width, this.playerLeft.height, this.playerLeft.baseSpeed, + ctx, "white"); + this.playerRight = new RacketClient( + this.playerRight.pos, this.playerRight.width, this.playerRight.height, this.playerRight.baseSpeed, + ctx, "white"); + + // Balls const newBallsArr: BallClient[] = []; this.ballsArr.forEach((ball) => { - newBallsArr.push(new BallClient(ball.pos, ball.width, ball.baseSpeed, ball.speedIncrease, ctx, "white")); + newBallsArr.push(new BallClient( + ball.pos, ball.width, ball.baseSpeed, ball.speedIncrease, + ctx, "white") + ); }); this.ballsArr = newBallsArr; + + // Walls + if (options & en.MatchOptions.movingWalls) + { + const dir = new Vector; + + assertMovingRectangle(this.wallTop); + dir.assign(this.wallTop.dir.x, this.wallTop.dir.y); + this.wallTop = new MovingRectangleClient( + this.wallTop.pos, this.wallTop.width, this.wallTop.height, + this.wallTop.baseSpeed, + ctx, "grey"); + (this.wallTop).dir.assign(dir.x, dir.y); + + assertMovingRectangle(this.wallBottom); + dir.assign(this.wallBottom.dir.x, this.wallBottom.dir.y); + this.wallBottom = new MovingRectangleClient( + this.wallBottom.pos, this.wallBottom.width, this.wallBottom.height, + this.wallBottom.baseSpeed, + ctx, "grey"); + (this.wallBottom).dir.assign(dir.x, dir.y); + } + else + { + this.wallTop = new RectangleClient( + this.wallTop.pos, this.wallTop.width, this.wallTop.height, + ctx, "grey"); + this.wallBottom = new RectangleClient( + this.wallBottom.pos, this.wallBottom.width, this.wallBottom.height, + ctx, "grey"); + } } } diff --git a/src/client/constants.ts b/src/client/constants.ts index 90453470..786d0f99 100644 --- a/src/client/constants.ts +++ b/src/client/constants.ts @@ -20,4 +20,7 @@ export const soundPongVolume = 0.3; // between 0 and 1 // TODO: replace by a selector on the website import * as en from "../shared_js/enums.js" -export const optionsPLACEHOLDER = en.MatchOptions.noOption | en.MatchOptions.multiBalls; +export const optionsPLACEHOLDER = en.MatchOptions.noOption; +// export const optionsPLACEHOLDER = en.MatchOptions.multiBalls; +// export const optionsPLACEHOLDER = en.MatchOptions.movingWalls; +// export const optionsPLACEHOLDER = en.MatchOptions.movingWalls | en.MatchOptions.multiBalls; diff --git a/src/client/draw.ts b/src/client/draw.ts index 5d87e7a6..984c2acb 100644 --- a/src/client/draw.ts +++ b/src/client/draw.ts @@ -32,9 +32,9 @@ function drawDynamic() function drawStatic() { + gc.midLine.update(); gc.wallTop.update(); gc.wallBottom.update(); - gc.midLine.update(); } function drawGrid() diff --git a/src/client/gameLoop.ts b/src/client/gameLoop.ts index 76773e58..b911641a 100644 --- a/src/client/gameLoop.ts +++ b/src/client/gameLoop.ts @@ -2,6 +2,7 @@ import * as c from "./constants.js"; import * as en from "../shared_js/enums.js" import { gc, clientInfo } from "./global.js"; +import { wallsMovements } from "../shared_js/wallsMovement.js"; let actual_time: number = Date.now(); let last_time: number; @@ -26,6 +27,10 @@ function gameLoop() gc.ballsArr.forEach((ball) => { ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]); }); + + if (c.optionsPLACEHOLDER & en.MatchOptions.movingWalls) { + wallsMovements(delta_time, gc); + } } function opponentInterpolation(delta: number) diff --git a/src/client/ws.ts b/src/client/ws.ts index aea26ed0..fd97756c 100644 --- a/src/client/ws.ts +++ b/src/client/ws.ts @@ -90,6 +90,11 @@ function gameUpdate(data: ev.EventGameUpdate) { console.log("gameUpdate"); + if (c.optionsPLACEHOLDER & en.MatchOptions.movingWalls) { + gc.wallTop.pos.y = data.wallTop.y; + gc.wallBottom.pos.y = data.wallBottom.y; + } + data.ballsArr.forEach((ball, i) => { gc.ballsArr[i].pos.assign(ball.x, ball.y); gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY); diff --git a/src/server/class/GameSession.ts b/src/server/class/GameSession.ts index f25f11d4..93cc544a 100644 --- a/src/server/class/GameSession.ts +++ b/src/server/class/GameSession.ts @@ -7,6 +7,7 @@ import { GameComponentsServer } from "./GameComponentsServer.js"; import { clientInputListener } from "../wsServer.js"; import { random } from "../utils.js"; import { Ball } from "../../shared_js/class/Rectangle.js"; +import { wallsMovements } from "../../shared_js/wallsMovement.js"; /* Arg "s: GameSession" replace "this: GameSession" for use with setTimeout(), @@ -95,6 +96,10 @@ class GameSession { gc.ballsArr.forEach((ball) => { s._ballMovement(s.delta_time, ball); }); + + if (s.matchOptions & en.MatchOptions.movingWalls) { + wallsMovements(s.delta_time, gc); + } } private _ballMovement(delta: number, ball: Ball) { const gc = this.components; @@ -133,6 +138,10 @@ class GameSession { speed: ball.speed }); }); + if (s.matchOptions & en.MatchOptions.movingWalls) { + update.wallTop.y = gc.wallTop.pos.y; + update.wallBottom.y = gc.wallBottom.pos.y; + } s.playersMap.forEach( (client) => { update.lastInputId = client.lastInputId; @@ -152,7 +161,7 @@ class GameSession { } } ball.pos.x = c.w_mid; - ball.pos.y = Math.floor((c.h * 0.1) + random() * (c.h * 0.8)); + ball.pos.y = random(c.h*0.3, c.h*0.7); ball.speed = ball.baseSpeed; ball.ballInPlay = true; } diff --git a/src/shared_js/class/Event.ts b/src/shared_js/class/Event.ts index 7694605b..3f0d440a 100644 --- a/src/shared_js/class/Event.ts +++ b/src/shared_js/class/Event.ts @@ -39,6 +39,12 @@ class EventGameUpdate extends ServerEvent { dirY: number, speed: number }[] = []; + wallTop? = { + y: 0 + }; + wallBottom? = { + y: 0 + }; lastInputId = 0; constructor() { // TODO: constructor that take GameComponentsServer maybe ? super(en.EventTypes.gameUpdate); diff --git a/src/shared_js/class/GameComponents.ts b/src/shared_js/class/GameComponents.ts index 4bb0d2c1..4994af89 100644 --- a/src/shared_js/class/GameComponents.ts +++ b/src/shared_js/class/GameComponents.ts @@ -2,29 +2,26 @@ import * as c from "../constants.js" import * as en from "../../shared_js/enums.js" import { VectorInteger } from "./Vector.js"; -import { Rectangle, Racket, Ball } from "./Rectangle.js"; +import { Rectangle, MovingRectangle, Racket, Ball } from "./Rectangle.js"; import { clamp, random } from "../utils.js"; class GameComponents { - wallTop: Rectangle; - wallBottom: Rectangle; + wallTop: Rectangle | MovingRectangle; + wallBottom: Rectangle | MovingRectangle; playerLeft: Racket; playerRight: Racket; ballsArr: Ball[] = []; constructor(options: en.MatchOptions) { - let pos = new VectorInteger; + const pos = new VectorInteger; - pos.assign(0, 0); - this.wallTop = new Rectangle(pos, c.w, c.wallSize); - pos.assign(0, c.h-c.wallSize); - this.wallBottom = new Rectangle(pos, c.w, c.wallSize); - + // Rackets pos.assign(0+c.pw, c.h_mid-c.ph/2); this.playerLeft = new Racket(pos, c.pw, c.ph, c.racketSpeed); pos.assign(c.w-c.pw-c.pw, c.h_mid-c.ph/2); this.playerRight = new Racket(pos, c.pw, c.ph, c.racketSpeed); + // Balls let ballsCount = 1; if (options & en.MatchOptions.multiBalls) { ballsCount = c.multiBallsCount; @@ -46,6 +43,22 @@ class GameComponents { ball.dir = ball.dir.normalized(); }); + + // Walls + if (options & en.MatchOptions.movingWalls) { + pos.assign(0, 0); + this.wallTop = new MovingRectangle(pos, c.w, c.wallSize, c.movingWallSpeed); + (this.wallTop).dir.y = -1; + pos.assign(0, c.h-c.wallSize); + this.wallBottom = new MovingRectangle(pos, c.w, c.wallSize, c.movingWallSpeed); + (this.wallBottom).dir.y = 1; + } + else { + pos.assign(0, 0); + this.wallTop = new Rectangle(pos, c.w, c.wallSize); + pos.assign(0, c.h-c.wallSize); + this.wallBottom = new Rectangle(pos, c.w, c.wallSize); + } } } diff --git a/src/shared_js/constants.ts b/src/shared_js/constants.ts index cd76f51e..ae3320e5 100644 --- a/src/shared_js/constants.ts +++ b/src/shared_js/constants.ts @@ -20,4 +20,7 @@ export const normalizedSpeed = false; // for consistency in speed independent of export const matchStartDelay = 3000; // millisecond export const newRoundDelay = 1500; // millisecond +// Game Variantes export const multiBallsCount = 3; +export const movingWallPosMax = Math.floor(w*0.12); +export const movingWallSpeed = Math.floor(w*0.08); diff --git a/src/shared_js/enums.ts b/src/shared_js/enums.ts index 5dafa8e0..dfba2aa3 100644 --- a/src/shared_js/enums.ts +++ b/src/shared_js/enums.ts @@ -40,7 +40,8 @@ enum ClientRole { enum MatchOptions { // binary flags, can be mixed noOption = 0b0, - multiBalls = 1 << 0 + multiBalls = 1 << 0, + movingWalls = 1 << 1 } export {EventTypes, InputEnum, PlayerSide, ClientRole, MatchOptions} diff --git a/src/shared_js/utils.ts b/src/shared_js/utils.ts index 50feb05f..35bb5af1 100644 --- a/src/shared_js/utils.ts +++ b/src/shared_js/utils.ts @@ -1,4 +1,6 @@ +import { MovingRectangle } from "./class/Rectangle.js"; + function random(min: number = 0, max: number = 1) { return Math.random() * (max - min) + min; } @@ -16,4 +18,9 @@ function clamp(n: number, min: number, max: number) : number return (n); } -export {random, sleep, clamp} +function assertMovingRectangle(value: unknown): asserts value is MovingRectangle { + // if (value !== MovingRectangle) throw new Error("Not a MovingRectangle"); + return; +} + +export {random, sleep, clamp, assertMovingRectangle} diff --git a/src/shared_js/wallsMovement.ts b/src/shared_js/wallsMovement.ts new file mode 100644 index 00000000..dbf3f558 --- /dev/null +++ b/src/shared_js/wallsMovement.ts @@ -0,0 +1,20 @@ + +import * as c from "./constants.js"; +import { MovingRectangle } from "../shared_js/class/Rectangle.js"; +import { GameComponents } from "./class/GameComponents.js"; + +function wallsMovements(delta: number, gc: GameComponents) +{ + const wallTop = gc.wallTop; + const wallBottom = gc.wallBottom; + if (wallTop.pos.y <= 0 || wallTop.pos.y >= c.movingWallPosMax) { + wallTop.dir.y *= -1; + } + if (wallBottom.pos.y >= c.h-c.wallSize || wallBottom.pos.y <= c.h-c.movingWallPosMax) { + wallBottom.dir.y *= -1; + } + wallTop.moveAndCollide(delta, [gc.playerLeft, gc.playerRight]); + wallBottom.moveAndCollide(delta, [gc.playerLeft, gc.playerRight]); +} + +export {wallsMovements}