Skip to content

Commit 20fc8ef

Browse files
authored
v01.2
1 parent 970ee6d commit 20fc8ef

File tree

12 files changed

+303
-0
lines changed

12 files changed

+303
-0
lines changed

dodge-and-survive/LICENSE

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2025, hellenicdev
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice, this
9+
list of conditions and the following disclaimer.
10+
11+
2. Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
3. Neither the name of the copyright holder nor the names of its
16+
contributors may be used to endorse or promote products derived from
17+
this software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

dodge-and-survive/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# 3d-multiplayer-shooter

dodge-and-survive/background.jpg

738 KB
Loading

dodge-and-survive/enemy.png

6.85 KB
Loading

dodge-and-survive/index.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Dodge & Survive</title>
7+
<link rel="stylesheet" href="style.css" />
8+
</head>
9+
<body>
10+
<canvas id="gameCanvas"></canvas>
11+
12+
<!-- Sound Effects -->
13+
<audio id="bg-music" src="sounds/bg.mp3" loop preload="auto"></audio>
14+
<audio id="hit-sound" src="sounds/hit.wav" preload="auto"></audio>
15+
<audio id="spawn-sound" src="sounds/spawn.wav" preload="auto"></audio>
16+
17+
<script src="script.js"></script>
18+
</body>
19+
</html>

dodge-and-survive/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "3d-multiplayer-game",
3+
"version": "1.0.0",
4+
"main": "server/index.js",
5+
"scripts": {
6+
"start": "node server/index.js"
7+
},
8+
"dependencies": {
9+
"express": "^4.18.2",
10+
"socket.io": "^4.5.4"
11+
}
12+
}

dodge-and-survive/player.png

1.02 KB
Loading

dodge-and-survive/script.js

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
let paused = false;
2+
const canvas = document.getElementById('gameCanvas');
3+
const ctx = canvas.getContext('2d');
4+
5+
// Load images
6+
const bgImage = new Image();
7+
bgImage.src = 'background.jpg';
8+
9+
const playerImage = new Image();
10+
playerImage.src = 'player.png';
11+
12+
const enemyImage = new Image();
13+
enemyImage.src = 'enemy.png';
14+
15+
// Resize canvas
16+
function resizeCanvas() {
17+
canvas.width = window.innerWidth;
18+
canvas.height = window.innerHeight;
19+
}
20+
resizeCanvas();
21+
window.addEventListener('resize', resizeCanvas);
22+
23+
// Load sounds
24+
const bgMusic = document.getElementById('bg-music');
25+
const hitSound = document.getElementById('hit-sound');
26+
const spawnSound = document.getElementById('spawn-sound');
27+
28+
// Game objects
29+
const player = {
30+
x: canvas.width / 2 - 20,
31+
y: canvas.height - 60,
32+
width: 40,
33+
height: 40,
34+
speed: 5,
35+
moveLeft: false,
36+
moveRight: false,
37+
bullets: []
38+
};
39+
40+
let enemies = [];
41+
let score = 0;
42+
let lives = 3;
43+
let gameOver = false;
44+
let started = false;
45+
46+
// Draw functions
47+
function drawBackground() {
48+
ctx.drawImage(bgImage, 0, 0, canvas.width, canvas.height);
49+
}
50+
51+
function drawPlayer() {
52+
ctx.drawImage(playerImage, player.x, player.y, player.width, player.height);
53+
}
54+
55+
function drawEnemies() {
56+
enemies.forEach(e => ctx.drawImage(enemyImage, e.x, e.y, e.width, e.height));
57+
}
58+
59+
function drawBullets() {
60+
ctx.fillStyle = 'yellow';
61+
player.bullets.forEach(b => ctx.fillRect(b.x, b.y, 4, 10));
62+
}
63+
64+
function drawUI() {
65+
ctx.fillStyle = 'white';
66+
ctx.font = '20px Arial';
67+
ctx.fillText(`Score: ${score}`, 10, 30);
68+
ctx.fillText(`Lives: ${lives}`, canvas.width - 100, 30);
69+
}
70+
71+
function drawGameOver() {
72+
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
73+
ctx.fillRect(0, 0, canvas.width, canvas.height);
74+
ctx.fillStyle = 'white';
75+
ctx.font = '36px Arial';
76+
ctx.fillText('Game Over', canvas.width / 2 - 100, canvas.height / 2 - 20);
77+
ctx.font = '24px Arial';
78+
ctx.fillText(`Score: ${score}`, canvas.width / 2 - 40, canvas.height / 2 + 20);
79+
ctx.fillText('Press R to Restart', canvas.width / 2 - 90, canvas.height / 2 + 60);
80+
}
81+
82+
function drawStartScreen() {
83+
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
84+
ctx.fillRect(0, 0, canvas.width, canvas.height);
85+
ctx.fillStyle = 'white';
86+
ctx.font = '28px Arial';
87+
ctx.fillText('Dodge & Survive', canvas.width / 2 - 100, canvas.height / 2 - 30);
88+
ctx.font = '20px Arial';
89+
ctx.fillText('Press Enter or Click to Start', canvas.width / 2 - 110, canvas.height / 2 + 10);
90+
}
91+
92+
// Game logic
93+
function updateEnemies() {
94+
enemies.forEach(e => e.y += 2);
95+
enemies = enemies.filter(e => e.y < canvas.height);
96+
}
97+
98+
function updateBullets() {
99+
player.bullets.forEach(b => b.y -= 8);
100+
player.bullets = player.bullets.filter(b => b.y > 0);
101+
}
102+
103+
function checkCollision(a, b) {
104+
return (
105+
a.x < b.x + b.width &&
106+
a.x + (a.width || 4) > b.x &&
107+
a.y < b.y + b.height &&
108+
a.y + (a.height || 10) > b.y
109+
);
110+
}
111+
112+
function handleCollisions() {
113+
// Bullet hits enemy
114+
for (let i = enemies.length - 1; i >= 0; i--) {
115+
for (let j = player.bullets.length - 1; j >= 0; j--) {
116+
if (checkCollision(player.bullets[j], enemies[i])) {
117+
enemies.splice(i, 1);
118+
player.bullets.splice(j, 1);
119+
score += 1;
120+
hitSound.currentTime = 0;
121+
hitSound.play();
122+
break;
123+
}
124+
}
125+
}
126+
127+
// Enemy hits player
128+
for (let i = enemies.length - 1; i >= 0; i--) {
129+
if (checkCollision(enemies[i], player)) {
130+
enemies.splice(i, 1);
131+
lives -= 1;
132+
hitSound.currentTime = 0;
133+
hitSound.play();
134+
if (lives <= 0) {
135+
gameOver = true;
136+
bgMusic.pause();
137+
bgMusic.currentTime = 0;
138+
}
139+
}
140+
}
141+
}
142+
143+
function shoot() {
144+
player.bullets.push({
145+
x: player.x + player.width / 2 - 2,
146+
y: player.y,
147+
});
148+
}
149+
150+
function createEnemy() {
151+
const x = Math.random() * (canvas.width - 40);
152+
enemies.push({ x, y: -40, width: 40, height: 40 });
153+
spawnSound.currentTime = 0;
154+
spawnSound.play();
155+
}
156+
157+
function resetGame() {
158+
enemies = [];
159+
player.bullets = [];
160+
score = 0;
161+
lives = 3;
162+
gameOver = false;
163+
player.x = canvas.width / 2 - 20;
164+
bgMusic.currentTime = 0;
165+
bgMusic.play();
166+
}
167+
168+
// Game loop
169+
function gameLoop() {
170+
drawBackground();
171+
172+
if (!started) {
173+
drawStartScreen();
174+
requestAnimationFrame(gameLoop);
175+
return;
176+
}
177+
178+
if (!gameOver) {
179+
if (player.moveLeft && player.x > 0) player.x -= player.speed;
180+
if (player.moveRight && player.x + player.width < canvas.width)
181+
player.x += player.speed;
182+
183+
updateEnemies();
184+
updateBullets();
185+
handleCollisions();
186+
187+
drawPlayer();
188+
drawEnemies();
189+
drawBullets();
190+
drawUI();
191+
} else {
192+
drawGameOver();
193+
}
194+
195+
requestAnimationFrame(gameLoop);
196+
}
197+
198+
// Controls
199+
document.addEventListener('keydown', (e) => {
200+
if (e.key === 'ArrowLeft' || e.key === 'a') player.moveLeft = true;
201+
if (e.key === 'ArrowRight' || e.key === 'd') player.moveRight = true;
202+
if (e.key === ' ' && started && !gameOver) shoot();
203+
if ((e.key === 'Enter' || e.key === ' ') && !started) {
204+
started = true;
205+
bgMusic.play();
206+
}
207+
if (e.key === 'r' && gameOver) resetGame();
208+
});
209+
210+
document.addEventListener('keyup', (e) => {
211+
if (e.key === 'ArrowLeft' || e.key === 'a') player.moveLeft = false;
212+
if (e.key === 'ArrowRight' || e.key === 'd') player.moveRight = false;
213+
});
214+
215+
canvas.addEventListener('click', () => {
216+
if (!started) {
217+
started = true;
218+
bgMusic.play();
219+
}
220+
});
221+
222+
// Start enemy spawning
223+
setInterval(() => {
224+
if (started && !gameOver) createEnemy();
225+
}, 1000);
226+
227+
window.onload = gameLoop;

dodge-and-survive/sounds/bg.mp3

4.53 MB
Binary file not shown.

dodge-and-survive/sounds/hit.wav

308 KB
Binary file not shown.

0 commit comments

Comments
 (0)