Update entire project to leverage current Phaser 3 template setup

Switch from gulp to Webpack, use eslint, and more.
This commit is contained in:
James Skemp 2020-05-11 19:47:07 -05:00
parent 47303c0573
commit 31537f73ab
28 changed files with 4636 additions and 61867 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*.{ts,tsx}]
indent_style = tab
trim_trailing_whitespace = true
[*.yml]
indent_style = space
indent_size = 2

4
.eslintignore Normal file
View File

@ -0,0 +1,4 @@
# Don't lint node_modules
node_modules
# Don't lint our build files
public

22
.eslintrc.js Normal file
View File

@ -0,0 +1,22 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
],
rules: {
"indent": ["warn", "tab"],
"no-console": ["warn"],
"no-multiple-empty-lines": ["warn", { "max": 1 }]
},
env: {
"browser": true,
"commonjs": true,
"es2017": true
}
};

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
# build directories
public/
# legacy
build/
dist/

20
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,20 @@
image: node:latest
cache:
paths:
- node_modules/
pages:
cache:
paths:
- node_modules/
script:
- npm install typescript -g
- npm install
- npm run build
artifacts:
paths:
- public
only:
- master

7
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"files.exclude": {
"node_modules": true,
"package-lock.json": true
},
"files.insertFinalNewline": true
}

View File

@ -1,7 +1,7 @@
# Fishy-Fishy, Example Phaser 3 Game
The following is a game developed using Phaser 3 and TypeScript, using NodeJS and Visual Studio Code for development.
Phaser starter projects can be found at https://github.com/JamesSkemp/phaser-starter-templates.
A Phaser 3 starter project can be found at https://github.com/JamesSkemp/phaser-3-vsc-typescript-nodejs.
## What the Base Project Provides
@ -12,7 +12,7 @@ Phaser starter projects can be found at https://github.com/JamesSkemp/phaser-sta
- This directory is intended to store any plugins that are used by your game.
- ts
- This directory is intended to store your TypeScript files, and includes a .gitignore so that any JavaScript files that are built aren't committed.
- A possible directory structure, as well as a few starter states, have been added for ease, but can be removed without issue.
- A possible directory structure, as well as a few starter scenes, have been added for ease, but can be removed without issue.
- app.css can include any styling necessary for your application.
- favicon.ico
- HTML5 Logo by [World Wide Web Consortium/W3C](http://www.w3.org/) and included here based upon the [logo FAQ](http://www.w3.org/html/logo/faq.html) allowing it (and it seems like the best logo to start with).
@ -26,6 +26,17 @@ Phaser starter projects can be found at https://github.com/JamesSkemp/phaser-sta
- tsconfig.json
- This file determines how TypeScript will compile the project.
- By default all TypeScript files will be compiled into a single app.js file in the root of the project.
- webpack.config.js
- This file contains the default webpack configuration.
- .editorconfig
- "EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs." See https://editorconfig.org/
- .eslintignore
- This file sets which directories/files should not be parsed by eslint.
- .eslintrc.js
- This file contains the starting eslint configurations. Base support for TypeScript is included.
- See ESLint [Rules](https://eslint.org/docs/rules/) for more information.
- .gitlab-ci.yml
- Adds support to build and host your site on [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/).
- .vscode/tasks.json
- Used by Visual Studio code when running the build task.
@ -34,14 +45,14 @@ To build this project you'll need [Node.js](https://nodejs.org) installed.
Next run `npm install` in the root directory to install the various dependencies.
Run `gulp` after modifying code to populate the **dist** directory with the final site contents.
Run `npm run build` after modifying code to populate the **public** directory with the final site contents. You can instead run `npm run build-dev` to build the JavaScript in development mode, or `npm run start:dev` to run an automatically updating instance.
If you'd like to run a simple web server, install http-server via `npm install http-server -g`, which can then be run from the dist directory by running `http-server`.
If you'd like to run a simple web server, install http-server via `npm install http-server -g`, which can then be run from the public directory by running `http-server`.
If you'd like to lint your TypeScript/JavaScript, run `npm run lint`.
## Upgrading Phaser
To upgrade Phaser CE run `npm upgrade phaser` (passing `--save` if you wish to update the package.json).
Phaser TypeScript definitions can currently be found at https://github.com/photonstorm/phaser3-docs/tree/master/typescript
To upgrade Phaser 3 run `npm upgrade phaser`.
## Credits
Assets from [Kenney's Fish Pack](https://kenney.nl/assets/fish-pack).

View File

@ -1,74 +0,0 @@
var gulp = require("gulp");
var ts = require("gulp-typescript");
var tsProject = ts.createProject("tsconfig.json");
var browserify = require("browserify");
var tsify = require("tsify");
var source = require("vinyl-source-stream");
var glob = require("glob");
var buffer = require("vinyl-buffer");
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var pump = require('pump');
var paths = {
content: [
"src/assets/**/*",
"src/plugins/**/*",
"src/app.css",
"src/favicon.ico",
"src/*.html"
]
};
gulp.task("copy-html", function () {
return gulp.src(paths.content, { base: "src" })
.pipe(gulp.dest('dist'));
});
gulp.task("copy-phaser", function () {
return gulp.src("./node_modules/phaser/dist/phaser.min.js")
.pipe(gulp.dest('dist/lib'));
});
gulp.task("default2", ["copy-html", "copy-phaser"], function (cb) {
pump(
[
gulp.src("./src/ts/**/*.ts").pipe(tsProject()),
//concat('app.js'),
/*uglify({
warnings: false,
parse: {
// parse options
},
compress: false,
mangle: false,
output: {
beautify: false,
preserve_line: false
// output options
},
nameCache: null, // or specify a name cache object
toplevel: false,
ie8: false,
}),*/
gulp.dest('dist')
], cb
);
return;
});
gulp.task("default", ["copy-html", "copy-phaser"], function () {
var typeScriptFiles = glob.sync('./src/ts/**/*.ts');
return browserify({
basedir: '.',
debug: false,
entries: [typeScriptFiles],
//entries: ["src/ts/Game.ts"],
extensions: ["*.ts"]
})
.plugin(tsify)
.bundle()
.pipe(source('app.js'))
.pipe(buffer())
.pipe(gulp.dest('dist'));
});

6201
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "fishy-fishy-phaser-game",
"version": "0.0.1",
"version": "0.0.2",
"description": "Fishy-Fishy is an example game based upon the Starter Project for Phaser 3 with TypeScript, NodeJS, and VS Code.",
"repository": {
"type": "git",
@ -16,17 +16,22 @@
"dependencies": {
"phaser": "^3.23.0"
},
"scripts": {
"build": "webpack --mode production",
"build-dev": "webpack --mode none",
"start:dev": "webpack-dev-server --mode production",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx || (exit 0)"
},
"devDependencies": {
"browserify": "^16.5.1",
"glob": "^7.1.6",
"gulp": "^3.9.1",
"gulp-concat": "^2.6.1",
"gulp-typescript": "^3.2.3",
"gulp-uglify": "^3.0.2",
"pump": "^3.0.0",
"tsify": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^2.32.0",
"@typescript-eslint/parser": "^2.32.0",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^5.1.1",
"eslint": "^7.0.0",
"ts-loader": "^7.0.4",
"typescript": "^3.8.3",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0"
}
}

59666
phaser.d.ts vendored

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

View File

@ -1,10 +1,12 @@
import * as phaser from "phaser";
import MainMenuScene from "./Scenes/MainMenuScene";
import Preloader from "./Scenes/Preloader";
import Boot from "./Scenes/Boot";
import Preloader from "./Scenes/Preloader";
import MainMenu from "./Scenes/MainMenu";
import SplashScreen from "./Scenes/SplashScreen";
import Utilities from "./Utilities";
import MainGame from "./Scenes/MainGame";
import MainSettings from "./Scenes/MainSettings";
const gameConfig: GameConfig = {
const gameConfig: Phaser.Types.Core.GameConfig = {
width: 640,
height: 480,
type: Phaser.AUTO,
@ -13,19 +15,44 @@ const gameConfig: GameConfig = {
};
export default class Game extends Phaser.Game {
constructor(config: GameConfig) {
console.log((new Date).toISOString() + ' : Entered Game constructor()');
constructor(config: Phaser.Types.Core.GameConfig) {
Utilities.LogSceneMethodEntry("Game", "constructor");
super(config);
this.scene.add(Boot.Name, Boot);
this.scene.add(Preloader.Name, Preloader);
this.scene.add(MainMenuScene.Name, MainMenuScene);
this.scene.add(SplashScreen.Name, SplashScreen);
this.scene.add(MainMenu.Name, MainMenu);
this.scene.add(MainGame.Name, MainGame);
this.scene.add(MainSettings.Name, MainSettings);
this.scene.start(Boot.Name);
}
};
}
window.onload = () => {
var game = new Game(gameConfig);
/**
* Workaround for inability to scale in Phaser 3.
* From http://www.emanueleferonato.com/2018/02/16/how-to-scale-your-html5-games-if-your-framework-does-not-feature-a-scale-manager-or-if-you-do-not-use-any-framework/
*/
function resize(): void {
const canvas = document.querySelector("canvas");
const width = window.innerWidth;
const height = window.innerHeight;
const wratio = width / height;
const ratio = Number(gameConfig.width) / Number(gameConfig.height);
if (wratio < ratio) {
canvas.style.width = width + "px";
canvas.style.height = (width / ratio) + "px";
} else {
canvas.style.width = (height * ratio) + "px";
canvas.style.height = height + "px";
}
}
window.onload = (): void => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const game = new Game(gameConfig);
// Uncomment the following two lines if you want the game to scale to fill the entire page, but keep the game ratio.
//resize();
//window.addEventListener("resize", resize, true);
};

1
src/ts/Plugins/README.md Normal file
View File

@ -0,0 +1 @@
This directory could contain any custom plugins used by the game.

1
src/ts/Prefabs/README.md Normal file
View File

@ -0,0 +1 @@
This directory could contain any prefabs/game object templates/classes the game might require.

View File

@ -1,4 +1,5 @@
import Preloader from "./Preloader";
import Utilities from "../Utilities";
export default class Boot extends Phaser.Scene {
/**
@ -6,10 +7,12 @@ export default class Boot extends Phaser.Scene {
*/
public static Name: string = "Boot";
preload(): void {
public preload(): void {
}
create() : void {
public create(): void {
Utilities.LogSceneMethodEntry("Boot", "create");
this.scene.start(Preloader.Name);
}
}
}

View File

@ -1,16 +1,17 @@
import Utilities from "../Utilities";
export default class MainGame extends Phaser.Scene {
/**
* Unique name of the scene.
*/
public static Name: string = "MainGame";
preload(): void {
public preload(): void {
}
create(): void {
console.log((new Date).toISOString() + ' : Entered MainGame create()');
}
public create(): void {
Utilities.LogSceneMethodEntry("MainGame", "create");
update() : void {
this.add.image(this.cameras.main.centerX, this.cameras.main.centerY, "Phaser-Logo-Small");
}
}
}

39
src/ts/Scenes/MainMenu.ts Normal file
View File

@ -0,0 +1,39 @@
import Utilities from "../Utilities";
import MainGame from "./MainGame";
import MainSettings from "./MainSettings";
export default class MainMenu extends Phaser.Scene {
/**
* Unique name of the scene.
*/
public static Name: string = "MainMenu";
public preload(): void {
// Preload as needed.
}
public create(): void {
Utilities.LogSceneMethodEntry("MainMenu", "create");
const textYPosition = this.cameras.main.height / 3;
const newGameText = this.add.text(this.cameras.main.centerX, textYPosition, "Start");
newGameText
.setFontFamily("monospace")
.setFontSize(40)
.setFill("#fff")
.setAlign("center")
.setOrigin(0.5);
newGameText.setInteractive();
newGameText.on("pointerdown", () => { this.scene.start(MainGame.Name); }, this);
const settingsText = this.add.text(this.cameras.main.centerX, textYPosition * 2, "Settings");
settingsText.setOrigin(0.5);
settingsText.setFontFamily("monospace").setFontSize(30).setFill("#fff");
settingsText.setInteractive();
settingsText.on("pointerdown", () => { this.scene.start(MainSettings.Name); }, this);
}
public update(): void {
// Update logic, as needed.
}
}

View File

@ -1,21 +0,0 @@
import MainGame from "./MainGame";
export default class MainMenuScene extends Phaser.Scene {
/**
* Unique name of the scene.
*/
public static Name: string = "MainMenuScene";
preload(): void {
}
create(): void {
console.log((new Date).toISOString() + ' : Entered MainMenu create()');
this.scene.start(MainGame.Name);
}
update() : void {
}
}

View File

@ -0,0 +1,23 @@
import Utilities from "../Utilities";
import MainMenu from "./MainMenu";
export default class MainSettings extends Phaser.Scene {
/**
* Unique name of the scene.
*/
public static Name: string = "MainSettings";
public create(): void {
Utilities.LogSceneMethodEntry("MainSettings", "create");
const startYPosition = this.cameras.main.height / 4;
const fontSize = 25;
// Add a button to return to the main menu.
const backText = this.add.text(this.cameras.main.centerX, startYPosition * 2, "Go Back");
backText
.setOrigin(0.5)
.setFontFamily("monospace").setFontSize(fontSize).setFill("#fff")
.setInteractive();
backText.on("pointerdown", () => { this.scene.start(MainMenu.Name); }, this);
}
}

View File

@ -1,4 +1,5 @@
import MainMenuScene from "./MainMenuScene";
import SplashScreen from "./SplashScreen";
import Utilities from "../Utilities";
export default class Preloader extends Phaser.Scene {
/**
@ -6,68 +7,12 @@ export default class Preloader extends Phaser.Scene {
*/
public static Name: string = "Preloader";
preload(): void {
var progressBar = this.add.graphics();
var progressBox = this.add.graphics();
progressBox.fillStyle(0x222222, 0.8);
progressBox.fillRect(640 / 4, 480 / 2 - 30, 640 / 2, 50);
var width = this.cameras.main.width;
var height = this.cameras.main.height;
var loadingText = this.make.text({
x: width / 2,
y: height / 2 - 50,
text: 'Loading...',
style: {
font: '20px monospace',
fill: '#ffffff'
}
});
loadingText.setOrigin(0.5, 0.5);
var percentText = this.make.text({
x: width / 2,
y: height / 2 - 5,
text: '0%',
style: {
font: '18px monospace',
fill: '#ffffff'
}
});
percentText.setOrigin(0.5, 0.5);
var assetText = this.make.text({
x: width / 2,
y: height / 2 + 50,
text: '',
style: {
font: '18px monospace',
fill: '#ffffff'
}
});
public preload(): void {
this.addProgressBar();
assetText.setOrigin(0.5, 0.5);
this.load.on('progress', function (value) {
percentText.setText(parseInt(value * 100 + '') + '%');
progressBar.clear();
progressBar.fillStyle(0xffffff, 1);
progressBar.fillRect((640 / 4) + 10, (480 / 2) - 30 + 10, (640 / 2 - 10 - 10) * value, 30);
});
this.load.on('fileprogress', function (file) {
assetText.setText('Loading asset: ' + file.key);
});
this.load.on('complete', function () {
progressBar.destroy();
progressBox.destroy();
loadingText.destroy();
percentText.destroy();
assetText.destroy();
});
this.load.path = 'assets/';
this.load.path = "assets/";
this.load.image("phaser_pixel_medium_flat");
this.load.image("Phaser-Logo-Small");
this.load.image('transparent');
// This will eventually use the spritesheet, but for now add each image individually.
@ -78,12 +23,78 @@ export default class Preloader extends Phaser.Scene {
}
}
create(): void {
console.log((new Date).toISOString() + ' : Entered Preloader create()');
public create(): void {
Utilities.LogSceneMethodEntry("Preloader", "create");
this.scene.start(MainMenuScene.Name);
this.scene.start(SplashScreen.Name);
}
update() : void {
public update(): void {
}
/**
* Adds a progress bar to the display, showing the percentage of assets loaded and their name.
*/
private addProgressBar(): void {
const width = this.cameras.main.width;
const height = this.cameras.main.height;
const progressBar = this.add.graphics();
const progressBox = this.add.graphics();
progressBox.fillStyle(0x222222, 0.8);
progressBox.fillRect(width / 4, height / 2 - 30, width / 2, 50);
const loadingText = this.make.text({
x: width / 2,
y: height / 2 - 50,
text: "Loading...",
style: {
font: "20px monospace",
fill: "#ffffff"
}
});
loadingText.setOrigin(0.5, 0.5);
const percentText = this.make.text({
x: width / 2,
y: height / 2 - 5,
text: "0%",
style: {
font: "18px monospace",
fill: "#ffffff"
}
});
percentText.setOrigin(0.5, 0.5);
const assetText = this.make.text({
x: width / 2,
y: height / 2 + 50,
text: "",
style: {
font: "18px monospace",
fill: "#ffffff"
}
});
assetText.setOrigin(0.5, 0.5);
this.load.on("progress", (value) => {
percentText.setText(parseInt(value * 100 + "", 10) + "%");
progressBar.clear();
progressBar.fillStyle(0xffffff, 1);
progressBar.fillRect((width / 4) + 10, (height / 2) - 30 + 10, (width / 2 - 10 - 10) * value, 30);
});
this.load.on("fileprogress", (file) => {
assetText.setText("Loading asset: " + file.key);
});
this.load.on("complete", () => {
progressBar.destroy();
progressBox.destroy();
loadingText.destroy();
percentText.destroy();
assetText.destroy();
});
}
}

15
src/ts/Scenes/README.md Normal file
View File

@ -0,0 +1,15 @@
This directory could include any scenes (previously states) the game might have.
This project includes the following scenes:
- Boot:
- Adds default settings for the game, including multitouch and device-specific settings.
- Could also preload any assets need to display a progress bar during asset preloading.
- Preloader:
- Used to preload any assets that might be used by the game.
- MainMenu:
- Could display a main menu to move to additional scenes.
While the scenes log basic information to the console during these scenes, these can of course be removed without issue.
**Game.ts** is where these scenes are defined for use by the game.

View File

@ -0,0 +1,44 @@
import Utilities from "../Utilities";
import MainMenu from "./MainMenu";
export default class SplashScreen extends Phaser.Scene {
/**
* Unique name of the scene.
*/
public static Name: string = "SplashScreen";
public preload(): void {
this.load.path = "assets/";
}
public create(): void {
Utilities.LogSceneMethodEntry("SplashScreen", "create");
const titleText = this.add.text(this.cameras.main.centerX, this.cameras.main.centerY * 0.5, "Fishy-Fishy")
.setOrigin(0.5, 0)
.setFontFamily("monospace").setFontSize(26).setFill("#fff");
const poweredByText = this.add.text(this.cameras.main.centerX, this.cameras.main.centerY - 25, "Powered By");
poweredByText.setOrigin(0.5, 0.5);
poweredByText.setFontFamily("monospace").setFontSize(20).setFill("#fff");
this.add.image(this.cameras.main.centerX, this.cameras.main.centerY, "phaser_pixel_medium_flat");
this.input.setDefaultCursor("pointer");
this.input.on("pointerdown", this.loadMainMenu, this);
this.time.addEvent({
// Run after three seconds.
delay: 3000,
callback: this.loadMainMenu,
callbackScope: this,
loop: false
});
}
/**
* Load the next scene, the main menu.
*/
private loadMainMenu(): void {
this.scene.start(MainMenu.Name);
}
}

18
src/ts/Utilities.ts Normal file
View File

@ -0,0 +1,18 @@
export default class Utilities {
/**
* Logs a particular message to the console.
* @param message Message to log.
*/
public static Log(message: string): void {
console.log((new Date()).toISOString() + " : " + message);
}
/**
* Logs the start of a method in a scene.
* @param sceneName Name of the scene.
* @param method Method called within the scene.
*/
public static LogSceneMethodEntry(sceneName: string, method: string): void {
this.Log("Entered " + sceneName + " " + method + "()");
}
}

View File

@ -1 +1 @@
/// <reference path="../../phaser.d.ts" />
/// <reference path="../../node_modules/phaser/types/phaser.d.ts" />

View File

@ -1,6 +1,9 @@
{
"compilerOptions": {
"target": "es5",
"rootDir": "./src/ts/"
"target": "es2017",
"module": "commonjs",
"esModuleInterop": true,
"rootDir": "./src/ts/",
"sourceMap": true
}
}
}

55
webpack.config.js Normal file
View File

@ -0,0 +1,55 @@
/*global __dirname */
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
entry: {
app: './src/ts/Game.ts'
},
module: {
rules: [{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
filename: 'app.js',
path: path.resolve(__dirname, 'public')
},
devtool: "source-map",
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin([{
from: './node_modules/phaser/dist/phaser.min.js',
to: 'lib'
}]),
new CopyWebpackPlugin([{
from: './src/*.html',
to: path.resolve(__dirname, 'public'),
flatten: true
}]),
new CopyWebpackPlugin([{
from: './src/*.css',
to: path.resolve(__dirname, 'public'),
flatten: true
}]),
new CopyWebpackPlugin([{
from: './src/*.ico',
to: path.resolve(__dirname, 'public'),
flatten: true
}]),
new CopyWebpackPlugin([{
from: './src/assets',
to: 'assets',
ignore: [ '*.md' ]
}])
],
devServer: {
open: true
}
};