forked from esamarathon/esa-layouts
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* First draft of turtle * It listens for twitch now * Move some text around * MVP * Dorito failed * change label * Add bsg sprites * cleanup
- Loading branch information
Showing
32 changed files
with
950 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
export default class Animator { | ||
private timer = 0; | ||
private index = 0; | ||
|
||
constructor( | ||
private readonly playSpeed: number, | ||
private readonly showTime: number, | ||
private readonly images: HTMLImageElement[], | ||
) { | ||
// | ||
} | ||
|
||
// A method used to update the animation over time. | ||
update() { | ||
this.timer += this.playSpeed; | ||
if (this.timer >= this.showTime) { | ||
this.timer = 0; | ||
this.index = (this.index + 1) % this.images.length; | ||
} | ||
} | ||
|
||
// A method that returns the current image of the animation. | ||
getImage() { | ||
return this.images[this.index]; | ||
} | ||
|
||
// A method that reset the animation to the first image. | ||
reset() { | ||
this.index = 0; | ||
} | ||
|
||
// A method that can be used to create an instance of an animator | ||
// by specifying images' locations instead of instances of HTMLImageElements. | ||
static create(playSpeed: number, showTime: number, imageSelectors: string[]) { | ||
const images: HTMLImageElement[] = []; | ||
|
||
imageSelectors.forEach((selector) => { | ||
images.push(document.querySelector(selector) as HTMLImageElement); | ||
}); | ||
|
||
return new Animator(playSpeed, showTime, images); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
export default class Background { | ||
private current: number; | ||
|
||
constructor( | ||
private readonly max: number, | ||
private readonly w: number, | ||
private readonly h: number, | ||
) { | ||
this.current = max; | ||
} | ||
|
||
draw(ctx: CanvasRenderingContext2D) { | ||
// Draw background color. | ||
// ctx.beginPath(); | ||
// ctx.fillStyle = 'rgb(0, 0, 0)'; | ||
// ctx.rect(0, 0, this.w, this.h); | ||
// ctx.fill(); | ||
// ctx.closePath(); | ||
|
||
// Set inverse colors for other objects. | ||
ctx.fillStyle = 'rgb(255, 255, 255)'; | ||
ctx.strokeStyle = 'rgb(255, 255, 255)'; | ||
} | ||
|
||
update(): void { | ||
// Does not need to do anything anymore | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import Point2D from '@esa-layouts/countdown/game/Point2D'; | ||
import { IObstacle } from '@esa-layouts/countdown/game/types/options'; | ||
|
||
export default class Collider { | ||
constructor( | ||
public readonly position: Point2D, | ||
public readonly w: number, | ||
public readonly h: number, | ||
) { | ||
// | ||
} | ||
|
||
// A method that can be used to check if the | ||
// collider overlaps with another collider. | ||
overlaps(other: Collider | IObstacle) { | ||
return this.position.x < other.position.x + other.w | ||
&& this.position.x + this.w > other.position.x | ||
&& this.position.y < other.position.y + other.h | ||
&& this.position.y + this.h > other.position.y; | ||
} | ||
|
||
// A method that returns true if the collider | ||
// overlaps with one in the list of colliders. | ||
overlapsWithOthers(others: Collider[] | IObstacle[]): boolean { | ||
for (const other of others) { | ||
if (this.overlaps(other)) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
import { DifficultyOptions, PlayerOptions, SpawnerOptions } from '@esa-layouts/countdown/game/types/options'; | ||
import RunnerPlayer from '@esa-layouts/countdown/game/RunnerPlayer'; | ||
import Background from '@esa-layouts/countdown/game/Background'; | ||
import Spawner from '@esa-layouts/countdown/game/Spawner'; | ||
import InputHandler from '@esa-layouts/countdown/game/InputHandler'; | ||
|
||
export default class EndlessRunnerGame { | ||
private readonly ctx: CanvasRenderingContext2D; | ||
private readonly groundY: number; | ||
|
||
private speed = 0; | ||
public score = 0; | ||
public highScore = parseInt(localStorage.getItem('highScore') || '0', 10); | ||
private player: RunnerPlayer | null = null; | ||
private spawner: Spawner | null = null; | ||
private gameOver = false; | ||
|
||
private readonly inputHandler = new InputHandler(); | ||
private background: Background = new Background(0, 0, 0); | ||
|
||
constructor( | ||
private readonly canvas: HTMLCanvasElement, | ||
private readonly frameRate: number, | ||
groundOffset: number, | ||
private readonly playerOptions: PlayerOptions, | ||
private readonly spawnerOptions: SpawnerOptions, | ||
private readonly difficulty: DifficultyOptions, | ||
) { | ||
this.groundY = this.canvas.height - groundOffset; | ||
|
||
const ctx = canvas.getContext('2d'); | ||
|
||
if (ctx === null) { | ||
throw new Error('Could not get 2d context from canvas'); | ||
} | ||
|
||
this.ctx = ctx; | ||
|
||
this.initialize(); | ||
} | ||
|
||
initialize(): void { | ||
this.background = new Background(0, this.canvas.width, this.canvas.height); | ||
this.player = RunnerPlayer.create(this.playerOptions, this.groundY); | ||
this.spawner = Spawner.create(this.spawnerOptions, this.canvas.width, this.groundY); | ||
this.speed = 0; | ||
this.score = 0; | ||
this.gameOver = false; | ||
} | ||
|
||
start(): void { | ||
setInterval(() => { | ||
this.update(); | ||
}, this.frameRate); | ||
} | ||
|
||
handleInput(): void { | ||
if (this.inputHandler.isJump) { | ||
// If the game is ended, | ||
// restart the game. | ||
if (this.gameOver) { | ||
// this.initialize(); | ||
// Do nothing. | ||
} else { | ||
// otherwise, execute the | ||
// player's jump behaviour. | ||
this.player?.jump(); | ||
} | ||
} | ||
} | ||
|
||
update(): void { | ||
this.handleInput(); | ||
|
||
if (this.player === null || this.spawner === null) { | ||
return; | ||
} | ||
|
||
// Clear the canvas. | ||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); | ||
|
||
// Draw game objects | ||
this.background.draw(this.ctx); | ||
this.drawGround(); | ||
// this.drawScore(); | ||
this.player?.draw(this.ctx, this.gameOver); | ||
this.spawner?.draw(this.ctx); | ||
|
||
if (this.gameOver) { | ||
// Draw game over elements. | ||
this.drawGameOver(); | ||
this.updateHighScore(this.score); | ||
|
||
// otherwise, execute game behaviour. | ||
} else { | ||
this.increaseDifficulty(); | ||
|
||
// Execute update. | ||
this.background.update(); | ||
this.player.update(); | ||
this.spawner.update(); | ||
|
||
// Check for collisions. | ||
this.gameOver = this.player.overlapsWithOthers(this.spawner.activeObstacles); | ||
|
||
if (this.gameOver) { | ||
setTimeout(() => { | ||
this.initialize(); | ||
}, 5 * 1000); | ||
} | ||
|
||
// Increase score. | ||
// eslint-disable-next-line no-plusplus | ||
this.score++; | ||
} | ||
|
||
// if (this.highScore > 0) { | ||
// this.drawHighScore(); | ||
// } | ||
} | ||
|
||
increaseDifficulty() { | ||
if (this.player === null || this.spawner === null) { | ||
return; | ||
} | ||
|
||
if (this.speed < this.difficulty.maxIncreasement) { | ||
this.speed += this.difficulty.speedIncreasement; | ||
this.player.movement.jumpPower += this.difficulty.speedIncreasement; | ||
this.player.movement.gravity += this.difficulty.speedIncreasement; | ||
this.spawner.speed += this.difficulty.speedIncreasement; | ||
} | ||
} | ||
|
||
drawGameOver(): void { | ||
this.ctx.font = '40px sans-serif'; | ||
this.ctx.beginPath(); | ||
this.ctx.fillText( | ||
'Type "jump" to jump', | ||
(this.canvas.width / 2) - 260, | ||
(this.canvas.height / 2) - 50, | ||
); | ||
this.ctx.closePath(); | ||
|
||
this.ctx.beginPath(); | ||
this.ctx.fillText('GAME OVER', (this.canvas.width / 2) - 190, this.canvas.height / 2); | ||
this.ctx.closePath(); | ||
|
||
this.ctx.font = '30px sans-serif'; | ||
this.ctx.beginPath(); | ||
this.ctx.fillText( | ||
'Game restarts in 5 seconds', | ||
(this.canvas.width / 2) - 290, | ||
(this.canvas.height / 2) + 50, | ||
); | ||
this.ctx.closePath(); | ||
} | ||
|
||
drawScore(): void { | ||
this.ctx.beginPath(); | ||
this.ctx.fillText(`score: ${this.score}`, 10, this.highScore > 0 ? 40 : 20); | ||
this.ctx.closePath(); | ||
} | ||
|
||
drawHighScore(): void { | ||
this.ctx.beginPath(); | ||
this.ctx.fillText(`HI score: ${this.highScore}`, 10, 20); | ||
this.ctx.closePath(); | ||
} | ||
|
||
updateHighScore(newHigh: number): void { | ||
this.highScore = Math.max(this.highScore, newHigh); | ||
// localStorage.setItem('highScore', this.highScore.toString()); | ||
} | ||
|
||
drawGround(): void { | ||
this.ctx.beginPath(); | ||
this.ctx.rect(0, this.groundY, this.canvas.width, 3); | ||
this.ctx.fill(); | ||
this.ctx.closePath(); | ||
} | ||
} |
Oops, something went wrong.