Skip to content

Commit

Permalink
start multiplayer
Browse files Browse the repository at this point in the history
  • Loading branch information
rjawesome committed Dec 7, 2023
1 parent 69d3a67 commit 87580d6
Show file tree
Hide file tree
Showing 14 changed files with 1,420 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ scripts/__pycache__/convert_notebooks.cpython-311.pyc
_posts/*_IPYNB_2_.md
nohup.out
Gemfile.lock
node_modules
192 changes: 192 additions & 0 deletions _posts/2023-11-27-CSSE-mutliplayer-game.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
---
layout: base
title: Socket.io Multiplayer Game
description: Using socket.io for multiplayer game
type: ccc
courses: { csse: {week: 20} }
image: /images/platformer/backgrounds/hills.png
---

<style>
#gameBegin, #controls, #gameOver {
position: relative;
z-index: 2; /*Ensure the controls are on top*/
}
</style>

<!-- Prepare DOM elements -->
<!-- Wrap both the canvas and controls in a container div -->
<div id="canvasContainer">
<div id="gameBegin" hidden>
<button id="startGame">Start Game</button>
</div>
<div id="controls"> <!-- Controls -->
<!-- Background controls -->
<button id="toggleCanvasEffect">Invert</button>
</div>
<div id="gameOver" hidden>
<button id="restartGame">Restart</button>
</div>
</div>


<script type="module">
// Imports
import GameEnv from '{{site.baseurl}}/assets/js/multiplayer/GameEnv.js';
import GameLevel from '{{site.baseurl}}/assets/js/multiplayer/GameLevel.js';
import GameControl from '{{site.baseurl}}/assets/js/multiplayer/GameControl.js';


/* ==========================================
* ======= Data Definitions =================
* ==========================================
*/

// Define assets for the game
var assets = {
obstacles: {
tube: { src: "/images/platformer/obstacles/tube.png" },
},
platforms: {
grass: { src: "/images/platformer/platforms/grass.png" },
alien: { src: "/images/platformer/platforms/alien.png" }
},
backgrounds: {
start: { src: "/images/platformer/backgrounds/home.png" },
hills: { src: "/images/platformer/backgrounds/hills.png" },
planet: { src: "/images/platformer/backgrounds/planet.jpg" },
castles: { src: "/images/platformer/backgrounds/castles.png" },
end: { src: "/images/platformer/backgrounds/game_over.png" }
},
players: {
mario: {
src: "/images/platformer/sprites/mario.png",
width: 256,
height: 256,
w: { row: 10, frames: 15 },
wa: { row: 11, frames: 15 },
wd: { row: 10, frames: 15 },
a: { row: 3, frames: 7, idleFrame: { column: 7, frames: 0 } },
s: { },
d: { row: 2, frames: 7, idleFrame: { column: 7, frames: 0 } }
},
monkey: {
src: "/images/platformer/sprites/monkey.png",
width: 40,
height: 40,
w: { row: 9, frames: 15 },
wa: { row: 9, frames: 15 },
wd: { row: 9, frames: 15 },
a: { row: 1, frames: 15, idleFrame: { column: 7, frames: 0 } },
s: { row: 12, frames: 15 },
d: { row: 0, frames: 15, idleFrame: { column: 7, frames: 0 } }
}
}
};

// add File to assets, ensure valid site.baseurl
Object.keys(assets).forEach(category => {
Object.keys(assets[category]).forEach(assetName => {
assets[category][assetName]['file'] = "{{site.baseurl}}" + assets[category][assetName].src;
});
});

/* ==========================================
* ===== Game Level Call Backs ==============
* ==========================================
*/

// Level completion tester
function testerCallBack() {
// console.log(GameEnv.player?.x)
if (GameEnv.player?.x > GameEnv.innerWidth) {
return true;
} else {
return false;
}
}

// Helper function for button click
function waitForButton(buttonName) {
// resolve the button click
return new Promise((resolve) => {
const waitButton = document.getElementById(buttonName);
const waitButtonListener = () => {
resolve(true);
};
waitButton.addEventListener('click', waitButtonListener);
});
}

// Start button callback
async function startGameCallback() {
const id = document.getElementById("gameBegin");
id.hidden = false;

// Use waitForRestart to wait for the restart button click
await waitForButton('startGame');
id.hidden = true;

return true;
}

// Home screen exits on Game Begin button
function homeScreenCallback() {
// gameBegin hidden means game has started
const id = document.getElementById("gameBegin");
return id.hidden;
}

// Game Over callback
async function gameOverCallBack() {
const id = document.getElementById("gameOver");
id.hidden = false;

// Use waitForRestart to wait for the restart button click
await waitForButton('restartGame');
id.hidden = true;

// Change currentLevel to start/restart value of null
GameEnv.currentLevel = null;

return true;
}

/* ==========================================
* ========== Game Level setup ==============
* ==========================================
* Start/Homme sequence
* a.) the start level awaits for button selection
* b.) the start level automatically cycles to home level
* c.) the home advances to 1st game level when button selection is made
*/
// Start/Home screens
new GameLevel( {tag: "start", callback: startGameCallback } );
new GameLevel( {tag: "home", background: assets.backgrounds.start, callback: homeScreenCallback } );
// Game screens
new GameLevel( {tag: "hills", background: assets.backgrounds.hills, platform: assets.platforms.grass, player: assets.players.mario, tube: assets.obstacles.tube, callback: testerCallBack } );
new GameLevel( {tag: "alien", background: assets.backgrounds.planet, platform: assets.platforms.alien, player: assets.players.monkey, callback: testerCallBack } );
// Game Over screen
new GameLevel( {tag: "end", background: assets.backgrounds.end, callback: gameOverCallBack } );

/* ==========================================
* ========== Game Control ==================
* ==========================================
*/

// create listeners
toggleCanvasEffect.addEventListener('click', GameEnv.toggleInvert);
window.addEventListener('resize', GameEnv.resize);

// more listeners
GameEnv.socket.on("stateUpdate", (data) => {
console.log("update", data)
for (var gameObj of GameEnv.gameObjects) {
gameObj.updateInfo(data)
}
})

// start game
GameControl.gameLoop();

</script>
54 changes: 54 additions & 0 deletions assets/js/multiplayer/Background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import GameEnv from './GameEnv.js';
import GameObject from './GameObject.js';

export class Background extends GameObject {
constructor(canvas, image, speedRatio) {
super(canvas, image, speedRatio);
}

/* Update uses modulo math to cycle to start at width extent
* x is position in cycle
* speed can be used to scroll faster
* width is extent of background image
*/
update() {
this.x = (this.x - this.speed) % this.width;
}

/* To draws are used to capture primary frame and wrap around ot next frame
* x to y is primary draw
* x + width to y is wrap around draw
*/
draw() {
this.ctx.drawImage(this.image, this.x, this.y);
this.ctx.drawImage(this.image, this.x + this.width, this.y);
}

/* Background camvas is set to screen
* the ADJUST contant elements portions of image that don't wrap well
* the GameEnv.top is a getter used to set canvas under Menu
* the GameEnv.bottom is setter used to establish game bottom at offsetHeight of canvas
*/
size() {
// Update canvas size
const ADJUST = 1 // visual layer adjust; alien_planet.jpg: 1.42, try 1 for others

const canvasWidth = GameEnv.innerWidth;
const canvasHeight = canvasWidth / this.aspect_ratio;
GameEnv.backgroundHeight = canvasHeight;
const canvasLeft = 0;

this.canvas.width = this.width;
this.canvas.height = this.height;
this.canvas.style.width = `${canvasWidth}px`;
this.canvas.style.height = `${GameEnv.backgroundHeight}px`;
this.canvas.style.position = 'absolute';
this.canvas.style.left = `${canvasLeft}px`;
this.canvas.style.top = `${GameEnv.top}px`;

// set bottom of game to new background height
GameEnv.setBottom();
}
}

export default Background;
Loading

0 comments on commit 87580d6

Please sign in to comment.