Added JQuery, fixed offline, added start button

This commit is contained in:
Nikita Iryupin 2021-06-05 10:14:16 +02:00
parent e3d38cd730
commit 5526696c96
17 changed files with 347 additions and 25 deletions

Binary file not shown.

View File

@ -1,13 +1,48 @@
<!doctype html> <!doctype html>
<html> <html>
<head lang='en'> <head lang='en'>
<meta charset='utf-8'> <meta charset='utf-8'>
<meta name='viewport' content='width=device-width'> <meta name='viewport' content='width=device-width'>
<link rel="icon" href="./favicon.ico"/> <link rel="icon" href="./favicon.ico" />
<link rel="stylesheet" href="./styles/main.css">
<link rel="stylesheet" href="./styles/tank-animation.css">
<title>BattleCity.js</title> <title>BattleCity.js</title>
<script src='bundle.js'></script> <script src="./libs/jquery-3.6.0.min.js"></script>
<script src='./bundle.js'></script>
</head> </head>
<body> <body>
<main><canvas id="canvas"></canvas></main> <main class="wrapper">
<canvas id="canvas"></canvas>
<div class="loading">
<div class="center">
<div><div class="pixel"></div></div>
<div class="text-loading">Loading...</div>
<div class='container start-level-button'>
<a href='#' class='playBut'>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="213.7px"
height="213.7px" viewBox="0 0 213.7 213.7" enable-background="new 0 0 213.7 213.7"
xml:space="preserve">
<polygon class='triangle' id="XMLID_18_" fill="none" stroke-width="7" stroke-linecap="round"
stroke-linejoin="round" stroke-miterlimit="10" points="
73.5,62.5 148.5,105.8 73.5,149.1 " />
<circle class='circle' id="XMLID_17_" fill="none" stroke-width="7" stroke-linecap="round"
stroke-linejoin="round" stroke-miterlimit="10" cx="106.8" cy="106.8" r="103.3" />
</svg>
</a>
</div>
</div>
</div>
</main>
</body> </body>
</html> </html>

2
public/libs/jquery-3.6.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

BIN
public/sound/brick.ogg Normal file

Binary file not shown.

BIN
public/sound/explosion.ogg Normal file

Binary file not shown.

BIN
public/sound/gameover.ogg Normal file

Binary file not shown.

BIN
public/sound/gamestart.ogg Normal file

Binary file not shown.

BIN
public/sound/steel.ogg Normal file

Binary file not shown.

118
public/styles/main.css Normal file
View File

@ -0,0 +1,118 @@
@font-face {
font-family: VT323;
src : url(./../fonts/VT323-Regular.ttf);
}
body {
font-family: VT323, 'Common Pixel', sans-serif;
}
#canvas {
border: 1px solid #f8aa28;
border-radius: 1%;
}
.wrapper {
width : 416px;
height: 416px;
border: 1px solid #f8aa28;
border-radius: 1%;
position: relative;
}
.loading {
width : 100%;
height : 100%;
position : absolute;
top : 0px;
left : 0px;
background-color: black;
border: 1px solid #f8aa28;
border-radius: 1%;
}
.center {
display: flex;
flex-direction: column;
align-items: center;
justify-items: center;
}
.center:first-child {
margin-top: 5%;
text-align: center;
}
.text-loading {
color: white;
}
div.start-level-button {
display: none;
}
.container {
width : 100%;
text-align: center;
margin-top: 10vh;
}
.circle {
stroke : #f8aa28;
stroke-dasharray : 650;
stroke-dashoffset : 650;
-webkit-transition: all 0.5s ease-in-out;
transition: all 0.5s ease-in-out;
opacity : 0.3;
}
.playBut {
display : inline-block;
transition: all 0.5s ease;
-webkit-transition: all 0.5s ease;
}
.playBut .triangle {
-webkit-transition: all 0.7s ease-in-out;
transition: all 0.7s ease-in-out;
stroke-dasharray : 240;
stroke-dashoffset : 480;
stroke : #FFF;
transform : translateY(0)
}
.playBut:hover .triangle {
stroke-dashoffset: 0;
opacity : 1;
stroke : #f8aa28;
animation : nudge 0.7s ease-in-out;
@keyframes nudge {
0% {
transform: translateX(0)
}
30% {
transform: translateX(-5px)
}
50% {
transform: translateX(5px)
}
70% {
transform: translateX(-2px)
}
100% {
transform: translateX(0)
}
}
}
.playBut:hover .circle {
stroke-dashoffset: 0;
opacity : 1;
}

File diff suppressed because one or more lines are too long

43
public/sw.js Normal file
View File

@ -0,0 +1,43 @@
const filesToCache = [
"./",
"fonts/VT323-Regular.ttf",
"bundle.js",
"bundle.js.map",
"favicon.ico",
"nesBattle.png",
"sound/brick.ogg",
"sound/explosion.ogg",
"sound/gameover.ogg",
"sound/gamestart.ogg",
"sound/steel.ogg",
"styles/main.css",
"stages/stage-1.json",
"stages/stage-2.json",
"stages/stage-3.json",
"libs/jquery-3.6.0.min.js"
]
const cacheName = "battleCity";
self.addEventListener('install', event => {
self.skipWaiting();
event.waitUntil(
caches.open(cacheName).then(cache => cache.addAll(filesToCache))
);
});
self.addEventListener('fetch', event => {
event.respondWith (
caches.match(event.request).then(response => {
if (response) return response;
return fetch(event.request)
.then(response => {
if (response.ok)
return caches.open(cacheName).then(cache => {
cache.put(event.request.url, response.clone());
return response;
});
});
})
)
});

View File

@ -7,11 +7,15 @@ import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH; const production = !process.env.ROLLUP_WATCH;
export default { export default {
external: ["jquery"],
input: 'src/main.js', input: 'src/main.js',
output: { output: {
file: 'public/bundle.js', file: 'public/bundle.js',
format: 'iife', // immediately-invoked function expression — suitable for <script> tags format: 'iife', // immediately-invoked function expression — suitable for <script> tags
sourcemap: true sourcemap: true,
globals: {
jquery: "$"
}
}, },
plugins: [ plugins: [
resolve(), // tells Rollup how to find date-fns in node_modules resolve(), // tells Rollup how to find date-fns in node_modules

View File

@ -2,6 +2,7 @@ import { BlockTypeEnum } from "../Blocks/blockTypeEnum";
import { Constants } from "../constants"; import { Constants } from "../constants";
import { Map } from "../Graphics/Map/map"; import { Map } from "../Graphics/Map/map";
import { Tank } from "../Tanks/tank"; import { Tank } from "../Tanks/tank";
import { SoundEnum, SoundManager } from "./soundManager";
export class CollisionManager { export class CollisionManager {
/** /**
@ -20,6 +21,14 @@ export class CollisionManager {
_aiTanks = []; _aiTanks = [];
/**
* @param {SoundManager} soundManager
*/
constructor(soundManager) {
this.soundManager = soundManager;
}
attachMap(map) { attachMap(map) {
this._map = map; this._map = map;
} }
@ -56,6 +65,15 @@ export class CollisionManager {
if (blockColl) { if (blockColl) {
for (let blockInfo of Array.from(blockColl)) { for (let blockInfo of Array.from(blockColl)) {
blockInfo.block.resolveAttack(blockInfo.boxId, direction); blockInfo.block.resolveAttack(blockInfo.boxId, direction);
switch(blockInfo.block.getType()) {
case BlockTypeEnum.Brick:
this.soundManager.playSound(SoundEnum.BrickCollision);
break;
case BlockTypeEnum.Iron:
this.soundManager.playSound(SoundEnum.SteelCollision);
break;
}
} }
return true; return true;
} }
@ -67,6 +85,7 @@ export class CollisionManager {
if (collTank && collTank.id != tank.id) { if (collTank && collTank.id != tank.id) {
collTank.isDestroyed = true; collTank.isDestroyed = true;
this.soundManager.playSound(SoundEnum.Explosion);
return true; return true;
} }
else { else {

View File

@ -6,6 +6,9 @@ import { MapParser } from "../Graphics/Map/mapParser";
import { CollisionManager } from "./collisionManager"; import { CollisionManager } from "./collisionManager";
import { AITankManager } from "./../Tanks/aiTankManager"; import { AITankManager } from "./../Tanks/aiTankManager";
import { DirectionEnum } from "../Tanks/directionEnum"; import { DirectionEnum } from "../Tanks/directionEnum";
import { SoundEnum, SoundManager } from "./soundManager";
import $ from 'jquery';
export class GameField { export class GameField {
constructor(width = 512, height = 512, ctx, canvas) { constructor(width = 512, height = 512, ctx, canvas) {
@ -16,9 +19,10 @@ export class GameField {
this.tileset = null; this.tileset = null;
this.map = null; this.map = null;
this.soundManager = new SoundManager();
this.collisionManager = new CollisionManager(); this.collisionManager = new CollisionManager(this.soundManager);
this.tank = new Tank(Constants.PLAYER_TANK_ID_IMAGE.getValue(), 0, 18*16, this.collisionManager); this.tank = new Tank(Constants.PLAYER_TANK_ID_IMAGE.getValue(), 0, 18 * 16, this.collisionManager);
this.aiTanks = new AITankManager(this.collisionManager); this.aiTanks = new AITankManager(this.collisionManager);
this.collisionManager.setMainTank(this.tank); this.collisionManager.setMainTank(this.tank);
} }
@ -55,36 +59,78 @@ export class GameField {
this.tileset.addTile(386, 0, 28, 28, 200 + DirectionEnum.Down.getValue()); this.tileset.addTile(386, 0, 28, 28, 200 + DirectionEnum.Down.getValue());
this.tileset.addTile(452, 0, 28, 28, 200 + DirectionEnum.Right.getValue()); this.tileset.addTile(452, 0, 28, 28, 200 + DirectionEnum.Right.getValue());
this.tileset.addTile(512, 8*32, 32, 32, BlockTypeEnum.Explosion1); this.tileset.addTile(512, 8 * 32, 32, 32, BlockTypeEnum.Explosion1);
this.tileset.addTile(544, 8*32, 32, 32, BlockTypeEnum.Explosion2); this.tileset.addTile(544, 8 * 32, 32, 32, BlockTypeEnum.Explosion2);
this.tileset.addTile(576, 8*32, 32, 32, BlockTypeEnum.Explosion3); this.tileset.addTile(576, 8 * 32, 32, 32, BlockTypeEnum.Explosion3);
await this.tileset.loadAll(); await this.tileset.loadAll();
} }
async prepareGame(level) {
async gameLoop() {
if (!this.tileset) { if (!this.tileset) {
await this.setupTileset(); await this.setupTileset();
} }
if (!this.map) { this.map = await new MapParser().getMap(level, this.tileset);
this.map = await new MapParser().getMap(1, this.tileset);
this.canvas.height = this.height; this.canvas.height = this.height;
this.canvas.width = this.width; this.canvas.width = this.width;
this.collisionManager.attachMap(this.map); this.collisionManager.attachMap(this.map);
this.aiTanks.setCount(this.map._enemiesCount); this.aiTanks.setCount(this.map._enemiesCount);
} }
if (this.map.getEagle().isDestroyed()) return; async play(level) {
if (this.aiTanks.isAllTanksDied()) return; if (!this.tileset || !this.map) {
if (this.tank.canBeErased()) return; await this.prepareGame(level);
$(".text-loading").hide();
$(".start-level-button").show();
$(".start-level-button").on("click", () => {
$(".loading").hide();
this.soundManager.playSound(SoundEnum.GameStart);
requestAnimationFrame(() => this.gameLoop());
})
}
}
lose() {
this.soundManager.playSound(SoundEnum.GameOver);
requestAnimationFrame(() => this.drawFieldRecursive())
}
win() {
requestAnimationFrame(() => this.drawFieldRecursive())
}
async gameLoop() {
if (this.map.getEagle().isDestroyed()) {
this.lose();
return;
}
if (this.aiTanks.isAllTanksDied()) {
this.win();
return;
}
if (this.tank.canBeErased()) {
this.lose();
return;
}
this.drawField(); this.drawField();
requestAnimationFrame(() => this.gameLoop()); requestAnimationFrame(() => this.gameLoop());
} }
drawFieldRecursive() {
this.drawField();
requestAnimationFrame(() => this.drawFieldRecursive());
}
drawField() { drawField() {
let lateDraw = []; let lateDraw = [];
@ -96,10 +142,9 @@ export class GameField {
Constants.TILE_SIZE.getValue(), Constants.TILE_SIZE.getValue()); Constants.TILE_SIZE.getValue(), Constants.TILE_SIZE.getValue());
let element = this.map.getElement(x, y); let element = this.map.getElement(x, y);
if (element.getType() == BlockTypeEnum.Forest){ if (element.getType() == BlockTypeEnum.Forest) {
lateDraw.push({element, x, y}); lateDraw.push({ element, x, y });
} else } else {
{
element.draw(this.ctx, x * Constants.TILE_SIZE.getValue(), y * Constants.TILE_SIZE.getValue()); element.draw(this.ctx, x * Constants.TILE_SIZE.getValue(), y * Constants.TILE_SIZE.getValue());
} }
} }

View File

@ -0,0 +1,24 @@
import { Constants } from "../constants";
export class SoundManager {
playSound(soundEnum) {
if (soundEnum)
soundEnum.playSound();
}
}
export class SoundEnum {
static BrickCollision = new SoundEnum(Constants.BRICK_SOUND.getValue());
static SteelCollision = new SoundEnum(Constants.STEEL_SOUND.getValue());
static Explosion = new SoundEnum(Constants.EXPLOSION_SOUND.getValue());
static GameOver = new SoundEnum(Constants.GAME_OVER_SOUND.getValue());
static GameStart = new SoundEnum(Constants.GAME_START_SOUND.getValue());
playSound() {
this._audio.play();
}
constructor(URL) {
this._audio = new Audio(URL);
}
}

View File

@ -7,6 +7,12 @@ export class Constants {
static PLAYER_TANK_ID_IMAGE = new Constants(100); static PLAYER_TANK_ID_IMAGE = new Constants(100);
static BRICK_SOUND = new Constants("sound/brick.ogg");
static STEEL_SOUND = new Constants("sound/steel.ogg");
static EXPLOSION_SOUND = new Constants("sound/explosion.ogg");
static GAME_START_SOUND = new Constants("sound/gamestart.ogg");
static GAME_OVER_SOUND = new Constants("sound/gameover.ogg");
constructor(value){ constructor(value){
this._value = value; this._value = value;
} }

View File

@ -2,12 +2,19 @@ import { GameField } from "./GameLogic/gameField";
import { TankKeyAdapter } from "./KeyAdapters/tankKeyAdapter"; import { TankKeyAdapter } from "./KeyAdapters/tankKeyAdapter";
export function init() { export function init() {
if('serviceWorker' in navigator) {
navigator.serviceWorker
.register('./sw.js')
.then(function() { console.log("Service Worker Registered"); });
}
const canvas = document.getElementById("canvas"); const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
if (ctx) { if (ctx) {
const game = new Game(416, 416, ctx, canvas); const game = new Game(416, 416, ctx, canvas);
game.start(); game.start();
} }
} }
export class Game { export class Game {
@ -43,6 +50,6 @@ export class Game {
addEventListener("keyup", (ev) => { addEventListener("keyup", (ev) => {
if (prevEv && prevEv.key == ev.key) prevEv = undefined; if (prevEv && prevEv.key == ev.key) prevEv = undefined;
}); });
requestAnimationFrame(() => this.gameField.gameLoop()); requestAnimationFrame(() => this.gameField.play(1));
} }
} }