-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from devshareacademy/simon
contains the prototype code for the game simon
- Loading branch information
Showing
17 changed files
with
1,530 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/dist | ||
/node_modules | ||
yarn-error.log | ||
.DS_Store |
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,14 @@ | ||
{ | ||
"eslint.format.enable": true, | ||
"eslint.options": { | ||
"overrideConfigFile": "config/.eslintrc" | ||
}, | ||
"editor.defaultFormatter": "dbaeumer.vscode-eslint", | ||
"editor.codeActionsOnSave": { | ||
"source.fixAll.eslint": "explicit" | ||
}, | ||
"cSpell.words": [ | ||
"devshareacademy", | ||
"spritesheet" | ||
] | ||
} |
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,49 @@ | ||
# Phaser 3 TypeScript - Simon | ||
|
||
A basic Phaser 3 TypeScript prototype of the game Simon. | ||
|
||
## Local Development | ||
|
||
### Requirements | ||
|
||
[Node.js](https://nodejs.org) and [Yarn](https://yarnpkg.com/) are required to install dependencies and run scripts via `yarn`. | ||
|
||
[Vite](https://vitejs.dev/) is required to bundle and serve the web application. This is included as part of the projects dev dependencies. | ||
|
||
### Available Commands | ||
|
||
| Command | Description | | ||
|---------|-------------| | ||
| `yarn install --frozen-lockfile` | Install project dependencies | | ||
| `yarn start` | Build project and open web server running project | | ||
| `yarn build` | Builds code bundle for production | | ||
| `yarn lint` | Uses ESLint to lint code | | ||
|
||
### Writing Code | ||
|
||
After cloning the repo, run `yarn install --frozen-lockfile` from your project directory. Then, you can start the local development | ||
server by running `yarn start`. | ||
|
||
After starting the development server with `yarn start`, you can edit any files in the `src` folder | ||
and parcel will automatically recompile and reload your server (available at `http://localhost:8080` | ||
by default). | ||
|
||
### Deploying Code | ||
|
||
After you run the `yarn build` command, your code will be built into a single bundle located at | ||
`dist/*` along with any other assets you project depended. | ||
|
||
If you put the contents of the `dist` folder in a publicly-accessible location (say something like `http://myserver.com`), | ||
you should be able to open `http://myserver.com/index.html` and play your game. | ||
|
||
### 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:8080/path-to-file-your-file/file-name.file-type`. | ||
|
||
## Ideas for improvement | ||
|
||
* add simple score mechanic | ||
* add high local high scores | ||
* create npm package for the simon game class | ||
* add support for mobile | ||
* add title & game over screens |
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,9 @@ | ||
{ | ||
"root": true, | ||
"extends": "@devshareacademy/eslint-config", | ||
"parserOptions": { | ||
"project": "./tsconfig.json" | ||
}, | ||
"rules": {}, | ||
"ignorePatterns": ["node_modules", "dist"] | ||
} |
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,11 @@ | ||
import { defineConfig } from 'vite'; | ||
|
||
export default defineConfig({ | ||
build: { | ||
rollupOptions: { | ||
output: { | ||
entryFileNames: 'assets/js/[name]-[hash].js', | ||
}, | ||
}, | ||
}, | ||
}); |
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,27 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Phaser 3 - Simon</title> | ||
<style> | ||
html, | ||
body, | ||
.container { | ||
margin: 0px; | ||
height: 100vh; | ||
width: 100vw; | ||
overflow: hidden; | ||
background: #d7d7d7; | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div class="container" id="game-container"> | ||
<h1>Phaser 3 - Simon</h1> | ||
</div> | ||
<script type="module" src="/src/main.ts"></script> | ||
</body> | ||
</html> |
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,40 @@ | ||
{ | ||
"name": "@devshareacademy/phaser-3-simon-prototype", | ||
"version": "1.0.0", | ||
"description": "A basic Phaser 3 Typescript prototype for the game Simon.", | ||
"scripts": { | ||
"start": "vite --config config/vite.config.js", | ||
"build": "tsc && vite build --config config/vite.config.js", | ||
"serve": "vite preview --config config/vite.config.js", | ||
"lint": "eslint ./src --ext .ts,.tsx --config ./config/.eslintrc" | ||
}, | ||
"author": "scottwestover", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/devshareacademy/phaser-3-typescript-games-and-examples.git" | ||
}, | ||
"homepage": "https://github.com/devshareacademy/phaser-3-typescript-games-and-examples", | ||
"devDependencies": { | ||
"@devshareacademy/eslint-config": "0.0.16", | ||
"@devshareacademy/prettier-config": "0.0.4", | ||
"@devshareacademy/tsconfig": "0.0.3", | ||
"@typescript-eslint/eslint-plugin": "5.22.0", | ||
"@typescript-eslint/parser": "5.22.0", | ||
"eslint": "8.14.0", | ||
"eslint-config-prettier": "8.5.0", | ||
"eslint-plugin-prettier": "4.0.0", | ||
"prettier": "2.6.2", | ||
"typescript": "4.6.4", | ||
"vite": "2.9.13" | ||
}, | ||
"dependencies": { | ||
"phaser": "3.55.2" | ||
}, | ||
"resolutions": {}, | ||
"prettier": "@devshareacademy/prettier-config", | ||
"volta": { | ||
"node": "16.15.0", | ||
"yarn": "1.22.11" | ||
} | ||
} |
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,13 @@ | ||
☐ project setup | ||
☐ create buttons | ||
☐ add hover events with alpha | ||
☐ core simon logic | ||
☐ generate a random element in the sequence | ||
☐ method to handle player input | ||
☐ track player input until sequence match, or mismatch | ||
☐ reset game | ||
☐ connect simon to core scene | ||
☐ add simple state for tracking game state | ||
☐ play a sequence | ||
☐ change game alpha and play sound | ||
☐ wait for player input |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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,144 @@ | ||
import Phaser from 'phaser'; | ||
import Simon from './simon'; | ||
import { sleep } from './utils'; | ||
|
||
const ASSET_KEYS = { | ||
SOUND1: 'SOUND1', | ||
SOUND2: 'SOUND2', | ||
SOUND3: 'SOUND3', | ||
SOUND4: 'SOUND4', | ||
} as const; | ||
|
||
type GameState = keyof typeof GAME_STATE; | ||
|
||
const GAME_STATE = { | ||
INITIAL: 'INITIAL', | ||
PLAYING_PATTERN: 'PLAYING_PATTERN', | ||
WAITING_FOR_INPUT: 'WAITING_FOR_INPUT', | ||
DONE: 'DONE', | ||
} as const; | ||
|
||
class Game extends Phaser.Scene { | ||
#gameState!: GameState; | ||
#simonGame!: Simon; | ||
#buttons!: Phaser.GameObjects.Rectangle[]; | ||
|
||
constructor() { | ||
super({ key: 'Game' }); | ||
} | ||
|
||
public init() { | ||
this.#gameState = GAME_STATE.INITIAL; | ||
this.#buttons = []; | ||
this.#simonGame = new Simon(); | ||
} | ||
|
||
public preload(): void { | ||
this.load.audio(ASSET_KEYS.SOUND1, 'assets/audio/simonSound1.mp3'); | ||
this.load.audio(ASSET_KEYS.SOUND2, 'assets/audio/simonSound2.mp3'); | ||
this.load.audio(ASSET_KEYS.SOUND3, 'assets/audio/simonSound3.mp3'); | ||
this.load.audio(ASSET_KEYS.SOUND4, 'assets/audio/simonSound4.mp3'); | ||
} | ||
|
||
public create(): void { | ||
// game width = | ||
const redButton = this.#createButton(20, 20, 0xdb0a8b, 0xf535aa, 0, ASSET_KEYS.SOUND1); | ||
const greenButton = this.#createButton(230, 20, 0x08c418, 0x16f529, 1, ASSET_KEYS.SOUND2); | ||
const yellowButton = this.#createButton(20, 230, 0xe6e600, 0xffff33, 2, ASSET_KEYS.SOUND3); | ||
const blueButton = this.#createButton(230, 230, 0x0066cc, 0x1589ff, 3, ASSET_KEYS.SOUND4); | ||
this.#buttons = [redButton, greenButton, yellowButton, blueButton]; | ||
|
||
// play sequence for player to remember and transition to wait for player input | ||
this.#playSequence().catch(() => { | ||
// do nothing | ||
}); | ||
} | ||
|
||
#createButton( | ||
x: number, | ||
y: number, | ||
color: number, | ||
hoverColor: number, | ||
buttonId: number, | ||
audioAssetKey: string, | ||
): Phaser.GameObjects.Rectangle { | ||
// create Phaser rectangle game object that is a little transparent and that is interactive (allows for player interaction) | ||
const button = this.add.rectangle(x, y, 200, 200, color).setOrigin(0).setAlpha(0.4).setInteractive(); | ||
|
||
// set custom data attribute to track hover and original color | ||
button.setDataEnabled(); | ||
button.data.set({ color, hoverColor, id: buttonId, audioAssetKey }, undefined); | ||
|
||
// add event listener for pointer over event (mouse hovers over element), when event fires update color and fill | ||
button.on(Phaser.Input.Events.POINTER_OVER as string, () => { | ||
if (this.#gameState === GAME_STATE.WAITING_FOR_INPUT) { | ||
button.fillColor = hoverColor; | ||
button.setAlpha(1); | ||
} | ||
}); | ||
|
||
// add event listener for pointer out event (mouse was hovering over element and then leaves), when event fires update color and fill | ||
button.on(Phaser.Input.Events.POINTER_OUT as string, () => { | ||
if (this.#gameState === GAME_STATE.WAITING_FOR_INPUT) { | ||
button.fillColor = color; | ||
button.setAlpha(0.4); | ||
} | ||
}); | ||
|
||
// add event listener for click events, when event fires update the player moves in the simon game logic | ||
button.on(Phaser.Input.Events.POINTER_DOWN as string, () => { | ||
if (this.#gameState === GAME_STATE.WAITING_FOR_INPUT) { | ||
this.sound.play(audioAssetKey); | ||
this.#simonGame.checkPlayerMove(button.data.values.id as number); | ||
|
||
button.fillColor = color; | ||
button.setAlpha(0.4); | ||
|
||
// check to see if game is over and go to DONE state | ||
if (this.#simonGame.isGameOver) { | ||
this.#gameState = GAME_STATE.DONE; | ||
console.log('Game is over'); | ||
return; | ||
} | ||
|
||
// check if the player sequence is complete, if so go to the next round | ||
if (this.#simonGame.isPlayerSequenceComplete) { | ||
this.#gameState = GAME_STATE.PLAYING_PATTERN; | ||
this.#simonGame.generateNextSequenceElement(); | ||
this.#playSequence().catch(() => { | ||
// do nothing | ||
}); | ||
} | ||
} | ||
}); | ||
return button; | ||
} | ||
|
||
async #playSequence(): Promise<void> { | ||
await sleep(1000); | ||
const currentSequence = this.#simonGame.sequence; | ||
for (const num of currentSequence) { | ||
this.#buttons[num].fillColor = this.#buttons[num].data.values.hoverColor as number; | ||
this.#buttons[num].setAlpha(1); | ||
this.sound.play(this.#buttons[num].data.values.audioAssetKey as string); | ||
await sleep(1000); | ||
this.#buttons[num].fillColor = this.#buttons[num].data.values.color as number; | ||
this.#buttons[num].setAlpha(0.4); | ||
await sleep(400); | ||
} | ||
|
||
this.#gameState = GAME_STATE.WAITING_FOR_INPUT; | ||
} | ||
} | ||
|
||
const gameConfig: Phaser.Types.Core.GameConfig = { | ||
type: Phaser.CANVAS, | ||
pixelArt: true, | ||
parent: 'game-container', | ||
width: 450, | ||
height: 450, | ||
backgroundColor: '#5c5b5b', | ||
scene: [Game], | ||
}; | ||
|
||
const game = new Phaser.Game(gameConfig); |
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,69 @@ | ||
function getRandomIntInclusive(min: number, max: number): number { | ||
min = Math.ceil(min); | ||
max = Math.floor(max); | ||
return Math.floor(Math.random() * (max - min + 1) + min); | ||
} | ||
|
||
enum SimonError { | ||
INVALID_MOVE_GAME_IS_OVER = 'Game has already ended, please reset the game state.', | ||
} | ||
|
||
export default class Simon { | ||
#currentSequence: number[] = []; | ||
#playerMoves: number[] = []; | ||
#isGameOver = false; | ||
|
||
constructor() { | ||
this.#initializeGame(); | ||
} | ||
|
||
get sequence(): number[] { | ||
return [...this.#currentSequence]; | ||
} | ||
|
||
get isGameOver(): boolean { | ||
return this.#isGameOver; | ||
} | ||
|
||
get isPlayerSequenceComplete(): boolean { | ||
return this.#playerMoves.length === this.#currentSequence.length; | ||
} | ||
|
||
public resetGame(): void { | ||
this.#initializeGame(); | ||
} | ||
|
||
public generateNextSequenceElement() { | ||
// validate that the game is not already done | ||
if (this.#isGameOver) { | ||
throw new Error(SimonError.INVALID_MOVE_GAME_IS_OVER); | ||
} | ||
|
||
this.#currentSequence.push(getRandomIntInclusive(0, 3)); | ||
// reset tracked players moves | ||
this.#playerMoves = []; | ||
} | ||
|
||
public checkPlayerMove(x: number) { | ||
// validate that the game is not already done | ||
if (this.#isGameOver) { | ||
throw new Error(SimonError.INVALID_MOVE_GAME_IS_OVER); | ||
} | ||
|
||
// only allow moves to be added if we have not validated a player sequence | ||
if (this.#playerMoves.length === this.#currentSequence.length) return; | ||
|
||
// add new move to the player sequence | ||
this.#playerMoves.push(x); | ||
|
||
// update game state if this last move was valid or not | ||
this.#isGameOver = | ||
this.#playerMoves[this.#playerMoves.length - 1] !== this.#currentSequence[this.#playerMoves.length - 1]; | ||
} | ||
|
||
#initializeGame() { | ||
this.#currentSequence = []; | ||
this.#playerMoves = []; | ||
this.generateNextSequenceElement(); | ||
} | ||
} |
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,3 @@ | ||
export function sleep(milliseconds: number) { | ||
return new Promise((resolve) => setTimeout(resolve, milliseconds)); | ||
} |
Oops, something went wrong.