Move the game to subfolder

This commit is contained in:
KGrzeg 2021-10-17 18:22:57 +02:00
parent a724d80bb7
commit a86c5cdda0
39 changed files with 0 additions and 0 deletions

4
game/.eslintignore Normal file
View file

@ -0,0 +1,4 @@
# don't ever lint node_modules
node_modules
# don't lint build output (make sure it's set to your correct build folder name)
dist

19
game/.eslintrc.js Normal file
View file

@ -0,0 +1,19 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint'
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended'
],
rules: {
'@typescript-eslint/explicit-function-return-type': 0,
'@typescript-eslint/ban-ts-ignore': 0,
'@typescript-eslint/no-namespace': { 'allowDeclarations': true },
'@typescript-eslint/member-delimiter-style': 0,
'@typescript-eslint/no-explicit-any': 0
}
}

4
game/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/.cache
/dist
/node_modules
/.DS_Store

21
game/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 ourcade
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

18062
game/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

38
game/package.json Normal file
View file

@ -0,0 +1,38 @@
{
"name": "hs3jam-miner",
"version": "1.0.0",
"description": "Simple Phaser 3 game for the Hackerspace Trójmiasto's little game jam",
"scripts": {
"start": "parcel src/index.html -p 8000",
"build": "parcel build src/index.html --out-dir dist --no-source-maps",
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint ./src --ext .js,.jsx,.ts,.tsx"
},
"author": "Grzegorz Kupczyk",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/ourcade/phaser3-parcel-template.git"
},
"homepage": "https://github.com/ourcade/phaser3-parcel-template",
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^2.29.0",
"@typescript-eslint/parser": "^2.29.0",
"eslint": "^6.8.0",
"minimist": ">=1.2.2",
"parcel-bundler": "^1.12.5",
"parcel-plugin-clean-easy": "^1.0.2",
"parcel-plugin-static-files-copy": "^2.4.3",
"typescript": "^3.8.3"
},
"dependencies": {
"phaser": "^3.55.2"
},
"parcelCleanPaths": [
"dist"
],
"staticFiles": {
"staticPath": "public",
"watcherGlob": "**"
}
}

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

138
game/readme.md Normal file
View file

@ -0,0 +1,138 @@
This is a fork of [phaser3-typescript-parcel-template](https://github.com/ourcade/phaser3-typescript-parcel-template)
![phaser3-parceljs-template](https://user-images.githubusercontent.com/2236153/71606463-37a0da80-2b2e-11ea-9b5f-5d26ccc84f91.png)
# Phaser 3 + TypeScript + Parcel Template
> For people who want to spend time making Phaser 3 games in TypeScript instead of configuring build tools.
![License](https://img.shields.io/badge/license-MIT-green)
This is a TypeScript specific fork of [phaser3-parcel-template](https://github.com/ourcade/phaser3-parcel-template).
## Prerequisites
You'll need [Node.js](https://nodejs.org/en/), [npm](https://www.npmjs.com/), and [Parcel](https://parceljs.org/) installed.
It is highly recommended to use [Node Version Manager](https://github.com/nvm-sh/nvm) (nvm) to install Node.js and npm.
For Windows users there is [Node Version Manager for Windows](https://github.com/coreybutler/nvm-windows).
Install Node.js and `npm` with `nvm`:
```bash
nvm install node
nvm use node
```
Replace 'node' with 'latest' for `nvm-windows`.
Then install Parcel:
```bash
npm install -g parcel-bundler
```
## Getting Started
Clone this repository to your local machine:
```bash
git clone https://github.com/ourcade/phaser3-typescript-parcel-template.git
```
This will create a folder named `phaser3-typescript-parcel-template`. You can specify a different folder name like this:
```bash
git clone https://github.com/ourcade/phaser3-typescript-parcel-template.git my-folder-name
```
Go into your new project folder and install dependencies:
```bash
cd phaser3-typescript-parcel-template # or 'my-folder-name'
npm install
```
Start development server:
```
npm run start
```
To create a production build:
```
npm run build
```
Production files will be placed in the `dist` folder. Then upload those files to a web server. 🎉
## Project Structure
```
.
├── dist
├── node_modules
├── public
├── src
│ ├── scenes
│ │ ├── HelloWorldScene.ts
│ ├── index.html
│ ├── main.ts
├── package.json
```
The contents of this template is the basic [Phaser 3 getting started example](http://phaser.io/tutorials/getting-started-phaser3/part5).
This template assumes you will want to organize your code into multiple files and use TypeScript.
TypeScript files are intended for the `src` folder. `main.ts` is the entry point referenced by `index.html`.
Other than that there is no opinion on how you should structure your project. There is a `scenes` folder in `src` where the `HelloWorldScene.ts` lives but you can do whatever you want.
## Static Assets
Any static assets like images or audio files should be placed in the `public` folder. It'll then be served at http://localhost:8000/images/my-image.png
Example `public` structure:
```
public
├── images
│ ├── my-image.png
├── music
│ ├── ...
├── sfx
│ ├── ...
```
They can then be loaded by Phaser with `this.image.load('my-image', 'images/my-image.png')`.
## TypeScript ESLint
This template uses a basic `typescript-eslint` set up for code linting.
It does not aim to be opinionated.
## Dev Server Port
You can change the dev server's port number by modifying the `start` script in `package.json`. We use Parcel's `-p` option to specify the port number.
The script looks like this:
```
parcel src/index.html -p 8000
```
Change 8000 to whatever you want.
## Other Notes
[parcel-plugin-clean-easy](https://github.com/lifuzhao100/parcel-plugin-clean-easy) is used to ensure only the latest files are in the `dist` folder. You can modify this behavior by changing `parcelCleanPaths` in `package.json`.
[parcel-plugin-static-files](https://github.com/elwin013/parcel-plugin-static-files-copy#readme) is used to copy static files from `public` into the output directory and serve it. You can add additional paths by modifying `staticFiles` in `package.json`.
## License
[MIT License](https://github.com/ourcade/phaser3-typescript-parcel-template/blob/master/LICENSE)

View file

@ -0,0 +1,97 @@
import Phaser from 'phaser'
import Bullet from '../classes/Bullet'
export default class Asteroid extends Phaser.Physics.Arcade.Sprite {
// asteroid can't kill player
// if younger than unbornAge in ms
static unbornAge = 1000
readonly wrapMargin = 30
readonly scaleMin = 0.7
readonly scaleMax = 1.3
readonly scaleRotationFactor = 0.4
age = 0
constructor(scene: Phaser.Scene) {
super(
scene,
Phaser.Math.RND.integerInRange(0, scene.physics.world.bounds.width),
Phaser.Math.RND.integerInRange(0, scene.physics.world.bounds.height),
'asteroids'
)
scene.add.existing(this)
scene.physics.add.existing(this)
this.setCircle(30, 30, 30)
this.setScale(Phaser.Math.RND.realInRange(this.scaleMin, this.scaleMax))
this.setVelocity(
Phaser.Math.RND.realInRange(-100, 100),
Phaser.Math.RND.realInRange(-100, 100)
)
this.setRandomShade()
this.anims.play(Asteroid.getRandomAnimationName())
this.anims.timeScale = 1 + (this.scaleMax - this.scale) * this.scaleRotationFactor
if (Phaser.Math.RND.integer() % 2 == 0)
this.anims.reverse()
this.setAlpha(0)
scene.tweens.addCounter({
from: 0,
to: 1,
duration: Asteroid.unbornAge,
onUpdate: (tween) => {
const value = tween.getValue()
this.setAlpha(value)
}
});
}
preUpdate(time: number, delta: number) {
super.preUpdate(time, delta)
this.scene.physics.world.wrap(this, this.scale * this.wrapMargin)
this.age += delta
}
gotHit(me, bullet) {
if (!(bullet instanceof Bullet)) return
if (bullet.active == false) return
me.scene.events.emit("asteroid:destroy")
me.destroy() // TODO: use objects pool
bullet.setActive(false)
bullet.setVisible(false)
}
private setRandomShade() {
const color = new Phaser.Display.Color()
color.randomGray(0xa0) //the darkest possible dye is 0xa0a0a0
this.setTint(color.color)
}
static createAnimations(scene: Phaser.Scene) {
["a", "b", "c", "d"].forEach((animationName, i) => {
const frames = scene.anims.generateFrameNames('asteroids', {
start: 0, end: 15,
zeroPad: 4,
prefix: `${animationName}4`
});
scene.anims.create({
key: `asteroid${i}`,
frames: frames,
frameRate: 16,
repeat: -1
})
})
}
static getRandomAnimationName() {
const animationsAmount = 3
const id = Phaser.Math.RND.integerInRange(0, animationsAmount)
return `asteroid${id}`
}
}

View file

@ -0,0 +1,36 @@
import Phaser from 'phaser'
export default class Bullet extends Phaser.Physics.Arcade.Sprite {
readonly lifetime = 4000 //ms
readonly speed = 600
age = 0
constructor(scene, x, y) {
super(scene, x, y, 'bullet')
}
fire(x: number, y: number, rotation: number) {
this.body.reset(x, y)
this.rotation = rotation
this.setActive(true)
this.setVisible(true)
const v = new Phaser.Math.Vector2(0, -this.speed)
v.rotate(rotation)
this.setVelocity(v.x, v.y)
this.age = 0
}
preUpdate(time: number, delta: number) {
super.preUpdate(time, delta)
this.age += delta
if (this.age >= this.lifetime) {
this.setActive(false)
this.setVisible(false)
}
}
}

View file

@ -0,0 +1,32 @@
import Phaser from 'phaser'
import Bullet from './Bullet'
import Ship from './Ship'
export default class Bullets extends Phaser.Physics.Arcade.Group {
readonly cooldown: number //ms
lastShoot = 0
constructor(scene: Phaser.Scene, fireRate: number) {
super(scene.physics.world, scene)
this.createMultiple({
frameQuantity: 30,
key: 'bullet',
active: false,
visible: false,
classType: Bullet
});
this.cooldown = 1000 / fireRate
}
fireBullet(time: number, shooter: Ship) {
let bullet = this.getFirstDead(false)
if (bullet && this.lastShoot + this.cooldown <= time) {
bullet.fire(shooter.x, shooter.y, shooter.rotation)
this.lastShoot = time
}
}
}

View file

@ -0,0 +1,59 @@
import PlayScene from '../scenes/PlayScene'
export default class DifficultyManager {
readonly spawnInterval = 1500 //ms
readonly maxAsteroids = 300
readonly level = 30
readonly nextLevelRequirementIncrease = 10
private scene: PlayScene
private difficultyLevel = 1
private points = 0
private spawnAtOnce = 1
private nextLevelRequirement = 10
constructor(scene: PlayScene) {
this.scene = scene
scene.events.on('asteroid:destroy', () => {
this.points += 1
this.nextLevelRequirement -= 1
this.scene.events.emit("getpoint", this.difficultyLevel)
if (this.nextLevelRequirement <= 0)
this.levelUp()
})
scene.time.addEvent({
delay: this.spawnInterval,
callback: this.spawnAsteroids,
callbackScope: this,
repeat: -1
});
}
levelUp() {
this.difficultyLevel += 1
this.spawnAtOnce += 1
this.nextLevelRequirement = this.difficultyLevel * this.nextLevelRequirementIncrease
this.scene.events.emit("lvlup", this.difficultyLevel)
}
spawnAsteroids() {
for (let i = 0; i < this.spawnAtOnce; ++i)
this.scene.spawnAsteroid()
}
getMaxAsteroids() {
return Math.min(this.difficultyLevel * 5, this.maxAsteroids)
}
getLevel() {
return this.difficultyLevel
}
getPoints() {
return this.points
}
}

86
game/src/classes/Ship.ts Normal file
View file

@ -0,0 +1,86 @@
import Phaser from 'phaser'
import Bullets from './Bullets'
import Thruster from './Thruster'
import Asteroid from './Asteroid'
export default class Ship extends Phaser.Physics.Arcade.Sprite {
readonly acceleration = 5
readonly dragForce = 0.6
readonly maxSpeed = 300
readonly colliderRadiusRatio = 0.43
readonly wrapMargin = 10
readonly fireRate = 5 //shoots/s
bullets: Bullets
thruster: Thruster
life = 3
constructor(scene: Phaser.Scene) {
super(
scene,
scene.physics.world.bounds.centerX,
scene.physics.world.bounds.centerY,
'ship'
)
scene.add.existing(this)
scene.physics.add.existing(this)
scene.input.on('pointermove', (pointer) => {
this.rotation = Phaser.Math.Angle.BetweenPoints(this, pointer) + Math.PI / 2
})
this.setDamping(true)
this.setDrag(this.dragForce)
this.body.setCircle(this.width * this.colliderRadiusRatio)
this.bullets = new Bullets(scene, this.fireRate)
this.thruster = new Thruster(scene, this)
this.setMaxVelocity(this.maxSpeed)
}
preUpdate(time: number, delta: number) {
super.preUpdate(time, delta)
const keyUp = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W)
const keyDown = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S)
const keyLeft = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A)
const keyRight = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D)
const vec = new Phaser.Math.Vector2(0, 0)
if (keyUp.isDown) vec.y = -this.acceleration
if (keyDown.isDown) vec.y = this.acceleration
if (keyRight.isDown) vec.x = this.acceleration
if (keyLeft.isDown) vec.x = -this.acceleration
if (this.scene.input.activePointer.leftButtonDown())
this.bullets!.fireBullet(time, this);
this.rotation = Phaser.Math.Angle.BetweenPoints(
this,
this.scene.input.activePointer
) + Math.PI / 2
this.body.velocity.add(vec)
this.scene.physics.world.wrap(this, this.wrapMargin)
this.thruster.update(time, delta)
}
gotHit(_, asteroid) {
if (!(asteroid instanceof Asteroid)) return
if (asteroid.age > Asteroid.unbornAge) {
this.life -= 1
this.scene.cameras.main.shake(100, 0.02)
asteroid.destroy() //TODO: use objects pool
this.scene.events.emit("ship:gothit")
}
if (!this.life){
this.scene.events.emit("ship:destroyed")
}
}
}

View file

@ -0,0 +1,59 @@
import Phaser from 'phaser'
import Utils from './Utils'
export default class Thruster {
readonly angleSpan = 40
readonly frequency = 100
readonly speed = 100
readonly framesIndex = 30
emitter: Phaser.GameObjects.Particles.ParticleEmitter
parent: Phaser.Physics.Arcade.Sprite
constructor(scene: Phaser.Scene, parent: Phaser.Physics.Arcade.Sprite) {
this.emitter = scene.add.particles('particles')
.createEmitter({
frame: [25, 26, 27, 28, 29, 30, 31, 32, 34, 35],
speed: this.speed,
frequency: this.frequency,
scale: { start: 0.2, end: 0 },
blendMode: 'ADD',
follow: parent
})
this.parent = parent
}
update(time: number, delta: number) {
const thrustDirection = Phaser.Math.RadToDeg(
this.parent.body.velocity.clone().negate().angle()
)
this.emitter.angle.start = thrustDirection - this.angleSpan / 2
this.emitter.angle.end = thrustDirection + this.angleSpan / 2
const thrustPower = this.getThrustPower()
this.emitter.setFrequency(thrustPower.frequency)
this.emitter.setAlpha(thrustPower.opacity)
}
getThrustPower() {
const velocityRange = [0, 320] as const
const frequencyRange = [300, 0] as const
const opacityRange = [0, 1] as const
return {
frequency: Utils.clampMap(
this.parent.body.velocity.length(),
...velocityRange,
...frequencyRange
),
opacity: Utils.clampMap(
this.parent.body.velocity.length(),
...velocityRange,
...opacityRange
)
}
}
}

49
game/src/classes/Utils.ts Normal file
View file

@ -0,0 +1,49 @@
export default {
map(value: number,
sourceMin: number,
sourceMax: number,
targetMin: number,
targetMax: number) {
const sourceRange = sourceMax - sourceMin
const targetRange = targetMax - targetMin
return (value - sourceMin) / sourceRange * targetRange + targetMin
},
clamp(value: number,
min: number,
max: number) {
let leftLimit = Math.min
let rightLimit = Math.max
if (max < min) {
leftLimit = Math.max
rightLimit = Math.max
}
return leftLimit(
rightLimit(min, value),
max)
},
clampMap(value: number,
sourceMin: number,
sourceMax: number,
targetMin: number,
targetMax: number) {
const v = this.map(value,
sourceMin,
sourceMax,
targetMin,
targetMax
)
const min = Math.min(targetMin, targetMax)
const max = Math.max(targetMin, targetMax)
return this.clamp(
v, min, max
)
}
}

124
game/src/gui.ts Normal file
View file

@ -0,0 +1,124 @@
declare interface Window { myStuff: any }
(() => {
const elements = {
bar: {
logged: document.getElementById("loggedbar"),
loggedout: document.getElementById("loggedoutbar"),
},
buttons: {
login: document.getElementById("login"),
logout: document.getElementById("logout"),
signup: document.getElementById("signup"),
},
name: document.getElementById("name"),
key: document.getElementById("key"),
}
function checkIfLogged() {
const token = localStorage.getItem("token")
const name = localStorage.getItem("name")
if (token !== null) {
window.myStuff.token = token
window.myStuff.name = name
console.log("my stuff", window.myStuff)
elements.bar.logged!.style.display = ""
elements.bar.loggedout!.style.display = "none"
elements.name!.innerText = name!;
elements.key!.innerText = token!;
} else {
console.log("not logged in")
elements.bar.logged!.style.display = "none"
elements.bar.loggedout!.style.display = ""
}
}
function loginByKey() {
const key = prompt("Type in the #key")
if (!key) {
alert("Login cancelled")
return
}
//TODO: fetch data from server
const response = {
token: Math.random().toString() + '.abc',
name: 'Hagis'
}
localStorage.setItem('token', response.token)
localStorage.setItem('name', response.name)
window.myStuff.token = response.token
window.myStuff.name = response.name
elements.bar.logged!.style.display = ''
elements.bar.loggedout!.style.display = 'none'
elements.name!.innerText = response.name
elements.key!.innerText = response.token
}
function signup() {
const nickname = prompt("Your nickname:")
if (!nickname) {
alert("Signup cancelled")
return
}
// TODO: Fetch data from server
//mock bad request
if (nickname == 'Hgs') {
alert("The name is occupied by someone else, try again with another nickname")
return
}
const response = {
token: Math.random().toString() + '.abc',
name: nickname!
}
localStorage.setItem('token', response.token)
localStorage.setItem('name', response.name)
window.myStuff.token = response.token
window.myStuff.name = response.name
elements.bar.logged!.style.display = ''
elements.bar.loggedout!.style.display = 'none'
elements.name!.innerText = response.name
elements.key!.innerText = response.token
}
function logout() {
const sure = confirm("Are you sure you want to logout? " +
"You won't be able to login again without #key. " +
"Make sure you copied key before log out!")
if (!sure)
return
localStorage.clear()
window.myStuff = {}
elements.bar.logged!.style.display = 'none'
elements.bar.loggedout!.style.display = ''
elements.name!.innerText = ''
elements.key!.innerText = ''
}
function setup() {
window.myStuff = {}
elements.buttons.login!.addEventListener("click", loginByKey)
elements.buttons.logout!.addEventListener("click", logout)
elements.buttons.signup!.addEventListener("click", signup)
checkIfLogged()
}
setup()
})()

25
game/src/index.html Normal file
View file

@ -0,0 +1,25 @@
<html>
<head>
<title>SPACE SMASHER 9001!</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="bar">
<div id="loggedbar" class="logged-in" style="display: none">
Logged in as <span id="name">username</span>
<span id="key" class="key">#asdasdasdasd</span>
<button id="logout">Log out</button>
</div>
<div id="loggedoutbar" class="logged-out" style="display: none">
Not logged in
<button id="login">Log in with #key</button>
<button id="signup">Sign up</button>
</div>
</div>
<script src="main.ts"></script>
<script src="gui.ts"></script>
</body>
</html>

22
game/src/main.ts Normal file
View file

@ -0,0 +1,22 @@
import Phaser, { Game } from 'phaser'
import PlayScene from './scenes/PlayScene'
import GameOverScene from './scenes/GameOverScene'
import StartScene from './scenes/StartScene'
const config: Phaser.Types.Core.GameConfig = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: {
debug: false
}
},
scene: [StartScene, PlayScene, GameOverScene]
}
export default new Phaser.Game(config)

View file

@ -0,0 +1,43 @@
import Phaser from 'phaser'
export default class PlayScene extends Phaser.Scene {
constructor() {
super('game-over-scene')
}
preload() {
this.load.image('phaser-logo', 'assets/img/phaser3-logo.png')
this.load.image('hs3-logo', 'assets/img/hs3-logo.png')
}
create(data) {
console.log("Show end screen with data:", data)
this.add.image(250, 550, 'phaser-logo').setScale(0.5)
this.add.image(550, 550, 'hs3-logo').setScale(0.5)
this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY - 100,
"KONIEC GRY", {
font: '64px Verdana',
}).setOrigin(0.5, 0.5)
const rank = 0
const pts = data.points || 0
const lvl = data.level || 0
const time = Math.ceil(data.elapsedTime || 0)
this.add.text(this.cameras.main.centerX, this.cameras.main.centerY, [
"Miejsce w rankingu: #" + rank,
"Punkty: " + pts,
"Poziom: " + lvl,
"Czas Gry: " + time + 's',
], {
font: '32px Verdana',
align: 'center',
color: 'cyan'
}).setOrigin(0.5, 0.5)
}
}

View file

@ -0,0 +1,113 @@
import Phaser from 'phaser'
import Ship from '../classes/Ship'
import Asteroid from '../classes/Asteroid'
import DifficultyManager from '../classes/DifficultyManager'
export default class PlayScene extends Phaser.Scene {
readonly maxAsteroids = 70
player?: Ship
rotFrames?: Phaser.Types.Animations.AnimationFrame[]
asteroids?: Phaser.GameObjects.Group
difficulty?: DifficultyManager
points = 0
progressLabel?: Phaser.GameObjects.Text
background?: Phaser.GameObjects.Image
backgroundOrder: number[] = []
backgroundId = 0
startTimestamp: number = 0
auth: any
constructor() {
super('play-scene')
this.backgroundOrder = []
for (let i = 1; i < 10; ++i)
this.backgroundOrder.push(i)
Phaser.Utils.Array.Shuffle(this.backgroundOrder)
}
preload() {
for (let i = 1; i < 10; i++)
this.load.image(`sky${i}`, `assets/img/nebula0${i}.png`)
this.load.image('ship', 'assets/img/ship.png')
this.load.image('bullet', 'assets/img/bullet.png')
this.load.spritesheet('particles', 'assets/img/boom.png', { frameWidth: 192, frameHeight: 192 })
this.load.multiatlas('asteroids', 'assets/img/asteroids.json', 'assets/img');
}
create(data) {
console.log("Started game with", data)
this.auth = data
Asteroid.createAnimations(this)
this.difficulty = new DifficultyManager(this)
this.events.on('getpoint', this.updateLabel, this)
this.events.on('lvlup', this.updateLabel, this)
this.events.on('lvlup', this.changeBackground, this)
this.events.on('ship:gothit', this.updateLabel, this)
this.events.on('ship:destroyed', this.gameOver, this)
this.background = this.add.image(400, 300, 'sky1')
this.asteroids = this.add.group()
this.player = new Ship(this)
this.progressLabel = this.add.text(5, 5, "", {
font: '32px Verdana',
color: 'cyan'
})
this.progressLabel.setDepth(1)
this.startTimestamp = this.time.now
this.changeBackground()
this.updateLabel()
this.hookupCollisions()
}
gameOver() {
console.log("%cU ded", "color:red")
console.log("points: ", this.difficulty!.getPoints())
this.scene.start('game-over-scene', {
points: this.difficulty!.getPoints(),
level: this.difficulty!.getLevel(),
elapsedTime: (this.time.now - this.startTimestamp) / 1000
})
}
changeBackground() {
const currentId = this.backgroundOrder[this.backgroundId]
const key = 'sky' + currentId.toString()
this.background!.setTexture(key)
this.backgroundId = (++this.backgroundId) % this.backgroundOrder.length
}
spawnAsteroid() {
if (this.asteroids!.getLength() < this.difficulty!.getMaxAsteroids())
this.asteroids!.add(new Asteroid(this))
}
updateLabel() {
const lvl = this.difficulty!.getLevel()
const pts = this.difficulty!.getPoints()
const lives = this.player!.life
const str = `Level: ${lvl}\tPoints: ${pts}\tHP: ${lives}`
this.progressLabel!.text = str
}
hookupCollisions() {
// player - asteroids
this.physics.add.overlap(
this.player!, this.asteroids!, // colliders
this.player!.gotHit, // callback
undefined, // callback filter
this.player // 'this' for callback
)
// bullets - asteroids
this.physics.add.overlap(
this.asteroids!, this.player!.bullets,
Asteroid.prototype.gotHit
)
}
}

View file

@ -0,0 +1,40 @@
import Phaser from 'phaser'
export default class StartScene extends Phaser.Scene {
constructor() {
super('start-scene')
}
preload() {
this.load.image('phaser-logo', 'assets/img/phaser3-logo.png')
this.load.image('hs3-logo', 'assets/img/hs3-logo.png')
}
create() {
this.add.image(250, 550, 'phaser-logo').setScale(0.5)
this.add.image(550, 550, 'hs3-logo').setScale(0.5)
this.add.text(
this.cameras.main.centerX,
this.cameras.main.centerY - 100,
"SPACE SMASHER 9001!", {
font: '64px Verdana',
}).setOrigin(0.5, 0.5)
this.add.text(this.cameras.main.centerX, this.cameras.main.centerY, [
"Naciśnij przycisk aby zacząć",
"",
"Zaloguj się przed rozpoczęciem gry, aby zachować rekord",
"albo graj jako gość (bez rankingu)"
], {
font: '21px Verdana',
align: 'center',
color: 'cyan'
}).setOrigin(0.5, 0.5)
this.input.on('pointerup', () => {
this.scene.start('play-scene', window.myStuff);
});
}
}

31
game/src/style.css Normal file
View file

@ -0,0 +1,31 @@
html,body{
padding: 0;
margin: 0;
position: relative;
background-color: black;
font-family: "Verdana", "Geneva", "Tahoma", "sans-serif";
}
body {
background-image: url('../public/assets/img/stars.png');
}
.bar {
width: 100%;
background-color: aqua;
width: 800px;
margin:auto;
margin-bottom: 10px;
text-align: center;
}
.key {
font-family: 'Courier New', Courier, monospace;
}
canvas {
display: block;
width: 800px;
margin: auto;
box-shadow: 0 0 35px rgba(0,255,255,0.3);
}

31
game/tsconfig.json Normal file
View file

@ -0,0 +1,31 @@
{
"compilerOptions": {
"target": "es2016",
"module": "es6",
"strict": true,
"noImplicitAny": false,
"noEmit": true,
"allowJs": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": "./src",
"paths": {
"~/*": ["./*"]
},
"typeRoots": [
"node_modules/@types",
"node_module/phaser/types"
],
"types": [
"phaser"
]
},
"include": [
"src/**/*"
]
}