Commit 8b36ef0e authored by Ryan's avatar Ryan

create game, add unit tests, and update gitlab ci to run tests in new stage

parent 0a2c0b42
Pipeline #8515022 passed with stages
in 3 minutes and 43 seconds
image: node:6
before_script:
- npm i gulp -g
build:
type: build
script:
- npm i
- npm i gulp -g
- gulp
- gulp build-test
artifacts:
paths:
- built/
- node_modules/
test:
type: test
script:
- gulp run-test
artifacts:
paths:
- built/
\ No newline at end of file
var gulp = require("gulp");
var browserify = require("browserify");
var gulp = require('gulp');
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var tsify = require("tsify");
var tsify = require('tsify');
var browserSync = require('browser-sync').create();
var del = require('del');
var sourcemaps = require('gulp-sourcemaps');
var tap = require('gulp-tap');
var buffer = require('gulp-buffer');
var mocha = require('gulp-spawn-mocha');
var paths = {
pages: ['src/**/*.html'],
......@@ -11,7 +15,6 @@ var paths = {
vendor: ['vendor/**/*.js']
};
// for now we are going to have to run gulp clean manually
gulp.task('clean', function(cb) {
return del(['built/**'], function(err) {
cb(err);
......@@ -20,31 +23,31 @@ gulp.task('clean', function(cb) {
gulp.task('copy-pages', function() {
return gulp.src(paths.pages)
.pipe(gulp.dest("built"));
.pipe(gulp.dest('built'));
});
gulp.task("copy-assets", function () {
gulp.task('copy-assets', function () {
return gulp.src(paths.assets)
.pipe(gulp.dest("built/assets"));
.pipe(gulp.dest('built/assets'));
});
gulp.task("copy-vendor", function () {
gulp.task('copy-vendor', function () {
return gulp.src(paths.vendor)
.pipe(gulp.dest("built/vendor"));
.pipe(gulp.dest('built/vendor'));
});
gulp.task("default", ["copy-pages", "copy-assets", "copy-vendor"], function () {
gulp.task('default', ['copy-pages', 'copy-assets', 'copy-vendor'], function () {
return browserify({
basedir: 'src/lib',
debug: true,
entries: ['./game.ts'],
cache: {},
packageCache: {}
packageCache: {},
})
.plugin(tsify)
.bundle()
.pipe(source('game.js'))
.pipe(gulp.dest("built"));
.pipe(gulp.dest('built'));
});
gulp.task('watch', ['default'], function(done) {
......@@ -57,11 +60,28 @@ gulp.task('serve', ['default'], function () {
// Serve files from the root of this project
browserSync.init({
server: {
baseDir: "built"
baseDir: 'built'
},
ghostMode: false,
online: false
});
gulp.watch(["src/**/*.*"], ['watch']);
});
\ No newline at end of file
gulp.watch(['src/**/*.*'], ['watch']);
});
gulp.task('build-test', function () {
return gulp.src('src/tests/**/*.ts', { read: false })
.pipe(tap(function (file) {
// replace file contents with browserify's bundle stream
file.contents = browserify(file.path, { debug: true })
.plugin(tsify, { project: "./tsconfig.test.json" })
.bundle();
}))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}) )
.pipe(gulp.dest('built/tests'));
});
gulp.task('run-test', function() {
gulp.src(['./built/tests/**/*.ts']).pipe(mocha());
});
......@@ -10,15 +10,23 @@
"author": "",
"license": "ISC",
"dependencies": {
"phaser-ce": "^2.7.3"
"phaser-ce": "^2.7.10"
},
"devDependencies": {
"@types/chai": "^3.5.2",
"@types/mocha": "^2.2.41",
"browser-sync": "^2.18.8",
"browserify": "^14.1.0",
"chai": "^3.5.0",
"del": "^2.2.2",
"gulp": "^3.9.1",
"gulp-buffer": "0.0.2",
"gulp-sourcemaps": "^2.6.0",
"gulp-spawn-mocha": "^3.1.0",
"gulp-tap": "^1.0.1",
"gulp-typescript": "^3.1.5",
"gulp-uglify": "^2.1.0",
"mocha": "^3.2.0",
"tsify": "^3.0.1",
"typescript": "^2.2.1",
"vinyl-source-stream": "^1.1.0"
......
import { Player } from "./player";
import { Weapon, BulletFactory } from "./weapon";
import { Starfield } from "./starfield";
window.onload = function() {
var game = new Phaser.Game(800, 600, Phaser.AUTO, 'gameCanvas', { preload: preload, create: create });
var game = new Phaser.Game(800, 600, Phaser.AUTO, 'gameCanvas', { preload: preload, create: create, update: update });
var player: Player;
var weapon: Weapon;
var starfield: Starfield;
function preload () {
game.load.image('logo', 'assets/phaser.png');
game.load.image('player', 'assets/player.png');
game.load.image('bullet', 'assets/bullet.png');
game.load.image('starfield', 'assets/starfield.png');
}
function create () {
var logo = game.add.sprite(game.world.centerX, game.world.centerY, 'logo');
logo.anchor.setTo(0.5, 0.5);
var tilesprite = game.add.tileSprite(0, 0, 800, 600, 'starfield');
starfield = new Starfield(tilesprite, 500);
var playerSprite = game.add.sprite(400, 550, 'player');
playerSprite.anchor.setTo(0.5);
player = new Player(game.input, playerSprite, 150);
var bulletFactory = new BulletFactory(game.add.group(), 30);
weapon = new Weapon(bulletFactory, player.sprite, 0.25, 1000);
player.loadWeapon(weapon);
starfield.setPlayer(player.sprite);
}
function update() {
var deltaSeconds = game.time.elapsedMS / 1000;
player.update(deltaSeconds);
weapon.update(deltaSeconds);
starfield.update(deltaSeconds);
}
}
import { Weapon } from "./weapon";
export class Player {
private upKey: Phaser.Key;
private downKey: Phaser.Key;
private leftKey: Phaser.Key;
private rightKey: Phaser.Key;
private weapon: Weapon;
constructor(private input: Phaser.Input, public sprite: Phaser.Sprite, private speed: number) {
this.upKey = this.input.keyboard.addKey(Phaser.Keyboard.UP);
this.downKey = this.input.keyboard.addKey(Phaser.Keyboard.DOWN);
this.leftKey = this.input.keyboard.addKey(Phaser.Keyboard.LEFT);
this.rightKey = this.input.keyboard.addKey(Phaser.Keyboard.RIGHT);
this.input.onDown.add(this.onClickDown.bind(this));
this.input.onUp.add(this.onClickUp.bind(this));
}
public loadWeapon(weapon: Weapon): void {
this.weapon = weapon;
}
private onClickDown(): void {
if (this.weapon)
this.weapon.trigger(true);
}
private onClickUp(): void {
if (this.weapon)
this.weapon.trigger(false);
}
public update(delta: number): void {
var y = 0;
var x = 0;
if (this.upKey.isDown)
y--;
if (this.downKey.isDown)
y++;
if (this.leftKey.isDown)
x--;
if (this.rightKey.isDown)
x++;
this.sprite.x += x * this.speed * delta;
this.sprite.y += y * this.speed * delta;
let angle = Math.atan2(this.input.y - this.sprite.y, this.input.x - this.sprite.x) + Math.PI / 2;
this.sprite.rotation = angle;
}
}
export class Starfield {
private player: Phaser.Sprite;
constructor(private tilesprite: Phaser.TileSprite, private speed: number) {
}
public setPlayer(player: Phaser.Sprite): void {
this.player = player;
}
public update(delta: number): void {
if (this.player) {
var parentRotation = this.player.rotation + Math.PI / 2;
var velx = Math.cos(parentRotation);
var vely = Math.sin(parentRotation);
this.tilesprite.tilePosition.x += velx * this.speed * delta;
this.tilesprite.tilePosition.y += vely * this.speed * delta;
}
}
}
export class Weapon {
private isTriggered: boolean = false;
private currentTimer: number = 0;
constructor(private bulletFactory: BulletFactory, private parent: Phaser.Sprite, private cooldown: number, private bulletSpeed: number) {
}
public trigger(on: boolean): void {
this.isTriggered = on;
}
public update(delta: number): void {
this.currentTimer -= delta;
if (this.isTriggered && this.currentTimer <= 0) {
this.shoot();
}
}
private shoot(): void {
// Reset timer
this.currentTimer = this.cooldown;
// Get velocity direction from player rotation
var parentRotation = this.parent.rotation + Math.PI / 2;
var velx = Math.cos(parentRotation);
var vely = Math.sin(parentRotation);
// Apply a small forward offset so bullet shoots from head of ship instead of the middle
var posx = this.parent.x - velx * 10
var posy = this.parent.y - vely * 10;
this.bulletFactory.generate(posx, posy, -velx * this.bulletSpeed, -vely * this.bulletSpeed, this.parent.rotation);
}
}
export class BulletFactory {
constructor(private bullets: Phaser.Group, private poolSize: number) {
this.bullets.enableBody = true;
this.bullets.physicsBodyType = Phaser.Physics.ARCADE;
this.bullets.createMultiple(30, 'bullet');
this.bullets.setAll('anchor.x', 0.5);
this.bullets.setAll('anchor.y', 0.5);
this.bullets.setAll('outOfBoundsKill', true);
this.bullets.setAll('checkWorldBounds', true);
}
public generate(posx: number, posy: number, velx: number, vely: number, rot: number): Phaser.Sprite {
var bullet = this.bullets.getFirstExists(false);
if (bullet) {
bullet.reset(posx, posy);
bullet.rotation = rot;
bullet.body.velocity.x = velx;
bullet.body.velocity.y = vely;
}
return bullet;
}
}
import { expect } from 'chai';
import { Weapon, BulletFactory } from '../lib/weapon';
describe('Weapon', () => {
var subject: Weapon;
var shotsFired: number = 0;
// Mocked bullet factory
var bulletFactory: BulletFactory = <BulletFactory>{
generate: function(px, py, vx, vy, rot) {
shotsFired++;
}
};
var parent: any = { x: 0, y: 0 };
beforeEach(() => {
shotsFired = 0;
subject = new Weapon(bulletFactory, parent, 0.25, 1);
});
it('should shoot if not in cooldown', () => {
subject.trigger(true);
subject.update(0.1);
expect(shotsFired).to.equal(1);
});
it('should not shoot during cooldown', () => {
subject.trigger(true);
subject.update(0.1);
subject.update(0.1);
expect(shotsFired).to.equal(1);
});
it('should shoot after cooldown ends', () => {
subject.trigger(true);
subject.update(0.1);
subject.update(0.3); // longer than timeout
expect(shotsFired).to.equal(2);
});
it('should not shoot if not triggered', () => {
subject.update(0.1);
subject.update(0.1);
expect(shotsFired).to.equal(0);
});
});
......@@ -10,6 +10,7 @@
},
"exclude": [
"node_modules",
"built"
"built",
"src/tests"
]
}
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": false,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true,
"outDir": "built",
"target": "ES5"
},
"exclude": [
"node_modules",
"built"
]
}
{
"name": "gitlab-game-demo",
"dependencies": {},
"globalDevDependencies": {
"phaser": "github:photonstorm/phaser-ce/typescript/typings.json"
}
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment