serveur de jeu - work in progress
This commit is contained in:
84
jeu/src/shared_js/class/Event.js
Normal file
84
jeu/src/shared_js/class/Event.js
Normal file
@@ -0,0 +1,84 @@
|
||||
import * as en from "../enums.js";
|
||||
/* From Server */
|
||||
export class ServerEvent {
|
||||
constructor(type = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
export class EventAssignId extends ServerEvent {
|
||||
constructor(id) {
|
||||
super(en.EventTypes.assignId);
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
export class EventMatchmakingComplete extends ServerEvent {
|
||||
constructor(side) {
|
||||
super(en.EventTypes.matchmakingComplete);
|
||||
this.side = side;
|
||||
}
|
||||
}
|
||||
export class EventGameUpdate extends ServerEvent {
|
||||
constructor() {
|
||||
super(en.EventTypes.gameUpdate);
|
||||
this.playerLeft = {
|
||||
y: 0
|
||||
};
|
||||
this.playerRight = {
|
||||
y: 0
|
||||
};
|
||||
this.ballsArr = [];
|
||||
this.wallTop = {
|
||||
y: 0
|
||||
};
|
||||
this.wallBottom = {
|
||||
y: 0
|
||||
};
|
||||
this.lastInputId = 0;
|
||||
}
|
||||
}
|
||||
export class EventScoreUpdate extends ServerEvent {
|
||||
constructor(scoreLeft, scoreRight) {
|
||||
super(en.EventTypes.scoreUpdate);
|
||||
this.scoreLeft = scoreLeft;
|
||||
this.scoreRight = scoreRight;
|
||||
}
|
||||
}
|
||||
export class EventMatchEnd extends ServerEvent {
|
||||
constructor(winner, forfeit = false) {
|
||||
super(en.EventTypes.matchEnd);
|
||||
this.winner = winner;
|
||||
this.forfeit = forfeit;
|
||||
}
|
||||
}
|
||||
/* From Client */
|
||||
export class ClientEvent {
|
||||
constructor(type = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
export class ClientAnnounce extends ClientEvent {
|
||||
constructor(role) {
|
||||
super(en.EventTypes.clientAnnounce);
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
export class ClientAnnouncePlayer extends ClientAnnounce {
|
||||
constructor(matchOptions, clientId = "") {
|
||||
super(en.ClientRole.player);
|
||||
this.clientId = clientId;
|
||||
this.matchOptions = matchOptions;
|
||||
}
|
||||
}
|
||||
export class ClientAnnounceSpectator extends ClientAnnounce {
|
||||
constructor(gameSessionId) {
|
||||
super(en.ClientRole.spectator);
|
||||
this.gameSessionId = gameSessionId;
|
||||
}
|
||||
}
|
||||
export class EventInput extends ClientEvent {
|
||||
constructor(input = en.InputEnum.noInput, id = 0) {
|
||||
super(en.EventTypes.clientInput);
|
||||
this.input = input;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
117
jeu/src/shared_js/class/Event.ts
Normal file
117
jeu/src/shared_js/class/Event.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
|
||||
import * as en from "../enums.js"
|
||||
|
||||
/* From Server */
|
||||
export class ServerEvent {
|
||||
type: en.EventTypes;
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventAssignId extends ServerEvent {
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
super(en.EventTypes.assignId);
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchmakingComplete extends ServerEvent {
|
||||
side: en.PlayerSide;
|
||||
constructor(side: en.PlayerSide) {
|
||||
super(en.EventTypes.matchmakingComplete);
|
||||
this.side = side;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventGameUpdate extends ServerEvent {
|
||||
playerLeft = {
|
||||
y: 0
|
||||
};
|
||||
playerRight = {
|
||||
y: 0
|
||||
};
|
||||
ballsArr: {
|
||||
x: number,
|
||||
y: number,
|
||||
dirX: number,
|
||||
dirY: number,
|
||||
speed: number
|
||||
}[] = [];
|
||||
wallTop? = {
|
||||
y: 0
|
||||
};
|
||||
wallBottom? = {
|
||||
y: 0
|
||||
};
|
||||
lastInputId = 0;
|
||||
constructor() { // TODO: constructor that take GameComponentsServer maybe ?
|
||||
super(en.EventTypes.gameUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
export class EventScoreUpdate extends ServerEvent {
|
||||
scoreLeft: number;
|
||||
scoreRight: number;
|
||||
constructor(scoreLeft: number, scoreRight: number) {
|
||||
super(en.EventTypes.scoreUpdate);
|
||||
this.scoreLeft = scoreLeft;
|
||||
this.scoreRight = scoreRight;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchEnd extends ServerEvent {
|
||||
winner: en.PlayerSide;
|
||||
forfeit: boolean;
|
||||
constructor(winner: en.PlayerSide, forfeit = false) {
|
||||
super(en.EventTypes.matchEnd);
|
||||
this.winner = winner;
|
||||
this.forfeit = forfeit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* From Client */
|
||||
export class ClientEvent {
|
||||
type: en.EventTypes; // readonly ?
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnounce extends ClientEvent {
|
||||
role: en.ClientRole;
|
||||
constructor(role: en.ClientRole) {
|
||||
super(en.EventTypes.clientAnnounce);
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnouncePlayer extends ClientAnnounce {
|
||||
clientId: string;
|
||||
matchOptions: en.MatchOptions;
|
||||
constructor(matchOptions: en.MatchOptions, clientId: string = "") {
|
||||
super(en.ClientRole.player);
|
||||
this.clientId = clientId;
|
||||
this.matchOptions = matchOptions;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnounceSpectator extends ClientAnnounce {
|
||||
gameSessionId: string;
|
||||
constructor(gameSessionId: string) {
|
||||
super(en.ClientRole.spectator);
|
||||
this.gameSessionId = gameSessionId;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventInput extends ClientEvent {
|
||||
input: en.InputEnum;
|
||||
id: number;
|
||||
constructor(input: en.InputEnum = en.InputEnum.noInput, id: number = 0) {
|
||||
super(en.EventTypes.clientInput);
|
||||
this.input = input;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
51
jeu/src/shared_js/class/GameComponents.js
Normal file
51
jeu/src/shared_js/class/GameComponents.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import * as c from "../constants.js";
|
||||
import * as en from "../../shared_js/enums.js";
|
||||
import { VectorInteger } from "./Vector.js";
|
||||
import { Rectangle, MovingRectangle, Racket, Ball } from "./Rectangle.js";
|
||||
import { random } from "../utils.js";
|
||||
export class GameComponents {
|
||||
constructor(options) {
|
||||
this.ballsArr = [];
|
||||
const pos = new VectorInteger;
|
||||
// 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;
|
||||
}
|
||||
pos.assign(-c.ballSize, -c.ballSize); // ball out =)
|
||||
while (this.ballsArr.length < ballsCount) {
|
||||
this.ballsArr.push(new Ball(pos, c.ballSize, c.ballSpeed, c.ballSpeedIncrease));
|
||||
}
|
||||
this.ballsArr.forEach((ball) => {
|
||||
ball.dir.x = 1;
|
||||
if (random() > 0.5) {
|
||||
ball.dir.x *= -1;
|
||||
}
|
||||
ball.dir.y = random(0, 0.2);
|
||||
if (random() > 0.5) {
|
||||
ball.dir.y *= -1;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
63
jeu/src/shared_js/class/GameComponents.ts
Normal file
63
jeu/src/shared_js/class/GameComponents.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
|
||||
import * as c from "../constants.js"
|
||||
import * as en from "../../shared_js/enums.js"
|
||||
import { VectorInteger } from "./Vector.js";
|
||||
import { Rectangle, MovingRectangle, Racket, Ball } from "./Rectangle.js";
|
||||
import { random } from "../utils.js";
|
||||
|
||||
export class GameComponents {
|
||||
wallTop: Rectangle | MovingRectangle;
|
||||
wallBottom: Rectangle | MovingRectangle;
|
||||
playerLeft: Racket;
|
||||
playerRight: Racket;
|
||||
ballsArr: Ball[] = [];
|
||||
constructor(options: en.MatchOptions)
|
||||
{
|
||||
const pos = new VectorInteger;
|
||||
|
||||
// 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;
|
||||
}
|
||||
pos.assign(-c.ballSize, -c.ballSize); // ball out =)
|
||||
while (this.ballsArr.length < ballsCount) {
|
||||
this.ballsArr.push(new Ball(pos, c.ballSize, c.ballSpeed, c.ballSpeedIncrease))
|
||||
}
|
||||
this.ballsArr.forEach((ball) => {
|
||||
ball.dir.x = 1;
|
||||
if (random() > 0.5) {
|
||||
ball.dir.x *= -1;
|
||||
}
|
||||
|
||||
ball.dir.y = random(0, 0.2);
|
||||
if (random() > 0.5) {
|
||||
ball.dir.y *= -1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
124
jeu/src/shared_js/class/Rectangle.js
Normal file
124
jeu/src/shared_js/class/Rectangle.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import { Vector, VectorInteger } from "./Vector.js";
|
||||
import * as c from "../constants.js";
|
||||
export class Rectangle {
|
||||
constructor(pos, width, height) {
|
||||
this.pos = new VectorInteger(pos.x, pos.y);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
collision(collider) {
|
||||
const thisLeft = this.pos.x;
|
||||
const thisRight = this.pos.x + this.width;
|
||||
const thisTop = this.pos.y;
|
||||
const thisBottom = this.pos.y + this.height;
|
||||
const colliderLeft = collider.pos.x;
|
||||
const colliderRight = collider.pos.x + collider.width;
|
||||
const colliderTop = collider.pos.y;
|
||||
const colliderBottom = collider.pos.y + collider.height;
|
||||
if ((thisBottom < colliderTop)
|
||||
|| (thisTop > colliderBottom)
|
||||
|| (thisRight < colliderLeft)
|
||||
|| (thisLeft > colliderRight)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
export class MovingRectangle extends Rectangle {
|
||||
constructor(pos, width, height, baseSpeed) {
|
||||
super(pos, width, height);
|
||||
this.dir = new Vector(0, 0);
|
||||
this.baseSpeed = baseSpeed;
|
||||
this.speed = baseSpeed;
|
||||
}
|
||||
move(delta) {
|
||||
// console.log(`delta: ${delta}, speed: ${this.speed}, speed*delta: ${this.speed * delta}`);
|
||||
// this.pos.x += Math.floor(this.dir.x * this.speed * delta);
|
||||
// this.pos.y += Math.floor(this.dir.y * this.speed * delta);
|
||||
this.pos.x += this.dir.x * this.speed * delta;
|
||||
this.pos.y += this.dir.y * this.speed * delta;
|
||||
}
|
||||
moveAndCollide(delta, colliderArr) {
|
||||
this._moveAndCollideAlgo(delta, colliderArr);
|
||||
}
|
||||
_moveAndCollideAlgo(delta, colliderArr) {
|
||||
let oldPos = new VectorInteger(this.pos.x, this.pos.y);
|
||||
this.move(delta);
|
||||
if (colliderArr.some(this.collision, this)) {
|
||||
this.pos = oldPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
export class Racket extends MovingRectangle {
|
||||
constructor(pos, width, height, baseSpeed) {
|
||||
super(pos, width, height, baseSpeed);
|
||||
}
|
||||
moveAndCollide(delta, colliderArr) {
|
||||
// let oldPos = new VectorInteger(this.pos.x, this.pos.y); // debug
|
||||
this._moveAndCollideAlgo(delta, colliderArr);
|
||||
// console.log(`y change: ${this.pos.y - oldPos.y}`);
|
||||
}
|
||||
}
|
||||
export class Ball extends MovingRectangle {
|
||||
constructor(pos, size, baseSpeed, speedIncrease) {
|
||||
super(pos, size, size, baseSpeed);
|
||||
this.ballInPlay = false;
|
||||
this.speedIncrease = speedIncrease;
|
||||
}
|
||||
moveAndBounce(delta, colliderArr) {
|
||||
this.move(delta);
|
||||
let i = colliderArr.findIndex(this.collision, this);
|
||||
if (i != -1) {
|
||||
this.bounce(colliderArr[i]);
|
||||
this.move(delta);
|
||||
}
|
||||
}
|
||||
bounce(collider) {
|
||||
this._bounceAlgo(collider);
|
||||
}
|
||||
_bounceAlgo(collider) {
|
||||
/* Could be more generic, but testing only Racket is enough,
|
||||
because in Pong collider can only be Racket or Wall. */
|
||||
if (collider instanceof Racket) {
|
||||
this._bounceRacket(collider);
|
||||
}
|
||||
else {
|
||||
this._bounceWall();
|
||||
}
|
||||
}
|
||||
_bounceWall() {
|
||||
this.dir.y = this.dir.y * -1;
|
||||
}
|
||||
_bounceRacket(racket) {
|
||||
this._bounceRacketAlgo(racket);
|
||||
}
|
||||
_bounceRacketAlgo(racket) {
|
||||
this.speed += this.speedIncrease;
|
||||
let x = this.dir.x * -1;
|
||||
const angleFactorDegree = 60;
|
||||
const angleFactor = angleFactorDegree / 90;
|
||||
const racketHalf = racket.height / 2;
|
||||
const ballMid = this.pos.y + this.height / 2;
|
||||
const racketMid = racket.pos.y + racketHalf;
|
||||
let impact = ballMid - racketMid;
|
||||
const horizontalMargin = racketHalf * 0.15;
|
||||
if (impact < horizontalMargin && impact > -horizontalMargin) {
|
||||
impact = 0;
|
||||
}
|
||||
else if (impact > 0) {
|
||||
impact = impact - horizontalMargin;
|
||||
}
|
||||
else if (impact < 0) {
|
||||
impact = impact + horizontalMargin;
|
||||
}
|
||||
let y = impact / (racketHalf - horizontalMargin) * angleFactor;
|
||||
this.dir.assign(x, y);
|
||||
// Normalize Vector (for consistency in speed independent of direction)
|
||||
if (c.normalizedSpeed) {
|
||||
this.dir = this.dir.normalized();
|
||||
}
|
||||
// console.log(`x: ${this.dir.x}, y: ${this.dir.y}`);
|
||||
}
|
||||
}
|
||||
142
jeu/src/shared_js/class/Rectangle.ts
Normal file
142
jeu/src/shared_js/class/Rectangle.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
|
||||
import { Vector, VectorInteger } from "./Vector.js";
|
||||
import { Component, Moving } from "./interface.js";
|
||||
import * as c from "../constants.js"
|
||||
|
||||
export class Rectangle implements Component {
|
||||
pos: VectorInteger;
|
||||
width: number;
|
||||
height: number;
|
||||
constructor(pos: VectorInteger, width: number, height: number) {
|
||||
this.pos = new VectorInteger(pos.x, pos.y);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
collision(collider: Rectangle): boolean {
|
||||
const thisLeft = this.pos.x;
|
||||
const thisRight = this.pos.x + this.width;
|
||||
const thisTop = this.pos.y;
|
||||
const thisBottom = this.pos.y + this.height;
|
||||
const colliderLeft = collider.pos.x;
|
||||
const colliderRight = collider.pos.x + collider.width;
|
||||
const colliderTop = collider.pos.y;
|
||||
const colliderBottom = collider.pos.y + collider.height;
|
||||
if ((thisBottom < colliderTop)
|
||||
|| (thisTop > colliderBottom)
|
||||
|| (thisRight < colliderLeft)
|
||||
|| (thisLeft > colliderRight)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MovingRectangle extends Rectangle implements Moving {
|
||||
dir: Vector = new Vector(0,0);
|
||||
speed: number;
|
||||
readonly baseSpeed: number;
|
||||
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number) {
|
||||
super(pos, width, height);
|
||||
this.baseSpeed = baseSpeed;
|
||||
this.speed = baseSpeed;
|
||||
}
|
||||
move(delta: number) { // Math.floor WIP until VectorInteger debug
|
||||
// console.log(`delta: ${delta}, speed: ${this.speed}, speed*delta: ${this.speed * delta}`);
|
||||
// this.pos.x += Math.floor(this.dir.x * this.speed * delta);
|
||||
// this.pos.y += Math.floor(this.dir.y * this.speed * delta);
|
||||
this.pos.x += this.dir.x * this.speed * delta;
|
||||
this.pos.y += this.dir.y * this.speed * delta;
|
||||
}
|
||||
moveAndCollide(delta: number, colliderArr: Rectangle[]) {
|
||||
this._moveAndCollideAlgo(delta, colliderArr);
|
||||
}
|
||||
protected _moveAndCollideAlgo(delta: number, colliderArr: Rectangle[]) {
|
||||
let oldPos = new VectorInteger(this.pos.x, this.pos.y);
|
||||
this.move(delta);
|
||||
if (colliderArr.some(this.collision, this)) {
|
||||
this.pos = oldPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Racket extends MovingRectangle {
|
||||
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number) {
|
||||
super(pos, width, height, baseSpeed);
|
||||
}
|
||||
moveAndCollide(delta: number, colliderArr: Rectangle[]) {
|
||||
// let oldPos = new VectorInteger(this.pos.x, this.pos.y); // debug
|
||||
this._moveAndCollideAlgo(delta, colliderArr);
|
||||
// console.log(`y change: ${this.pos.y - oldPos.y}`);
|
||||
}
|
||||
}
|
||||
|
||||
export class Ball extends MovingRectangle {
|
||||
readonly speedIncrease: number;
|
||||
ballInPlay: boolean = false;
|
||||
constructor(pos: VectorInteger, size: number, baseSpeed: number, speedIncrease: number) {
|
||||
super(pos, size, size, baseSpeed);
|
||||
this.speedIncrease = speedIncrease;
|
||||
}
|
||||
moveAndBounce(delta: number, colliderArr: Rectangle[]) {
|
||||
this.move(delta);
|
||||
let i = colliderArr.findIndex(this.collision, this);
|
||||
if (i != -1)
|
||||
{
|
||||
this.bounce(colliderArr[i]);
|
||||
this.move(delta);
|
||||
}
|
||||
}
|
||||
bounce(collider?: Rectangle) {
|
||||
this._bounceAlgo(collider);
|
||||
}
|
||||
protected _bounceAlgo(collider?: Rectangle) {
|
||||
/* Could be more generic, but testing only Racket is enough,
|
||||
because in Pong collider can only be Racket or Wall. */
|
||||
if (collider instanceof Racket) {
|
||||
this._bounceRacket(collider);
|
||||
}
|
||||
else {
|
||||
this._bounceWall();
|
||||
}
|
||||
}
|
||||
protected _bounceWall() { // Should be enough for Wall
|
||||
this.dir.y = this.dir.y * -1;
|
||||
}
|
||||
protected _bounceRacket(racket: Racket) {
|
||||
this._bounceRacketAlgo(racket);
|
||||
}
|
||||
protected _bounceRacketAlgo(racket: Racket) {
|
||||
this.speed += this.speedIncrease;
|
||||
|
||||
let x = this.dir.x * -1;
|
||||
|
||||
const angleFactorDegree = 60;
|
||||
const angleFactor = angleFactorDegree / 90;
|
||||
const racketHalf = racket.height/2;
|
||||
const ballMid = this.pos.y + this.height/2;
|
||||
const racketMid = racket.pos.y + racketHalf;
|
||||
|
||||
let impact = ballMid - racketMid;
|
||||
const horizontalMargin = racketHalf * 0.15;
|
||||
if (impact < horizontalMargin && impact > -horizontalMargin) {
|
||||
impact = 0;
|
||||
}
|
||||
else if (impact > 0) {
|
||||
impact = impact - horizontalMargin;
|
||||
}
|
||||
else if (impact < 0) {
|
||||
impact = impact + horizontalMargin;
|
||||
}
|
||||
|
||||
let y = impact / (racketHalf - horizontalMargin) * angleFactor;
|
||||
|
||||
this.dir.assign(x, y);
|
||||
// Normalize Vector (for consistency in speed independent of direction)
|
||||
if (c.normalizedSpeed) {
|
||||
this.dir = this.dir.normalized();
|
||||
}
|
||||
// console.log(`x: ${this.dir.x}, y: ${this.dir.y}`);
|
||||
}
|
||||
}
|
||||
40
jeu/src/shared_js/class/Vector.js
Normal file
40
jeu/src/shared_js/class/Vector.js
Normal file
@@ -0,0 +1,40 @@
|
||||
export class Vector {
|
||||
constructor(x = 0, y = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
assign(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
normalized() {
|
||||
const normalizationFactor = Math.abs(this.x) + Math.abs(this.y);
|
||||
return new Vector(this.x / normalizationFactor, this.y / normalizationFactor);
|
||||
}
|
||||
}
|
||||
export class VectorInteger extends Vector {
|
||||
}
|
||||
/*
|
||||
export class VectorInteger {
|
||||
// private _x: number = 0;
|
||||
// private _y: number = 0;
|
||||
// constructor(x: number = 0, y: number = 0) {
|
||||
// this._x = x;
|
||||
// this._y = y;
|
||||
// }
|
||||
// get x(): number {
|
||||
// return this._x;
|
||||
// }
|
||||
// set x(v: number) {
|
||||
// // this._x = Math.floor(v);
|
||||
// this._x = v;
|
||||
// }
|
||||
// get y(): number {
|
||||
// return this._y;
|
||||
// }
|
||||
// set y(v: number) {
|
||||
// // this._y = Math.floor(v);
|
||||
// this._y = v;
|
||||
// }
|
||||
}
|
||||
*/
|
||||
47
jeu/src/shared_js/class/Vector.ts
Normal file
47
jeu/src/shared_js/class/Vector.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
export class Vector {
|
||||
x: number;
|
||||
y: number;
|
||||
constructor(x: number = 0, y: number = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
assign(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
normalized() : Vector {
|
||||
const normalizationFactor = Math.abs(this.x) + Math.abs(this.y);
|
||||
return new Vector(this.x/normalizationFactor, this.y/normalizationFactor);
|
||||
}
|
||||
}
|
||||
|
||||
export class VectorInteger extends Vector {
|
||||
// PLACEHOLDER
|
||||
// VectorInteger with set/get dont work (No draw on the screen). Why ?
|
||||
}
|
||||
|
||||
/*
|
||||
export class VectorInteger {
|
||||
// private _x: number = 0;
|
||||
// private _y: number = 0;
|
||||
// constructor(x: number = 0, y: number = 0) {
|
||||
// this._x = x;
|
||||
// this._y = y;
|
||||
// }
|
||||
// get x(): number {
|
||||
// return this._x;
|
||||
// }
|
||||
// set x(v: number) {
|
||||
// // this._x = Math.floor(v);
|
||||
// this._x = v;
|
||||
// }
|
||||
// get y(): number {
|
||||
// return this._y;
|
||||
// }
|
||||
// set y(v: number) {
|
||||
// // this._y = Math.floor(v);
|
||||
// this._y = v;
|
||||
// }
|
||||
}
|
||||
*/
|
||||
1
jeu/src/shared_js/class/interface.js
Normal file
1
jeu/src/shared_js/class/interface.js
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
19
jeu/src/shared_js/class/interface.ts
Normal file
19
jeu/src/shared_js/class/interface.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
import { Vector, VectorInteger } from "./Vector.js";
|
||||
|
||||
export interface Component {
|
||||
pos: VectorInteger;
|
||||
}
|
||||
|
||||
export interface GraphicComponent extends Component {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
clear: (pos?: VectorInteger) => void;
|
||||
}
|
||||
|
||||
export interface Moving {
|
||||
dir: Vector;
|
||||
speed: number; // pixel per second
|
||||
move(delta: number): void;
|
||||
}
|
||||
23
jeu/src/shared_js/constants.js
Normal file
23
jeu/src/shared_js/constants.js
Normal file
@@ -0,0 +1,23 @@
|
||||
export const CanvasWidth = 1500;
|
||||
export const CanvasRatio = 1.66666;
|
||||
/* ratio 5/3 (1.66) */
|
||||
export const w = CanvasWidth;
|
||||
export const h = CanvasWidth / CanvasRatio;
|
||||
export const w_mid = Math.floor(w / 2);
|
||||
export const h_mid = Math.floor(h / 2);
|
||||
export const pw = Math.floor(w * 0.017);
|
||||
export const ph = pw * 6;
|
||||
export const ballSize = pw;
|
||||
export const wallSize = Math.floor(w * 0.01);
|
||||
export const racketSpeed = Math.floor(w * 0.66); // pixel per second
|
||||
export const ballSpeed = Math.floor(w * 0.66); // pixel per second
|
||||
export const ballSpeedIncrease = Math.floor(ballSpeed * 0.05); // pixel per second
|
||||
export const normalizedSpeed = false; // for consistency in speed independent of direction
|
||||
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);
|
||||
export const gameSessionIdPLACEHOLDER = "42"; // TESTING SPECTATOR PLACEHOLDER
|
||||
// for testing, force gameSession.id in wsServer.ts->matchmaking()
|
||||
30
jeu/src/shared_js/constants.ts
Normal file
30
jeu/src/shared_js/constants.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
export const CanvasWidth = 1500;
|
||||
export const CanvasRatio = 1.66666;
|
||||
/* ratio 5/3 (1.66) */
|
||||
|
||||
export const w = CanvasWidth;
|
||||
export const h = CanvasWidth / CanvasRatio;
|
||||
export const w_mid = Math.floor(w/2);
|
||||
export const h_mid = Math.floor(h/2);
|
||||
export const pw = Math.floor(w*0.017);
|
||||
export const ph = pw*6;
|
||||
export const ballSize = pw;
|
||||
export const wallSize = Math.floor(w*0.01);
|
||||
export const racketSpeed = Math.floor(w*0.66); // pixel per second
|
||||
export const ballSpeed = Math.floor(w*0.66); // pixel per second
|
||||
export const ballSpeedIncrease = Math.floor(ballSpeed*0.05); // pixel per second
|
||||
|
||||
export const normalizedSpeed = false; // for consistency in speed independent of direction
|
||||
|
||||
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);
|
||||
|
||||
|
||||
export const gameSessionIdPLACEHOLDER = "42"; // TESTING SPECTATOR PLACEHOLDER
|
||||
// for testing, force gameSession.id in wsServer.ts->matchmaking()
|
||||
43
jeu/src/shared_js/enums.js
Normal file
43
jeu/src/shared_js/enums.js
Normal file
@@ -0,0 +1,43 @@
|
||||
export var EventTypes;
|
||||
(function (EventTypes) {
|
||||
// Class Implemented
|
||||
EventTypes[EventTypes["gameUpdate"] = 1] = "gameUpdate";
|
||||
EventTypes[EventTypes["scoreUpdate"] = 2] = "scoreUpdate";
|
||||
EventTypes[EventTypes["matchEnd"] = 3] = "matchEnd";
|
||||
EventTypes[EventTypes["assignId"] = 4] = "assignId";
|
||||
EventTypes[EventTypes["matchmakingComplete"] = 5] = "matchmakingComplete";
|
||||
// Generic
|
||||
EventTypes[EventTypes["matchmakingInProgress"] = 6] = "matchmakingInProgress";
|
||||
EventTypes[EventTypes["matchStart"] = 7] = "matchStart";
|
||||
EventTypes[EventTypes["matchAbort"] = 8] = "matchAbort";
|
||||
EventTypes[EventTypes["matchNewRound"] = 9] = "matchNewRound";
|
||||
EventTypes[EventTypes["matchPause"] = 10] = "matchPause";
|
||||
EventTypes[EventTypes["matchResume"] = 11] = "matchResume";
|
||||
// Client
|
||||
EventTypes[EventTypes["clientAnnounce"] = 12] = "clientAnnounce";
|
||||
EventTypes[EventTypes["clientPlayerReady"] = 13] = "clientPlayerReady";
|
||||
EventTypes[EventTypes["clientInput"] = 14] = "clientInput";
|
||||
})(EventTypes || (EventTypes = {}));
|
||||
export var InputEnum;
|
||||
(function (InputEnum) {
|
||||
InputEnum[InputEnum["noInput"] = 0] = "noInput";
|
||||
InputEnum[InputEnum["up"] = 1] = "up";
|
||||
InputEnum[InputEnum["down"] = 2] = "down";
|
||||
})(InputEnum || (InputEnum = {}));
|
||||
export var PlayerSide;
|
||||
(function (PlayerSide) {
|
||||
PlayerSide[PlayerSide["left"] = 1] = "left";
|
||||
PlayerSide[PlayerSide["right"] = 2] = "right";
|
||||
})(PlayerSide || (PlayerSide = {}));
|
||||
export var ClientRole;
|
||||
(function (ClientRole) {
|
||||
ClientRole[ClientRole["player"] = 1] = "player";
|
||||
ClientRole[ClientRole["spectator"] = 2] = "spectator";
|
||||
})(ClientRole || (ClientRole = {}));
|
||||
export var MatchOptions;
|
||||
(function (MatchOptions) {
|
||||
// binary flags, can be mixed
|
||||
MatchOptions[MatchOptions["noOption"] = 0] = "noOption";
|
||||
MatchOptions[MatchOptions["multiBalls"] = 1] = "multiBalls";
|
||||
MatchOptions[MatchOptions["movingWalls"] = 2] = "movingWalls";
|
||||
})(MatchOptions || (MatchOptions = {}));
|
||||
46
jeu/src/shared_js/enums.ts
Normal file
46
jeu/src/shared_js/enums.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
export enum EventTypes {
|
||||
// Class Implemented
|
||||
gameUpdate = 1,
|
||||
scoreUpdate,
|
||||
matchEnd,
|
||||
assignId,
|
||||
matchmakingComplete,
|
||||
|
||||
// Generic
|
||||
matchmakingInProgress,
|
||||
matchStart,
|
||||
matchAbort,
|
||||
matchNewRound, // unused
|
||||
matchPause, // unused
|
||||
matchResume, // unused
|
||||
|
||||
// Client
|
||||
clientAnnounce,
|
||||
clientPlayerReady,
|
||||
clientInput,
|
||||
|
||||
}
|
||||
|
||||
export enum InputEnum {
|
||||
noInput = 0,
|
||||
up = 1,
|
||||
down,
|
||||
}
|
||||
|
||||
export enum PlayerSide {
|
||||
left = 1,
|
||||
right
|
||||
}
|
||||
|
||||
export enum ClientRole {
|
||||
player = 1,
|
||||
spectator
|
||||
}
|
||||
|
||||
export enum MatchOptions {
|
||||
// binary flags, can be mixed
|
||||
noOption = 0b0,
|
||||
multiBalls = 1 << 0,
|
||||
movingWalls = 1 << 1
|
||||
}
|
||||
18
jeu/src/shared_js/utils.js
Normal file
18
jeu/src/shared_js/utils.js
Normal file
@@ -0,0 +1,18 @@
|
||||
export function random(min = 0, max = 1) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
export function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
export function clamp(n, min, max) {
|
||||
if (n < min)
|
||||
n = min;
|
||||
else if (n > max)
|
||||
n = max;
|
||||
return (n);
|
||||
}
|
||||
// Typescript hack, unused
|
||||
export function assertMovingRectangle(value) {
|
||||
// if (value !== MovingRectangle) throw new Error("Not a MovingRectangle");
|
||||
return;
|
||||
}
|
||||
25
jeu/src/shared_js/utils.ts
Normal file
25
jeu/src/shared_js/utils.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
import { MovingRectangle } from "./class/Rectangle.js";
|
||||
|
||||
export function random(min: number = 0, max: number = 1) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
export function sleep (ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export function clamp(n: number, min: number, max: number) : number
|
||||
{
|
||||
if (n < min)
|
||||
n = min;
|
||||
else if (n > max)
|
||||
n = max;
|
||||
return (n);
|
||||
}
|
||||
|
||||
// Typescript hack, unused
|
||||
export function assertMovingRectangle(value: unknown): asserts value is MovingRectangle {
|
||||
// if (value !== MovingRectangle) throw new Error("Not a MovingRectangle");
|
||||
return;
|
||||
}
|
||||
13
jeu/src/shared_js/wallsMovement.js
Normal file
13
jeu/src/shared_js/wallsMovement.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as c from "./constants.js";
|
||||
export function wallsMovements(delta, gc) {
|
||||
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]);
|
||||
}
|
||||
18
jeu/src/shared_js/wallsMovement.ts
Normal file
18
jeu/src/shared_js/wallsMovement.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
import * as c from "./constants.js";
|
||||
import { MovingRectangle } from "../shared_js/class/Rectangle.js";
|
||||
import { GameComponents } from "./class/GameComponents.js";
|
||||
|
||||
export 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]);
|
||||
}
|
||||
Reference in New Issue
Block a user