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"
export const soundPong: HTMLAudioElement[] = [];
export const soundPongArr: HTMLAudioElement[] = [];
for (let i = 0; i <= 32; i++) {
soundPong.push(new Audio("http://localhost:8080/sound/pong/"+i+".mp3"));
soundPong[i].volume = c.soundPongVolume;
soundPongArr.push(new Audio("http://localhost:8080/sound/pong/"+i+".mp3"));
soundPongArr[i].volume = c.soundPongVolume;
soundPongArr[i].muted = c.soundMutedFlag;
}
export const soundRoblox = new Audio("http://localhost:8080/sound/roblox-oof.mp3");
soundRoblox.volume = c.soundRobloxVolume;
soundRoblox.muted = c.soundMutedFlag;

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
import * as c from "../constants.js"
import {Vector, VectorInteger} from "../../shared_js/class/Vector.js";
import {TextElem, TextNumericValue} from "./Text.js";
import { GameComponentsForClient } from "./GameComponents.js";
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import { TextElem, TextNumericValue } from "./Text.js";
import { RectangleClient, Line } from "./RectangleClient.js";
import { GameComponentsForClient } from "./GameComponents.js";
class GameComponentsClient extends GameComponentsForClient {
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 {Component, GraphicComponent, Moving} from "../../shared_js/class/interface.js";
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import { Component, GraphicComponent, Moving } from "../../shared_js/class/interface.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";
function updateRectangle(this: RectangleClient) {
@@ -91,7 +91,7 @@ class BallClient extends Ball implements GraphicComponent {
}
bounce(collider?: Rectangle) {
this._bounceAlgo(collider);
soundPong[ Math.floor(random(0, soundPong.length)) ].play();
soundPongArr[ Math.floor(random(0, soundPongArr.length)) ].play();
}
/* protected _bouncePlayer(collider: Racket) {
this._bouncePlayerAlgo(collider);

View File

@@ -1,6 +1,6 @@
import {Vector, VectorInteger} from "../../shared_js/class/Vector.js";
import {Component, Moving} from "../../shared_js/class/interface.js";
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import { Component } from "../../shared_js/class/interface.js";
// conflict with Text
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 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.
export const handleInputIntervalMS = 15; // millisecond
export const gameLoopIntervalMS = 15; // millisecond
export const drawLoopIntervalMS = 15; // millisecond
export const soundRobloxVolume = 0; // between 0 and 1
export const soundPongVolume = 0; // between 0 and 1
export const soundMutedFlag = true;
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();
@@ -10,6 +11,12 @@ function draw()
if (gridDisplay) {
drawGrid();
}
drawDynamic();
}
function drawDynamic()
{
gc.scoreLeft.update();
gc.scoreRight.update();
gc.playerLeft.update();
@@ -35,4 +42,4 @@ function drawGrid()
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 last_time: number;
@@ -18,8 +18,6 @@ function gameLoop()
// client prediction
gc.ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
d.draw();
}
export {gameLoop}

View File

@@ -1,3 +1,3 @@
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 { socket } from "./ws.js";
import { pong, gc, socket, clientInfo } from "./global.js"
import * as ev from "../shared_js/class/Event.js"
import * as en from "../shared_js/enums.js"
import { InputHistory } from "./class/InputHistory.js"
export let gridDisplay = false;
@@ -10,17 +10,6 @@ let actual_time: number = Date.now();
let last_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[] = [];
let id = 0;
/* idMax should be high enough to prevent duplicate "id" in "inputHistoryArr".
@@ -29,28 +18,25 @@ const idMax = 999; // 999 arbitrary
function handleInput()
{
console.log("handleInput");
last_time = actual_time;
actual_time = Date.now();
delta_time = (actual_time - last_time) / 1000;
const keys = pong.keys;
if (keys.length == 0) {
return;
}
// console.log("handleInput");
if (id > idMax) {
id = 0;
}
var keys = pong.keys;
if (keys.length == 0)
return;
if (keys.indexOf("g") != -1)
{
if (gridDisplay)
{
pong.clear();
d.drawStatic();
}
gridDisplay = !gridDisplay;
pong.deleteKey("g");
}
playerMove(delta_time, keys);
}
@@ -96,15 +82,8 @@ function repeatInput(lastInputId: number)
return false;
});
// console.log("repeatInput, lastInputId = " + lastInputId);
// console.log("repeatInput, before splice up to index " + i);
// console.log(inputHistoryArr);
inputHistoryArr.splice(0, i+1);
// console.log("repeatInput, after splice");
// console.log(inputHistoryArr);
inputHistoryArr.forEach((value: InputHistory) => {
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 { GameArea } from "./class/GameArea.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
Racket: W/S OR Up/Down
@@ -41,6 +41,7 @@ function resumeGame()
});
pong.handleInputInterval = window.setInterval(handleInput, c.handleInputIntervalMS);
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 {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 { soundRoblox } from "./audio.js"
import { sleep } from "./utils.js";
const wsPort = 8042;
const wsUrl = "ws://" + document.location.hostname + ":" + wsPort + "/pong";
const socket = new WebSocket(wsUrl, "json");
export const socket = new WebSocket(wsUrl, "json");
class ClientInfo {
id = "";
@@ -66,16 +66,13 @@ function inGameListener(event: MessageEvent)
const data: ev.ServerEvent = JSON.parse(event.data);
switch (data.type) {
case en.EventTypes.gameUpdate:
console.log("gameUpdate");
// setTimeout(gameUpdate, 1000, data as ev.EventGameUpdate); // artificial latency for testing purpose
gameUpdate(data as ev.EventGameUpdate);
break;
case en.EventTypes.scoreUpdate:
console.log("scoreUpdate");
scoreUpdate(data as ev.EventScoreUpdate);
break;
case en.EventTypes.matchEnd:
console.log("matchEnd");
matchEnd(data as ev.EventMatchEnd);
break;
}
@@ -83,6 +80,7 @@ function inGameListener(event: MessageEvent)
function gameUpdate(data: ev.EventGameUpdate)
{
console.log("gameUpdate");
gc.playerLeft.pos.y = Math.floor(data.playerLeft.y);
gc.playerRight.pos.y = Math.floor(data.playerRight.y);
gc.ball.pos.x = Math.floor(data.ball.x);
@@ -94,6 +92,7 @@ function gameUpdate(data: ev.EventGameUpdate)
function scoreUpdate(data: ev.EventScoreUpdate)
{
console.log("scoreUpdate");
if (clientInfo.side === en.PlayerSide.left && data.scoreRight > gc.scoreRight.value) {
soundRoblox.play();
}
@@ -106,6 +105,7 @@ function scoreUpdate(data: ev.EventScoreUpdate)
function matchEnd(data: ev.EventMatchEnd)
{
console.log("matchEnd");
if (data.winner === clientInfo.side) {
alert("WIN"); // placeholder TODO draw
}
@@ -113,5 +113,3 @@ function matchEnd(data: ev.EventMatchEnd)
alert("LOSE"); // placeholder TODO draw
}
}
export {socket}

View File

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

View File

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

View File

@@ -4,17 +4,16 @@ import url from "url";
import fs from "fs";
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 port = 8080;
const root = "../../www/";
const server = http.createServer((req, res) => {
// var q = new URL(req.url, `http://${req.getHeaders().host}`)
// @ts-ignore
var q = url.parse(req.url, true);
var filename = root + q.pathname;
// let q = new URL(req.url, `http://${req.getHeaders().host}`)
let q = url.parse(req.url, true);
let filename = root + q.pathname;
fs.readFile(filename, (err, data) => {
if (err) {
res.writeHead(404, {"Content-Type": "text/html"});

View File

@@ -1,7 +1,7 @@
import { WebSocketServer, WebSocket as BaseLibWebSocket } from "ws";
class WebSocket extends BaseLibWebSocket {
export class WebSocket extends BaseLibWebSocket {
id?: string;
}
@@ -10,8 +10,8 @@ import { v4 as uuidv4 } from 'uuid';
import * as en from "../shared_js/enums.js"
import * as ev from "../shared_js/class/Event.js"
import {Client, ClientPlayer} from "./class/Client.js"
import {GameSession} from "./class/GameSession.js"
import { Client, ClientPlayer } from "./class/Client.js"
import { GameSession } from "./class/GameSession.js"
// pas indispensable d'avoir un autre port si le WebSocket est relié à un serveur http préexistant ?
const wsPort = 8042;
@@ -61,8 +61,8 @@ function clientAnnounceListener(this: WebSocket, data: string)
// TODO: reconnection with msg.id ?
// TODO: spectator/player distinction with msg.type ?
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.EventAssignId(this.id) ));
this.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchmakingInProgress) ));
matchmaking(this);
}
else {
@@ -110,7 +110,7 @@ function matchmaking(socket: WebSocket)
});
gameSession.playersMap.forEach( (client) => {
/* 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);
});
}

View File

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

View File

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

View File

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