drawing now seperate from gameLoop

+ refactoring
+ added soundMutedFlag
This commit is contained in:
LuckyLaszlo
2022-11-24 13:56:51 +01:00
parent 7d5895a6cc
commit 3474d54a2b
22 changed files with 105 additions and 106 deletions

View File

@@ -1,12 +1,14 @@
import * as c from "./constants.js" import * as c from "./constants.js"
export const soundPong: HTMLAudioElement[] = []; export const soundPongArr: HTMLAudioElement[] = [];
for (let i = 0; i <= 32; i++) { for (let i = 0; i <= 32; i++) {
soundPong.push(new Audio("http://localhost:8080/sound/pong/"+i+".mp3")); soundPongArr.push(new Audio("http://localhost:8080/sound/pong/"+i+".mp3"));
soundPong[i].volume = c.soundPongVolume; soundPongArr[i].volume = c.soundPongVolume;
soundPongArr[i].muted = c.soundMutedFlag;
} }
export const soundRoblox = new Audio("http://localhost:8080/sound/roblox-oof.mp3"); export const soundRoblox = new Audio("http://localhost:8080/sound/roblox-oof.mp3");
soundRoblox.volume = c.soundRobloxVolume; soundRoblox.volume = c.soundRobloxVolume;
soundRoblox.muted = c.soundMutedFlag;

View File

@@ -5,6 +5,7 @@ class GameArea {
keys: string[] = []; keys: string[] = [];
handleInputInterval: number = 0; handleInputInterval: number = 0;
gameLoopInterval: number = 0; gameLoopInterval: number = 0;
drawLoopInterval: number = 0;
canvas: HTMLCanvasElement; canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D; ctx: CanvasRenderingContext2D;
constructor() { constructor() {

View File

@@ -1,7 +1,7 @@
import * as c from "../constants.js" import * as c from "../constants.js"
import {VectorInteger} from "../../shared_js/class/Vector.js"; import { VectorInteger } from "../../shared_js/class/Vector.js";
import {RectangleClient, RacketClient, BallClient} from "./RectangleClient.js"; import { RectangleClient, RacketClient, BallClient } from "./RectangleClient.js";
class GameComponentsForClient { class GameComponentsForClient {
wallTop: RectangleClient; wallTop: RectangleClient;

View File

@@ -1,9 +1,9 @@
import * as c from "../constants.js" import * as c from "../constants.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 { GameComponentsForClient } from "./GameComponents.js";
import { RectangleClient, Line } from "./RectangleClient.js"; import { RectangleClient, Line } from "./RectangleClient.js";
import { GameComponentsForClient } from "./GameComponents.js";
class GameComponentsClient extends GameComponentsForClient { class GameComponentsClient extends GameComponentsForClient {
midLine: Line; midLine: Line;

View File

@@ -0,0 +1,15 @@
import * as en from "../../shared_js/enums.js"
class InputHistory {
input: en.InputEnum;
inputId: number;
deltaTime: number;
constructor(input: en.InputEnum, inputId: number, deltaTime: number) {
this.input = input;
this.inputId = inputId;
this.deltaTime = deltaTime;
}
}
export {InputHistory}

View File

@@ -1,8 +1,8 @@
import {Vector, VectorInteger} from "../../shared_js/class/Vector.js"; import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import {Component, GraphicComponent, Moving} from "../../shared_js/class/interface.js"; import { Component, GraphicComponent, Moving } from "../../shared_js/class/interface.js";
import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js"; import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js";
import {soundPong, soundRoblox} from "../audio.js" import { soundPongArr } from "../audio.js"
import { random } from "../utils.js"; import { random } from "../utils.js";
function updateRectangle(this: RectangleClient) { function updateRectangle(this: RectangleClient) {
@@ -91,7 +91,7 @@ class BallClient extends Ball implements GraphicComponent {
} }
bounce(collider?: Rectangle) { bounce(collider?: Rectangle) {
this._bounceAlgo(collider); this._bounceAlgo(collider);
soundPong[ Math.floor(random(0, soundPong.length)) ].play(); soundPongArr[ Math.floor(random(0, soundPongArr.length)) ].play();
} }
/* protected _bouncePlayer(collider: Racket) { /* protected _bouncePlayer(collider: Racket) {
this._bouncePlayerAlgo(collider); this._bouncePlayerAlgo(collider);

View File

@@ -1,6 +1,6 @@
import {Vector, VectorInteger} from "../../shared_js/class/Vector.js"; import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import {Component, Moving} from "../../shared_js/class/interface.js"; import { Component } from "../../shared_js/class/interface.js";
// conflict with Text // conflict with Text
class TextElem implements Component { class TextElem implements Component {

View File

@@ -1,5 +1,5 @@
import {w} from "../shared_js/constants.js" import { w } from "../shared_js/constants.js"
export * from "../shared_js/constants.js" export * from "../shared_js/constants.js"
export const midLineSize = Math.floor(w/150); export const midLineSize = Math.floor(w/150);
@@ -9,6 +9,8 @@ export const gridSize = Math.floor(w/500);
// min interval on Firefox seems to be 15. Chrome can go lower. // min interval on Firefox seems to be 15. Chrome can go lower.
export const handleInputIntervalMS = 15; // millisecond export const handleInputIntervalMS = 15; // millisecond
export const gameLoopIntervalMS = 15; // millisecond export const gameLoopIntervalMS = 15; // millisecond
export const drawLoopIntervalMS = 15; // millisecond
export const soundRobloxVolume = 0; // between 0 and 1 export const soundMutedFlag = true;
export const soundPongVolume = 0; // between 0 and 1 export const soundRobloxVolume = 0.3; // between 0 and 1
export const soundPongVolume = 0.3; // between 0 and 1

View File

@@ -1,7 +1,8 @@
import {pong, gc, clientInfo} from "./global.js"
import {gridDisplay} from "./handleInput.js";
function draw() import { pong, gc } from "./global.js"
import { gridDisplay } from "./handleInput.js";
function drawLoop()
{ {
pong.clear(); pong.clear();
@@ -10,6 +11,12 @@ function draw()
if (gridDisplay) { if (gridDisplay) {
drawGrid(); drawGrid();
} }
drawDynamic();
}
function drawDynamic()
{
gc.scoreLeft.update(); gc.scoreLeft.update();
gc.scoreRight.update(); gc.scoreRight.update();
gc.playerLeft.update(); gc.playerLeft.update();
@@ -35,4 +42,4 @@ function drawGrid()
gc.h_grid_d1.update(); gc.h_grid_d1.update();
} }
export {draw, drawStatic, drawGrid} export {drawLoop}

View File

@@ -1,5 +1,5 @@
import {gc} from "./global.js";
import * as d from "./draw.js"; import { gc } from "./global.js";
let actual_time: number = Date.now(); let actual_time: number = Date.now();
let last_time: number; let last_time: number;
@@ -18,8 +18,6 @@ function gameLoop()
// client prediction // client prediction
gc.ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]); gc.ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
d.draw();
} }
export {gameLoop} export {gameLoop}

View File

@@ -1,3 +1,3 @@
export {pong, gc} from "./pong.js" export {pong, gc} from "./pong.js"
export {clientInfo} from "./ws.js" export {socket, clientInfo} from "./ws.js"

View File

@@ -1,8 +1,8 @@
import {pong, gc, clientInfo} from "./global.js"
import * as d from "./draw.js"; import { pong, gc, socket, clientInfo } from "./global.js"
import { socket } from "./ws.js";
import * as ev from "../shared_js/class/Event.js" import * as ev from "../shared_js/class/Event.js"
import * as en from "../shared_js/enums.js" import * as en from "../shared_js/enums.js"
import { InputHistory } from "./class/InputHistory.js"
export let gridDisplay = false; export let gridDisplay = false;
@@ -10,17 +10,6 @@ let actual_time: number = Date.now();
let last_time: number; let last_time: number;
let delta_time: number; let delta_time: number;
class InputHistory {
input: en.InputEnum;
inputId: number;
deltaTime: number;
constructor(input: en.InputEnum, inputId: number, deltaTime: number) {
this.input = input;
this.inputId = inputId;
this.deltaTime = deltaTime;
}
}
const inputHistoryArr: InputHistory[] = []; const inputHistoryArr: InputHistory[] = [];
let id = 0; let id = 0;
/* idMax should be high enough to prevent duplicate "id" in "inputHistoryArr". /* idMax should be high enough to prevent duplicate "id" in "inputHistoryArr".
@@ -29,28 +18,25 @@ const idMax = 999; // 999 arbitrary
function handleInput() function handleInput()
{ {
console.log("handleInput");
last_time = actual_time; last_time = actual_time;
actual_time = Date.now(); actual_time = Date.now();
delta_time = (actual_time - last_time) / 1000; delta_time = (actual_time - last_time) / 1000;
const keys = pong.keys;
if (keys.length == 0) {
return;
}
// console.log("handleInput");
if (id > idMax) { if (id > idMax) {
id = 0; id = 0;
} }
var keys = pong.keys;
if (keys.length == 0)
return;
if (keys.indexOf("g") != -1) if (keys.indexOf("g") != -1)
{ {
if (gridDisplay)
{
pong.clear();
d.drawStatic();
}
gridDisplay = !gridDisplay; gridDisplay = !gridDisplay;
pong.deleteKey("g"); pong.deleteKey("g");
} }
playerMove(delta_time, keys); playerMove(delta_time, keys);
} }
@@ -96,15 +82,8 @@ function repeatInput(lastInputId: number)
return false; return false;
}); });
// console.log("repeatInput, lastInputId = " + lastInputId);
// console.log("repeatInput, before splice up to index " + i);
// console.log(inputHistoryArr);
inputHistoryArr.splice(0, i+1); inputHistoryArr.splice(0, i+1);
// console.log("repeatInput, after splice");
// console.log(inputHistoryArr);
inputHistoryArr.forEach((value: InputHistory) => { inputHistoryArr.forEach((value: InputHistory) => {
playerMovePrediction(value.deltaTime, value.input); playerMovePrediction(value.deltaTime, value.input);
}); });

View File

@@ -1,13 +1,13 @@
import {GameArea} from "./class/GameArea.js";
import * as d from "./draw.js";
import {gameLoop} from "./gameLoop.js"
import * as c from "./constants.js" import * as c from "./constants.js"
import { GameArea } from "./class/GameArea.js";
import { GameComponentsClient } from "./class/GameComponentsClient.js"; import { GameComponentsClient } from "./class/GameComponentsClient.js";
import {countdown} from "./utils.js"; import { handleInput } from "./handleInput.js";
import {handleInput} from "./handleInput.js"; import { gameLoop } from "./gameLoop.js"
import { drawLoop } from "./draw.js";
import { countdown } from "./utils.js";
import {socket} from "./ws.js"; socket; // no-op import {socket} from "./ws.js"; socket; // no-op, just for loading
/* Keys /* Keys
Racket: W/S OR Up/Down Racket: W/S OR Up/Down
@@ -41,6 +41,7 @@ function resumeGame()
}); });
pong.handleInputInterval = window.setInterval(handleInput, c.handleInputIntervalMS); pong.handleInputInterval = window.setInterval(handleInput, c.handleInputIntervalMS);
pong.gameLoopInterval = window.setInterval(gameLoop, c.gameLoopIntervalMS); pong.gameLoopInterval = window.setInterval(gameLoop, c.gameLoopIntervalMS);
pong.gameLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
} }

View File

@@ -1,17 +1,17 @@
import {pong, gc} from "./global.js"
import * as ev from "../shared_js/class/Event.js"
import {matchmaking, matchmakingComplete, startGame} from "./pong.js";
import * as en from "../shared_js/enums.js"
import { RacketClient } from "./class/RectangleClient.js";
import { sleep } from "./utils.js";
import * as c from "./constants.js" import * as c from "./constants.js"
import {soundRoblox} from "./audio.js" import { gc } from "./global.js"
import * as ev from "../shared_js/class/Event.js"
import * as en from "../shared_js/enums.js"
import { matchmaking, matchmakingComplete, startGame } from "./pong.js";
import { RacketClient } from "./class/RectangleClient.js";
import { repeatInput } from "./handleInput.js"; import { repeatInput } from "./handleInput.js";
import { soundRoblox } from "./audio.js"
import { sleep } from "./utils.js";
const wsPort = 8042; const wsPort = 8042;
const wsUrl = "ws://" + document.location.hostname + ":" + wsPort + "/pong"; const wsUrl = "ws://" + document.location.hostname + ":" + wsPort + "/pong";
const socket = new WebSocket(wsUrl, "json"); export const socket = new WebSocket(wsUrl, "json");
class ClientInfo { class ClientInfo {
id = ""; id = "";
@@ -66,16 +66,13 @@ function inGameListener(event: MessageEvent)
const data: ev.ServerEvent = JSON.parse(event.data); const data: ev.ServerEvent = JSON.parse(event.data);
switch (data.type) { switch (data.type) {
case en.EventTypes.gameUpdate: case en.EventTypes.gameUpdate:
console.log("gameUpdate");
// setTimeout(gameUpdate, 1000, data as ev.EventGameUpdate); // artificial latency for testing purpose // setTimeout(gameUpdate, 1000, data as ev.EventGameUpdate); // artificial latency for testing purpose
gameUpdate(data as ev.EventGameUpdate); gameUpdate(data as ev.EventGameUpdate);
break; break;
case en.EventTypes.scoreUpdate: case en.EventTypes.scoreUpdate:
console.log("scoreUpdate");
scoreUpdate(data as ev.EventScoreUpdate); scoreUpdate(data as ev.EventScoreUpdate);
break; break;
case en.EventTypes.matchEnd: case en.EventTypes.matchEnd:
console.log("matchEnd");
matchEnd(data as ev.EventMatchEnd); matchEnd(data as ev.EventMatchEnd);
break; break;
} }
@@ -83,6 +80,7 @@ function inGameListener(event: MessageEvent)
function gameUpdate(data: ev.EventGameUpdate) function gameUpdate(data: ev.EventGameUpdate)
{ {
console.log("gameUpdate");
gc.playerLeft.pos.y = Math.floor(data.playerLeft.y); gc.playerLeft.pos.y = Math.floor(data.playerLeft.y);
gc.playerRight.pos.y = Math.floor(data.playerRight.y); gc.playerRight.pos.y = Math.floor(data.playerRight.y);
gc.ball.pos.x = Math.floor(data.ball.x); gc.ball.pos.x = Math.floor(data.ball.x);
@@ -94,6 +92,7 @@ function gameUpdate(data: ev.EventGameUpdate)
function scoreUpdate(data: ev.EventScoreUpdate) function scoreUpdate(data: ev.EventScoreUpdate)
{ {
console.log("scoreUpdate");
if (clientInfo.side === en.PlayerSide.left && data.scoreRight > gc.scoreRight.value) { if (clientInfo.side === en.PlayerSide.left && data.scoreRight > gc.scoreRight.value) {
soundRoblox.play(); soundRoblox.play();
} }
@@ -106,6 +105,7 @@ function scoreUpdate(data: ev.EventScoreUpdate)
function matchEnd(data: ev.EventMatchEnd) function matchEnd(data: ev.EventMatchEnd)
{ {
console.log("matchEnd");
if (data.winner === clientInfo.side) { if (data.winner === clientInfo.side) {
alert("WIN"); // placeholder TODO draw alert("WIN"); // placeholder TODO draw
} }
@@ -113,5 +113,3 @@ function matchEnd(data: ev.EventMatchEnd)
alert("LOSE"); // placeholder TODO draw alert("LOSE"); // placeholder TODO draw
} }
} }
export {socket}

View File

@@ -1,6 +1,6 @@
import {WebSocket} from "ws"; import { WebSocket } from "../wsServer.js";
import {Racket} from "../../shared_js/class/Rectangle.js"; import { Racket } from "../../shared_js/class/Rectangle.js";
import { GameSession } from "./GameSession.js"; import { GameSession } from "./GameSession.js";
class Client { class Client {

View File

@@ -8,16 +8,13 @@ import { GameComponents } from "../../shared_js/class/GameComponents.js";
// const mockCTX = new CanvasRenderingContext2D(); // const mockCTX = new CanvasRenderingContext2D();
class GameComponentsServer extends GameComponents { class GameComponentsServer extends GameComponents {
scoreLeft: number; scoreLeft: number = 0;
scoreRight: number; scoreRight: number = 0;
ballInPlay: boolean; ballInPlay: boolean = false;
constructor() constructor()
{ {
// super(mockCTX); // super(mockCTX);
super(); super();
this.scoreLeft = 0;
this.scoreRight = 0;
this.ballInPlay = false;
} }
} }

View File

@@ -1,11 +1,11 @@
import { ClientPlayer } from "./Client";
import { GameComponents } from "../../shared_js/class/GameComponents.js";
import { clientInputListener } from "../wsServer.js";
import * as c from "../constants.js"
import { GameComponentsServer } from "./GameComponentsServer.js";
import { random } from "../../shared_js/utils.js";
import * as en from "../../shared_js/enums.js" import * as en from "../../shared_js/enums.js"
import * as ev from "../../shared_js/class/Event.js" import * as ev from "../../shared_js/class/Event.js"
import * as c from "../constants.js"
import { ClientPlayer } from "./Client";
import { GameComponentsServer } from "./GameComponentsServer.js";
import { clientInputListener } from "../wsServer.js";
import { random } from "../../shared_js/utils.js";
/* /*
Arg "s: GameSession" replace "this: GameSession" for use with setTimeout(), Arg "s: GameSession" replace "this: GameSession" for use with setTimeout(),

View File

@@ -4,17 +4,16 @@ import url from "url";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import {wsServer} from "./wsServer.js"; wsServer; // no-op import {wsServer} from "./wsServer.js"; wsServer; // no-op, just for loading
const hostname = "localhost"; const hostname = "localhost";
const port = 8080; const port = 8080;
const root = "../../www/"; const root = "../../www/";
const server = http.createServer((req, res) => { const server = http.createServer((req, res) => {
// var q = new URL(req.url, `http://${req.getHeaders().host}`) // let q = new URL(req.url, `http://${req.getHeaders().host}`)
// @ts-ignore let q = url.parse(req.url, true);
var q = url.parse(req.url, true); let filename = root + q.pathname;
var filename = root + q.pathname;
fs.readFile(filename, (err, data) => { fs.readFile(filename, (err, data) => {
if (err) { if (err) {
res.writeHead(404, {"Content-Type": "text/html"}); res.writeHead(404, {"Content-Type": "text/html"});

View File

@@ -1,7 +1,7 @@
import { WebSocketServer, WebSocket as BaseLibWebSocket } from "ws"; import { WebSocketServer, WebSocket as BaseLibWebSocket } from "ws";
class WebSocket extends BaseLibWebSocket { export class WebSocket extends BaseLibWebSocket {
id?: string; id?: string;
} }
@@ -10,8 +10,8 @@ import { v4 as uuidv4 } from 'uuid';
import * as en from "../shared_js/enums.js" import * as en from "../shared_js/enums.js"
import * as ev from "../shared_js/class/Event.js" import * as ev from "../shared_js/class/Event.js"
import {Client, ClientPlayer} from "./class/Client.js" import { Client, ClientPlayer } from "./class/Client.js"
import {GameSession} from "./class/GameSession.js" import { GameSession } from "./class/GameSession.js"
// pas indispensable d'avoir un autre port si le WebSocket est relié à un serveur http préexistant ? // pas indispensable d'avoir un autre port si le WebSocket est relié à un serveur http préexistant ?
const wsPort = 8042; const wsPort = 8042;
@@ -61,8 +61,8 @@ function clientAnnounceListener(this: WebSocket, data: string)
// TODO: reconnection with msg.id ? // TODO: reconnection with msg.id ?
// TODO: spectator/player distinction with msg.type ? // TODO: spectator/player distinction with msg.type ?
this.send(JSON.stringify( new ev.EventAssignId(this.id) )) this.send(JSON.stringify( new ev.EventAssignId(this.id) ));
this.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchmakingInProgress) )) this.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchmakingInProgress) ));
matchmaking(this); matchmaking(this);
} }
else { else {
@@ -110,7 +110,7 @@ function matchmaking(socket: WebSocket)
}); });
gameSession.playersMap.forEach( (client) => { gameSession.playersMap.forEach( (client) => {
/* set listener last to be absolutly sure there no early game launch /* set listener last to be absolutly sure there no early game launch
(unlikely, but theoretically possible) */ (unlikely, but possible in theory) */
client.socket.once("message", playerReadyConfirmationListener); client.socket.once("message", playerReadyConfirmationListener);
}); });
} }

View File

@@ -3,8 +3,8 @@
For the moment, this code is only used by the server. For the moment, this code is only used by the server.
*/ */
import * as c from "../constants.js" import * as c from "../constants.js"
import {VectorInteger} from "./Vector.js"; import { VectorInteger } from "./Vector.js";
import {Rectangle, Racket, Ball} from "./Rectangle.js"; import { Rectangle, Racket, Ball } from "./Rectangle.js";
class GameComponents { class GameComponents {
wallTop: Rectangle; wallTop: Rectangle;

View File

@@ -1,6 +1,6 @@
import {Vector, VectorInteger} from "./Vector.js"; import { Vector, VectorInteger } from "./Vector.js";
import {Component, Moving} from "./interface.js"; import { Component, Moving } from "./interface.js";
class Rectangle implements Component { class Rectangle implements Component {
pos: VectorInteger; pos: VectorInteger;

View File

@@ -1,5 +1,5 @@
import {Vector, VectorInteger} from "./Vector.js"; import { Vector, VectorInteger } from "./Vector.js";
interface Component { interface Component {
pos: VectorInteger; pos: VectorInteger;