├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── gasp.mp3 └── particle.png ├── favicon.svg ├── index.html ├── package.json ├── src ├── main.ts ├── menu-scene.ts └── style.css ├── tsconfig.json └── vite.config.js /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ main ] 10 | pull_request: 11 | branches: [ main ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v2 27 | 28 | # Runs a single command using the runners shell 29 | - name: npm install 30 | run: npm install 31 | 32 | # Runs a set of commands using the runners shell 33 | - name: npm run build 34 | run: npm run build 35 | 36 | - name: Deploy 🚀 37 | uses: JamesIves/github-pages-deploy-action@4.1.1 38 | with: 39 | branch: gh-pages # The branch the action should deploy to. 40 | folder: dist # The folder the action should deploy. 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | package-lock.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yuval Greenfield 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## See this template in action 2 | See it live at https://ubershmekel.github.io/vite-phaser-ts-starter/ 3 | 4 | ## Get Started 5 | This is an example template. To try it out do the following: 6 | 7 | 1. Clone this repo 8 | 1. Run `npm install` 9 | 1. Run `npm run dev` 10 | 1. You should see a URL where your game shows up 11 | 12 | ``` 13 | { 14 | "scripts": { 15 | "dev": "vite", // start dev server 16 | "build": "vite build", // build for production 17 | "serve": "vite preview" // locally preview production build 18 | } 19 | } 20 | ``` 21 | 22 | Btw the live demo gets built by the github action at `.github/workflows/main.yml`. 23 | 24 | ## Why this tech stack 25 | 26 | I looked at quite a few web game frameworks. I settled on this setup because: 27 | 28 | * Phaser is the most prominent web game framework, with a lot of examples for pretty much every scenario. 29 | * Typescript lets me auto-complete everything and makes sure I avoid silly typo bugs. 30 | * Vite is much faster and simpler than Rollup and Webpack. I practically didn't have to do anything to get Phaser to work here, there's no complicated config file. The development-build-refresh cycle seems instant. It's fast enough that I never felt the need to measure it. Vite was built by evanw@ the person that built Vue.js. 31 | -------------------------------------------------------------------------------- /assets/gasp.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubershmekel/vite-phaser-ts-starter/2d9f7d7d1394e736dc8c59750ae8bec6cadd96e3/assets/gasp.mp3 -------------------------------------------------------------------------------- /assets/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ubershmekel/vite-phaser-ts-starter/2d9f7d7d1394e736dc8c59750ae8bec6cadd96e3/assets/particle.png -------------------------------------------------------------------------------- /favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite Phaser Game 8 | 9 | 10 |

Vite Phaser Game

11 |

This is an example of setting up a Phaser, TypeScript, Vite game. See source on github.

12 |
13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ld48", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "tsc && vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "typescript": "^4.8.4", 11 | "vite": "^3.1.4" 12 | }, 13 | "dependencies": { 14 | "phaser": "^3.55.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import './style.css' 2 | 3 | // const app = document.querySelector('#app')! 4 | 5 | // app.innerHTML = ` 6 | //

Hello Vite!

7 | // Documentation 8 | //
9 | // `; 10 | 11 | import 'phaser'; 12 | import { menu } from './menu-scene'; 13 | 14 | 15 | const GameConfig: Phaser.Types.Core.GameConfig = { 16 | title: 'ExampleGame', 17 | url: 'https://github.com/digitsensitive/phaser3-typescript', 18 | version: '2.0', 19 | width: 800, 20 | height: 600, 21 | type: Phaser.AUTO, 22 | parent: 'app', 23 | // `as as Phaser.Types.Scenes.SettingsConfig[]` is required until https://github.com/photonstorm/phaser/pull/6235 24 | scene: [menu()] as Phaser.Types.Scenes.SettingsConfig[], 25 | input: { 26 | keyboard: true 27 | }, 28 | physics: { 29 | default: 'arcade', 30 | arcade: { 31 | gravity: { y: 0 }, 32 | debug: false 33 | } 34 | }, 35 | backgroundColor: '#300000', 36 | render: { pixelArt: false, antialias: true }, 37 | scale: { 38 | mode: Phaser.Scale.FIT, 39 | autoCenter: Phaser.Scale.CENTER_BOTH, 40 | // `fullscreenTarget` must be defined for phones to not have 41 | // a small margin during fullscreen. 42 | fullscreenTarget: 'app', 43 | expandParent: false, 44 | }, 45 | }; 46 | 47 | 48 | export class Game extends Phaser.Game { 49 | constructor(config: Phaser.Types.Core.GameConfig) { 50 | super(config); 51 | } 52 | } 53 | 54 | window.addEventListener('load', () => { 55 | // Expose `_game` to allow debugging, mute button and fullscreen button 56 | (window as any)._game = new Game(GameConfig); 57 | }); 58 | -------------------------------------------------------------------------------- /src/menu-scene.ts: -------------------------------------------------------------------------------- 1 | import 'phaser'; 2 | import particleUrl from '../assets/particle.png'; 3 | import gaspUrl from '../assets/gasp.mp3'; 4 | 5 | export const menuSceneKey = 'MenuScene'; 6 | 7 | export function menu(): Phaser.Types.Scenes.SettingsConfig | Phaser.Types.Scenes.CreateSceneFromObjectConfig { 8 | let startKey: Phaser.Input.Keyboard.Key; 9 | let sprites: {s: Phaser.GameObjects.Image, r: number }[]; 10 | 11 | return { 12 | key: menuSceneKey, 13 | preload() { 14 | sprites = []; 15 | startKey = this.input.keyboard.addKey( 16 | Phaser.Input.Keyboard.KeyCodes.S, 17 | ); 18 | startKey.isDown = false; 19 | this.load.image('particle', particleUrl); 20 | this.load.audio('gasp', gaspUrl); 21 | }, 22 | create() { 23 | this.add.text(0, 0, 'Press S to restart scene', { 24 | fontSize: '60px', 25 | fontFamily: "Helvetica", 26 | }); 27 | 28 | this.add.image(100, 100, 'particle'); 29 | 30 | for (let i = 0; i < 300; i++) { 31 | const x = Phaser.Math.Between(-64, 800); 32 | const y = Phaser.Math.Between(-64, 600); 33 | 34 | const image = this.add.image(x, y, 'particle'); 35 | image.setBlendMode(Phaser.BlendModes.ADD); 36 | sprites.push({ s: image, r: 2 + Math.random() * 6 }); 37 | } 38 | }, 39 | update() { 40 | if (startKey.isDown) { 41 | this.sound.play('gasp'); 42 | this.scene.start(menuSceneKey); 43 | } 44 | 45 | for (let i = 0; i < sprites.length; i++) { 46 | const sprite = sprites[i].s; 47 | 48 | sprite.y -= sprites[i].r; 49 | 50 | if (sprite.y < -256) 51 | { 52 | sprite.y = 700; 53 | } 54 | } 55 | }, 56 | } 57 | } -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 8px; 4 | } 5 | 6 | #app { 7 | font-family: Avenir, Helvetica, Arial, sans-serif; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | color: #ff3c00; 11 | height: 98%; 12 | width: 98%; 13 | margin: auto; 14 | } 15 | 16 | .window-controls { 17 | margin-bottom: 0.5em; 18 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "lib": ["ESNext", "DOM", "scripthost"], 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "types": ["vite/client"], 12 | "noEmit": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "noImplicitReturns": true 16 | }, 17 | "include": ["./src"] 18 | } 19 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // Define `base` because this deploys to user.github.io/repo-name/ 3 | base: './', 4 | build: { 5 | // Do not inline images and assets to avoid the phaser error 6 | // "Local data URIs are not supported" 7 | assetsInlineLimit: 0 8 | }, 9 | } --------------------------------------------------------------------------------