added alternate game mode "movingWalls"

This commit is contained in:
LuckyLaszlo
2022-12-02 21:01:06 +01:00
parent 93a40f2ad9
commit 59c7ae2a16
13 changed files with 136 additions and 24 deletions

View File

@@ -13,15 +13,15 @@ Done:
(BUG désynchronisation: revenu à un traitement immédiat en attendant) (BUG désynchronisation: revenu à un traitement immédiat en attendant)
- Détruire les GameSession une fois finies. - Détruire les GameSession une fois finies.
- mode multi-balles - mode multi-balles
- mode murs mouvant (la zone de jeu rétréci / agrandi en continu)
TODO: TODO:
- mode spectateur - mode spectateur
- un ou deux modes de jeu alternatif.
- certaines utilisations de Math.floor() superflu ? Vérifier les appels. - certaines utilisations de Math.floor() superflu ? Vérifier les appels.
(éventuellement Math.round() ?) (éventuellement Math.round() ?)
- un autre mode de jeu alternatif ?
----------- -----------
idées modes de jeu : 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) - mode 2 raquettes (un joueur haut/gauche et bas/droite)
- skin patate ??? - skin patate ???
- (prediction de l'avancement de la balle basé sur la latence serveur ?) - (prediction de l'avancement de la balle basé sur la latence serveur ?)

View File

@@ -3,28 +3,68 @@ import * as c from "../constants.js"
import * as en from "../../shared_js/enums.js" import * as en from "../../shared_js/enums.js"
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js"; import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import { TextElem, TextNumericValue } from "./Text.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 { GameComponents } from "../../shared_js/class/GameComponents.js";
import { assertMovingRectangle } from "../utils.js";
class GameComponentsExtensionForClient extends GameComponents { class GameComponentsExtensionForClient extends GameComponents {
wallTop: RectangleClient; wallTop: RectangleClient | MovingRectangleClient;
wallBottom: RectangleClient; wallBottom: RectangleClient | MovingRectangleClient;
playerLeft: RacketClient; playerLeft: RacketClient;
playerRight: RacketClient; playerRight: RacketClient;
ballsArr: BallClient[]; ballsArr: BallClient[];
constructor(options: en.MatchOptions, ctx: CanvasRenderingContext2D) constructor(options: en.MatchOptions, ctx: CanvasRenderingContext2D)
{ {
super(options); 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[] = []; const newBallsArr: BallClient[] = [];
this.ballsArr.forEach((ball) => { 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; 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");
(<MovingRectangleClient>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");
(<MovingRectangleClient>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");
}
} }
} }

View File

@@ -20,4 +20,7 @@ export const soundPongVolume = 0.3; // between 0 and 1
// TODO: replace by a selector on the website // TODO: replace by a selector on the website
import * as en from "../shared_js/enums.js" 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;

View File

@@ -32,9 +32,9 @@ function drawDynamic()
function drawStatic() function drawStatic()
{ {
gc.midLine.update();
gc.wallTop.update(); gc.wallTop.update();
gc.wallBottom.update(); gc.wallBottom.update();
gc.midLine.update();
} }
function drawGrid() function drawGrid()

View File

@@ -2,6 +2,7 @@
import * as c from "./constants.js"; import * as c from "./constants.js";
import * as en from "../shared_js/enums.js" import * as en from "../shared_js/enums.js"
import { gc, clientInfo } from "./global.js"; import { gc, clientInfo } from "./global.js";
import { wallsMovements } from "../shared_js/wallsMovement.js";
let actual_time: number = Date.now(); let actual_time: number = Date.now();
let last_time: number; let last_time: number;
@@ -26,6 +27,10 @@ function gameLoop()
gc.ballsArr.forEach((ball) => { gc.ballsArr.forEach((ball) => {
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]); 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) function opponentInterpolation(delta: number)

View File

@@ -90,6 +90,11 @@ function gameUpdate(data: ev.EventGameUpdate)
{ {
console.log("gameUpdate"); 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) => { data.ballsArr.forEach((ball, i) => {
gc.ballsArr[i].pos.assign(ball.x, ball.y); gc.ballsArr[i].pos.assign(ball.x, ball.y);
gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY); gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY);

View File

@@ -7,6 +7,7 @@ import { GameComponentsServer } from "./GameComponentsServer.js";
import { clientInputListener } from "../wsServer.js"; import { clientInputListener } from "../wsServer.js";
import { random } from "../utils.js"; import { random } from "../utils.js";
import { Ball } from "../../shared_js/class/Rectangle.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(), Arg "s: GameSession" replace "this: GameSession" for use with setTimeout(),
@@ -95,6 +96,10 @@ class GameSession {
gc.ballsArr.forEach((ball) => { gc.ballsArr.forEach((ball) => {
s._ballMovement(s.delta_time, ball); s._ballMovement(s.delta_time, ball);
}); });
if (s.matchOptions & en.MatchOptions.movingWalls) {
wallsMovements(s.delta_time, gc);
}
} }
private _ballMovement(delta: number, ball: Ball) { private _ballMovement(delta: number, ball: Ball) {
const gc = this.components; const gc = this.components;
@@ -133,6 +138,10 @@ class GameSession {
speed: ball.speed 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) => { s.playersMap.forEach( (client) => {
update.lastInputId = client.lastInputId; update.lastInputId = client.lastInputId;
@@ -152,7 +161,7 @@ class GameSession {
} }
} }
ball.pos.x = c.w_mid; 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.speed = ball.baseSpeed;
ball.ballInPlay = true; ball.ballInPlay = true;
} }

View File

@@ -39,6 +39,12 @@ class EventGameUpdate extends ServerEvent {
dirY: number, dirY: number,
speed: number speed: number
}[] = []; }[] = [];
wallTop? = {
y: 0
};
wallBottom? = {
y: 0
};
lastInputId = 0; lastInputId = 0;
constructor() { // TODO: constructor that take GameComponentsServer maybe ? constructor() { // TODO: constructor that take GameComponentsServer maybe ?
super(en.EventTypes.gameUpdate); super(en.EventTypes.gameUpdate);

View File

@@ -2,29 +2,26 @@
import * as c from "../constants.js" import * as c from "../constants.js"
import * as en from "../../shared_js/enums.js" import * as en from "../../shared_js/enums.js"
import { VectorInteger } from "./Vector.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"; import { clamp, random } from "../utils.js";
class GameComponents { class GameComponents {
wallTop: Rectangle; wallTop: Rectangle | MovingRectangle;
wallBottom: Rectangle; wallBottom: Rectangle | MovingRectangle;
playerLeft: Racket; playerLeft: Racket;
playerRight: Racket; playerRight: Racket;
ballsArr: Ball[] = []; ballsArr: Ball[] = [];
constructor(options: en.MatchOptions) constructor(options: en.MatchOptions)
{ {
let pos = new VectorInteger; const pos = new VectorInteger;
pos.assign(0, 0); // Rackets
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);
pos.assign(0+c.pw, c.h_mid-c.ph/2); pos.assign(0+c.pw, c.h_mid-c.ph/2);
this.playerLeft = new Racket(pos, c.pw, c.ph, c.racketSpeed); 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); 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); this.playerRight = new Racket(pos, c.pw, c.ph, c.racketSpeed);
// Balls
let ballsCount = 1; let ballsCount = 1;
if (options & en.MatchOptions.multiBalls) { if (options & en.MatchOptions.multiBalls) {
ballsCount = c.multiBallsCount; ballsCount = c.multiBallsCount;
@@ -46,6 +43,22 @@ class GameComponents {
ball.dir = ball.dir.normalized(); 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);
(<MovingRectangle>this.wallTop).dir.y = -1;
pos.assign(0, c.h-c.wallSize);
this.wallBottom = new MovingRectangle(pos, c.w, c.wallSize, c.movingWallSpeed);
(<MovingRectangle>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);
}
} }
} }

View File

@@ -20,4 +20,7 @@ export const normalizedSpeed = false; // for consistency in speed independent of
export const matchStartDelay = 3000; // millisecond export const matchStartDelay = 3000; // millisecond
export const newRoundDelay = 1500; // millisecond export const newRoundDelay = 1500; // millisecond
// Game Variantes
export const multiBallsCount = 3; export const multiBallsCount = 3;
export const movingWallPosMax = Math.floor(w*0.12);
export const movingWallSpeed = Math.floor(w*0.08);

View File

@@ -40,7 +40,8 @@ enum ClientRole {
enum MatchOptions { enum MatchOptions {
// binary flags, can be mixed // binary flags, can be mixed
noOption = 0b0, noOption = 0b0,
multiBalls = 1 << 0 multiBalls = 1 << 0,
movingWalls = 1 << 1
} }
export {EventTypes, InputEnum, PlayerSide, ClientRole, MatchOptions} export {EventTypes, InputEnum, PlayerSide, ClientRole, MatchOptions}

View File

@@ -1,4 +1,6 @@
import { MovingRectangle } from "./class/Rectangle.js";
function random(min: number = 0, max: number = 1) { function random(min: number = 0, max: number = 1) {
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;
} }
@@ -16,4 +18,9 @@ function clamp(n: number, min: number, max: number) : number
return (n); 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}

View File

@@ -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 = <MovingRectangle>gc.wallTop;
const wallBottom = <MovingRectangle>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}