Building Block: Gaming
2022-06-23
Index
1. Onderzoek
Je hebt individueel met minimaal drie prototypes onderzoek gedaan naar verschillende game mechanics. Deze prototypes zijn online speelbaar. Bekijk de bronnenlijst voor een overzicht van mechanics. Je hebt ook onderzoek gedaan naar verschillende manieren van besturing, denk daarbij aan het gebruik van een game controller en touchscreen. Je hebt een onderbouwd voorstel gedaan voor de multiplayer mogelijkheden binnen jullie concept
- [x] individueel minimaal 3 prototypes (online speelbaar)
- [x] 3x onderzoek naar mechanics adv prototypes
- [x] onderzoek naar verschillende manieren van besturing
- [x] onderbouwd voorstel multiplayer mogelijkheden
1.1 Prototypes Itereren
1.1.1 Prototype 1
https://heygamers.github.io/cmgt_thegame-code/
Game Loop
De eerste en belangrijkste mechanic die is toegevoegd is de gameloop, dit legt de basis voor de game zodat er naar mate van tijd dingen kunnen gebeuren en de player invloed kan uitoefenen op het level.
Controls (Movement)
De volgende belangrijke mechanic die ik heb toegevoegd is het besturen van het character door de player. Hierdoor kan de character lopen door het level en zo heb je al een van de core drives te pakken: exploration.
(Spawning) Enemies
Vervolgens heb ik de basis gelegd voor de andere core drive: challenge (vechten). Dit heb ik gedaan door een eerste versie van enemies toe te voegen, deze zijn nog niet heel slim en bewegen enkel naar 1 kant om te laten zien dat ze horen te bewegen. Later kan ik dit verder uitbouwen in combinatie met andere mechanics.
Collision
En als laatste in het eerste prototype heb ik collision detection toegevoegd, dit detect of de player met een enemy in contact komt. In dit geval beweegt de enemy enkel naar een andere plek en gebeurt er verder niks. Maar ook deze mechanic kan later verder uitgewerkt worden samen met een andere mechanic.
1.1.2 Prototype 2
https://luukftf.github.io/cmgt_thegame-code/
Attack
Het tweede prototype draait eigenlijk volledig om de core drive van "challenge", als eerst heb ik toegevoegd dat je als player kan attacken. Dit doe je door op spatiebalk of de "J" knop te drukken. Het character heeft een pixel zwaard vast waarmee je enemies kan "glitchen" (het idee is dat je niks of niemand doodmaakt, maar dat je ze hackt of glitcht). Wanneer je attacked word je movement speed lager.
Killing
Vervolgens moeten deze attacks ook invloed hebben op de enemies en daarom is het nu mogelijk om een enemy te "glitchen" door een attack erop uit te voeren. Wanneer dit is uitgevoerd en je hebt de enemy geraakt verdwijnt het.
Enemies 2.0
In deze iteratie zijn de enemies iets slimmer en minder voorspelbaar. Ze bewegen nu allebei de kanten van het level op, op willekeurige momenten draaien ze zich om. Ook is het aantal dat spawned bij elke keer spelen iets anders. Dit houd de replayability hoger.
1.1.3 Prototype 3
https://cmgt-thegame.github.io/cmgt_thegame-code/
De huidige laatste iteratie van de game draait vooral om het echte gamifien van ons concept. Deze elementen maken het echt een game: met een begin, einde en een doel.
XP & Health
De eerste mechanic die het meer tot een echte game maakt is het rewarden voor het verslaan van enemies en het punishen voor geraakt worden. Dit word door 2 stats bijgehouden: XP en Health (of in dit geval "energypoint" aka EP). Voor elke kill komt er een bepaalde hoeveelheid XP bij en voor elke keer dat je geraakt word gaat er een halve EP af. De player begint met 3 EP en wanner dit op 0 EP komt dan zal de player verliezen. Deze punten zijn in de UI op elke moment te zien, om zo instantly gratification te kunnen krijgen van het verslaan van enemies.
Title Schermen
Zodra je alle enemies hebt verslagen, heb je de game gewonnen en komt het "win screen" te voorschijn. Hier krijg je gelijk te zien hoeveel XP je hebt verkregen en wat je High Score is. Als je dood gaat word je geconfronteerd met het "game over screen", daar staat ook gelijk hoeveel XP je hebt gekregen maar de highscore word niet geupdate.
Audio
Als laatste kers op de taart is er audiofeedback toegevoegd. Bij het uitvoeren van bepaalde key acties gaat nu gepaard met een satisfying of afschrikkend geluid. Bij het actieveren van de attack krijg je een klein geluidje. Bij het killen van een enemy krijg je een duidelijk geluid wat je instant gratification geeft. Bij het krijgen van damage weet je gelijk dat er iets niet goed is. en bij het winnen of verliezen is er een grandiozer geluid die gepaard gaat met het gevoel van winnen of verliezen. Ook is een gezellig achtergrond muziekje die de sfeer in de game groter maakt.
1.2 Besturing
Deze game is vooral met de arcade machine als hoofdbesturing gemaakt. Hierbij is in het ontwerpen van de game rekening gehouden met de beperkte mogelijkheden en is het meer simplistisch gehouden. Op de arcade machine heb je 1 joystick die 4 richtingen op kan en daarnaast zijn er nog 6 buttons voor interactie. Tot nu toe word alleen de joystick en 1 van die buttons gebruikt.
Om deze besturing ook mogelijk te maken op een browser/computer is dit gespiegeld naar een keyboard indeling: zo is de joystick op "WASD" en de pijltjestoetsen te bedienen. De buttons zijn door de volgende keys aan te sturen: "JKLIOP".
1.3 Multiplayer Mogelijkheden
Onze game is vooral als singleplayer bedoeld. Maar mijn voorstel voor een multiplayer implementatie is als volgt: op de arcade machine zijn 2 sets aan controllers, hiermee zou je 2 character in 1 level kunnen bedienen met 2 verschillende mensen. Mijn idee is om een local co-op feature erin te bouwen waarmee je met elkaar de enemies kan vechten en het level kan exploren: je werkt samen om 1 doel te bereiken.
2. Uitwerking
Je hebt een substantiële bijdrage geleverd aan het programmeren van jullie game.
- [x] bijdrage programmeren laten zien
- [o] bijdrage programmeren uitleggen
Repository: https://github.com/cmgt-thegame/cmgt_thegame-code
Zoals je bij de commits kan zien heb ik veel bijdrage gehad bij dit project. Hier volgen de belangrijkste voorbeelden.
In de codesnippets heb ik stukjes korter gemaakt door herhalende code en of code die later aan bod komt te vervangen met een ⋮
teken. (Zie de repository voor de volledige code)
2.1 Game Class
Game.ts
import * as PIXI from 'pixi.js'
import { Player } from './Player'
⋮
import backgroundSound from "url:../audio/backgroundmusic.mp3";
PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
export class Game {
public app : PIXI.Application;
⋮
public backgroundSound : HTMLAudioElement;
constructor() {
// create a app canvas
this.app = new PIXI.Application({ backgroundColor: 0x372840, width: this.frameWidth, height: this.framelHeight })
document.body.appendChild(this.app.view)
// preload all our textures
this.loader = new PIXI.Loader()
this.loader
.add('playerTexture1', playerImage1)
⋮
.add("backgroundSound", backgroundSound);
this.loader.load(()=>this.startGame())
}
private startGame() {
console.log("starting the game")
this.enemyAmount = this.randomInt(5,25)
this.background = new Background(this.loader.resources["backgroundTexture"].texture!)
this.app.stage.addChild(this.background);
for (let i = 0; i < this.enemyAmount; i++) {
let robot1 = new Robot1(this.loader.resources["robotTexture"].texture!, this)
this.app.stage.addChild(robot1)
robot1.randomLocation()
this.robot1s.push(robot1)
}
this.damageSound = this.loader.resources["damageSound"].data!
⋮
this.backgroundSound = this.loader.resources["backgroundSound"].data!
this.player = new Player(this.loader.resources["playerTexture1"].texture!,
this.loader.resources["playerTexture2"].texture!,
this.loader.resources["playerTexture3"].texture!,
1, this, this.levelWidth, this.levelHeight)
this.app.stage.addChild(this.player);
this.ui = new UI(this)
this.backgroundSound.play();
this.backgroundSound.volume = 0.4
this.app.ticker.add((delta) => this.update(delta))
}
private update(delta : number) {
⋮
}
gameOver(){
⋮
}
collision(sprite1 : PIXI.Sprite, sprite2 : PIXI.Sprite) {
⋮
}
randomInt(min : number, max: number) {
⋮
}
}
new Game()
De game class is eigenlijk het belangrijkste onderdeel van dit project, dit is waar de code begint en waarvanuit alles word opgezet, aangestuurd en bijgewerkt.
Als eerst worden de verschillende libraries, classes en graphics geimport. Daarna word de game class geopend en worden alle variabele aangemaakt met de bijbehorende types.
Vervolgens word de constructor()
aangeroepen, hierin worden alle dingen geïnitialiseerd. In dit geval betekent dat eerst de pixi view word aangemaakt en alle content word gepreload. Waarna de startGame()
function word aangeroepen
In deze function worden alle game elementen zichtbaar aangemaakt, zoals het spawnen van enemies en het spawnen van de playable character. Vervolgens word er een ticker aangeroepen die elke frame de update()
aanroept. In de update worden alle updates voor de verschillende classes.
2.2 Player Class
Player.ts
import * as PIXI from 'pixi.js'
import { Game } from "./Game"
export class Player extends PIXI.Sprite {
private game : Game;
private speedMult : number = 2.5;
private xspeed : number = 0;
private yspeed : number = 0;
private levelWidth : number;
private levelHeight : number;
private centerx : number;
private centery : number;
public isAttacking : boolean = false;
constructor(texture1 : PIXI.Texture, texture2 : PIXI.Texture, texture3 : PIXI.Texture, character : number, game : Game, levelWidth : number, levelHeight : number) {
super(texture1);
⋮
this.game = game;
this.levelWidth = levelWidth;
this.levelHeight = levelHeight;
this.centerx = game.app.screen.width/2;
this.centery = game.app.screen.height/2;
this.anchor.set(0.5)
this.x = this.centerx -500
this.y = this.centery
this.scale.set(4, 4)
window.addEventListener("keydown", (e : KeyboardEvent) => this.onKeyDown(e));
window.addEventListener("keyup", (e : KeyboardEvent) => this.onKeyUp(e));
}
public update() {
this.x = this.clamp(this.x + this.xspeed*this.speedMult, 0 + 100, this.game.levelWidth - 150)
this.y = this.clamp(this.y + this.yspeed*this.speedMult, 0 + 150, this.game.levelHeight - 300)
}
clamp(num: number, min: number, max: number) {
return Math.min(Math.max(num, min), max)
}
⋮
}
Game.ts
⋮
export class Game {
⋮
private player : Player;
⋮
constructor() {
⋮
this.loader = new PIXI.Loader()
this.loader
.add('playerTexture1', playerImage1)
.add('playerTexture2', playerImage2)
.add('playerTexture3', playerImage3)
⋮
this.loader.load(()=>this.startGame())
⋮
}
private startGame() {
⋮
this.player = new Player(this.loader.resources["playerTexture1"].texture!,
this.loader.resources["playerTexture2"].texture!,
this.loader.resources["playerTexture3"].texture!,
1, this, this.levelWidth, this.levelHeight)
this.app.stage.addChild(this.player);
⋮
}
private update(delta : number) {
this.player.update()
⋮
for (let robot1 of this.robot1s) {
⋮
if (this.collision(robot1, this.player)) {
if(this.player.isAttacking) {
⋮
} else {
⋮
}
}
}
⋮
}
⋮
}
2.3 Controls
Player.ts
⋮
export class Player extends PIXI.Sprite {
⋮
constructor(texture1 : PIXI.Texture, texture2 : PIXI.Texture, texture3 : PIXI.Texture, character : number, game : Game, levelWidth : number, levelHeight : number) {
⋮
window.addEventListener("keydown", (e : KeyboardEvent) => this.onKeyDown(e));
window.addEventListener("keyup", (e : KeyboardEvent) => this.onKeyUp(e));
}
⋮
private onKeyDown(e: KeyboardEvent): void {
switch (e.key.toUpperCase()) {
case "A":
case "ARROWLEFT":
this.xspeed = -1
this.scale.set(-4, 4)
break;
case "D":
case "ARROWRIGHT":
this.xspeed = 1
this.scale.set(4, 4)
break;
case "W":
case "ARROWUP":
this.yspeed = -1
break;
case "S":
case "ARROWDOWN":
this.yspeed = 1
break;
case " ":
case "J":
case "ENTER":
this.isAttacking = true
this.speedMult = 2
this.game.swingSound.play();
const myfilter = new PIXI.filters.ColorMatrixFilter()
this.filters = [myfilter]
myfilter.predator(0.2, false)
break;
}
}
private onKeyUp(e: KeyboardEvent): void {
switch (e.key.toUpperCase()) {
case "":
break;
case "A":
case "D":
case "ARROWLEFT":
case "ARROWRIGHT":
this.xspeed = 0
break;
case "W":
case "S":
case "ARROWUP":
case "ARROWDOWN":
this.yspeed = 0
break;
case " ":
case "J":
case "ENTER":
this.isAttacking = false
this.speedMult = 2.5
const myfilter = new PIXI.filters.ColorMatrixFilter()
this.filters = [myfilter]
break;
}
}
}
2.4 Enemy Class
Robot1.ts
import * as PIXI from 'pixi.js'
import { Game } from "./Game"
export class Robot1 extends PIXI.Sprite {
private game : Game;
private timerLeft : number = 0;
private timerRight : number = 0;
private upperTimer : number;
private lowerTimer : number;
private xspeed : number = 0.5;
private yspeed : number = 0;
constructor(texture: PIXI.Texture, game : Game) {
super(texture)
this.game = game;
this.x = 0
this.y = 0
this.scale.set(-4, 4)
this.lowerTimer = this.randomInt(20, 50)*30
this.upperTimer = this.randomInt(10, 25)*30
console.log(this.lowerTimer, this.upperTimer)
}
public randomLocation() {
this.x = this.randomInt(0 + 800,this.game.levelWidth - this.getBounds().x - 100)
this.y = this.randomInt(0 + 150,this.game.levelHeight - this.getBounds().y - 300)
}
public update(delta : number) {
this.x = this.clamp(this.x + this.xspeed, 0 + 100, this.game.levelWidth - 150)
this.y = this.clamp(this.y + this.yspeed, 0 + 150, this.game.levelHeight - 300)
this.timerLeft++
this.timerRight++
if(this.timerLeft > this.lowerTimer) {
console.log("TIMER")
this.xspeed = 0.5
this.timerLeft = 0
this.x =- this.getBounds().x
this.scale.set(-4, 4)
}
if(this.timerRight > this.upperTimer) {
this.xspeed = -0.5
this.timerRight = 0
this.x =+ this.getBounds().x
this.scale.set(4, 4)
}
}
randomInt(min : number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
clamp(num: number, min: number, max: number) {
return Math.min(Math.max(num, min), max)
}
}
Game.ts
⋮
import { Robot1 } from './Robot1'
import robotImage from "../img/robot1.png";
⋮
export class Game {
⋮
private robot1s : Robot1[] = [];
private enemyAmount : number = 10;
⋮
constructor() {
⋮
this.loader = new PIXI.Loader()
this.loader
⋮
.add('robotTexture', robotImage)
⋮
this.loader.load(()=>this.startGame())
}
private startGame() {
⋮
this.enemyAmount = this.randomInt(5,25)
⋮
for (let i = 0; i < this.enemyAmount; i++) {
let robot1 = new Robot1(this.loader.resources["robotTexture"].texture!, this)
this.app.stage.addChild(robot1)
robot1.randomLocation()
this.robot1s.push(robot1)
}
⋮
}
private update(delta : number) {
⋮
for (let robot1 of this.robot1s) {
robot1.update(delta)
if (this.collision(robot1, this.player)) {
if(this.player.isAttacking) {
this.robot1s = this.robot1s.filter(r => r != robot1)
robot1.destroy();
⋮
} else {
robot1.randomLocation();
this.ui.substractHalfBar();
⋮
}
}
}
if (this.robot1s.length == 0) {
⋮
}
}
⋮
}
2.5 Collision
Game.ts
⋮
export class Robot1 extends PIXI.Sprite {
⋮
collision(sprite1 : PIXI.Sprite, sprite2 : PIXI.Sprite) {
const bounds1 = sprite1.getBounds();
const bounds2 = sprite2.getBounds();
return (
bounds1.x < bounds2.x + bounds2.width &&
bounds1.x + bounds1.width > bounds2.x &&
bounds1.y < bounds2.y + bounds2.height &&
bounds1.y + bounds1.height > bounds2.y
);
}
⋮
}
2.6 UI Class
UI.ts
import * as PIXI from 'pixi.js'
import { Game } from './Game'
export class UI {
private game : Game;
private xp : number;
private hp : number;
private hiscore : number = 0;
private graphics : PIXI.Graphics;
private textStyle : PIXI.TextStyle;
private basicText : PIXI.Text;
private messageField: PIXI.Text
private graphics2 : PIXI.Graphics;
private centerx : number;
private centery : number;
constructor(game: Game) {
this.game = game
this.centerx = game.app.screen.width/2;
this.centery = game.app.screen.height/2;
this.xp = 0
this.hp = 6
this.graphics = new PIXI.Graphics()
this.graphics.beginFill(0x524a63)
this.graphics.drawRect(40, 20, 500, 60)
this.graphics.endFill()
this.game.app.stage.addChild(this.graphics)
this.textStyle = new PIXI.TextStyle({
fontSize: 32,
fill: '#dfeded'
})
this.basicText = new PIXI.Text(`XP: ${this.xp} Energy: XX-`, this.textStyle)
this.basicText.x = 50
this.basicText.y = 30
this.game.app.stage.addChild(this.basicText);
const gameOverStyle = new PIXI.TextStyle({
fontSize: 70,
align:"center",
fontWeight: 'bold',
fill: ['#372840'],
wordWrap: true,
wordWrapWidth: 420
})
this.messageField = new PIXI.Text('', gameOverStyle)
this.messageField.x = this.centerx - 200
this.messageField.y = this.centery - 200
}
substractHalfBar() {
if(this.hp > 0) {
this.hp -= 1
}
}
addKillXP() {
this.xp += 100
}
public showGameOver(){
this.game.deathSound.play();
this.graphics2 = new PIXI.Graphics()
this.graphics2.beginFill(0xdfeded)
this.graphics2.drawRect(600, 200, 675, 375)
this.graphics2.endFill()
this.game.app.stage.addChild(this.graphics2)
if (this.xp >= this.hiscore) { this.hiscore = this.xp }
this.game.app.stage.addChild(this.messageField)
this.messageField.text = `GAME OVER High Score : ${this.hiscore} XP`
}
public showWin(){
this.game.winSound.play();
this.graphics2 = new PIXI.Graphics()
this.graphics2.beginFill(0xdfeded)
this.graphics2.drawRect(600, 200, 675, 375)
this.graphics2.endFill()
this.game.app.stage.addChild(this.graphics2)
if (this.xp >= this.hiscore) { this.hiscore = this.xp }
this.game.app.stage.addChild(this.messageField)
this.messageField.text = `YOU WON!! High Score : ${this.hiscore} XP`
}
public update() {
this.basicText.text = `Energypoints: ${this.hp} | XP: ${this.xp}`
if (this.hp == 0) {
this.game.gameOver();
}
}
}
Game.ts
⋮
export class Game {
⋮
private update(delta : number) {
⋮
if (this.robot1s.length == 0) {
this.ui.showWin();
}
}
gameOver(){
console.log("GAMEOVER")
this.backgroundSound.pause();
this.app.stop()
this.ui.showGameOver()
}
}
2.7 Audio
Game.ts
⋮
import damageSound from "url:../audio/impact1.wav";
import hitSound from "url:../audio/Hit_Hurt2.wav";
import swingSound from "url:../audio/Shoot5.wav";
import deathSound from "url:../audio/Explode2.wav";
import winSound from "url:../audio/Random5.wav";
import backgroundSound from "url:../audio/backgroundmusic.mp3";
⋮
export class Game {
⋮
public damageSound : HTMLAudioElement;
public hitSound : HTMLAudioElement;
public swingSound : HTMLAudioElement;
public deathSound : HTMLAudioElement;
public winSound : HTMLAudioElement;
public backgroundSound : HTMLAudioElement;
⋮
constructor() {
⋮
this.loader = new PIXI.Loader()
this.loader
⋮
.add("damageSound", damageSound)
.add("hitSound", hitSound)
.add("swingSound", swingSound)
.add("deathSound", deathSound)
.add("winSound", winSound)
.add("backgroundSound", backgroundSound);
this.loader.load(()=>this.startGame())
}
private startGame() {
⋮
this.damageSound = this.loader.resources["damageSound"].data!
this.hitSound = this.loader.resources["hitSound"].data!
this.swingSound = this.loader.resources["swingSound"].data!
this.deathSound = this.loader.resources["deathSound"].data!
this.winSound = this.loader.resources["winSound"].data!
this.backgroundSound = this.loader.resources["backgroundSound"].data!
⋮
this.backgroundSound.play();
this.backgroundSound.volume = 0.4
⋮
}
private update(delta : number) {
⋮
for (let robot1 of this.robot1s) {
robot1.update(delta)
if (this.collision(robot1, this.player)) {
if(this.player.isAttacking) {
⋮
this.hitSound.play();
} else {
⋮
this.damageSound.play();
this.damageSound.volume = 0.9;
}
}
}
⋮
}
gameOver(){
⋮
this.backgroundSound.pause();
⋮
}
⋮
}
Player.ts
⋮
export class Player extends PIXI.Sprite {
⋮
private onKeyDown(e: KeyboardEvent): void {
switch (e.key.toUpperCase()) {
⋮
case " ":
case "J":
case "ENTER":
⋮
this.game.swingSound.play();
⋮
break;
}
}
⋮
}
UI.ts
⋮
export class UI {
⋮
public showGameOver(){
this.game.deathSound.play();
⋮
}
public showWin(){
this.game.winSound.play();
⋮
}
⋮
}
3. Samenwerking
Bij het opzetten van het project heb je zorg gedragen voor overdraagbaarheid van de code. Je werkt iteratief: in korte sprints voeg jij telkens jouw bijdrage toe aan jullie game. Je werkt samen met je team in één GitHub-repository.
- [x] opzetten van het project volgens git principes
- [x] iteratief werken
- [x] individuele bijdrage
- [x] 1 github repo
3.1 Opzet Project & Repo uitleg
Het eerste plan voor dit project was om het met zijn 3en te maken. Nu hebben we het bedenken & designen van de game wel in samenwerking gedaan, maar de het maken van de code heb ik in mijn eentje gedaan. Wel heb ik de repository zo opgezet dat het in theorie wel mogelijk is om goed samen te werken. Hier volgt de uitleg van hoe mijn setup in de praktijk werkt.
Ten eerste staat het in een github team, waardoor iedereen de repositories kan beheren en het op een gezamenlijke plek staat. https://github.com/cmgt-thegame
Binnen dit team staat 2 main repos: de design & code repo. In de design repo staat de todolist, pixelart en gamedesign document. https://github.com/cmgt-thegame/cmgt_thegame-design
In de code repository staat de code voor de game. https://github.com/cmgt-thegame/cmgt_thegame-code
Binnen de code repo zijn er verschillende branches om zo samenwerking en version management optimaal te hebben.
Als eerst is er de dev
branch, dit is de nieuwste versie branch (met een kans op brekende code). Vanaf deze branch maak je een eigen branch om features of bugfixes op uit te voeren.
Vervolgens heb je de prod
branch, dit staat voor production en deze branch word gebruikt om de live versie van de game op te draaien (in dit geval github pages). Wanneer een dev versie getest is en voldoet aan de definition of done word vanaf daar gemerged naar production en heb je een nieuwe versie van de game live.
3.2 Iteratief Werken voorbeeld
Ik heb de 3 prototypes die gemaakt moesten worden voor dit buildingblock op de volgende manier aangepakt. Elke keer dat er belangrijke nieuwe mechanics worden toegevoegd, maakte ik een branch vanuit production en deze ga ik de naam: prod-v0.x.x
. Zo zijn er de volgende branches ontstaan voor de verschillende prototypes:
- prototype 1: prod-v0.1.0
- prototype 2: prod-v0.2.0
- prototype 3: prod (de laatste live versie)
https://github.com/cmgt-thegame/cmgt_thegame-code/commits/prod
Changelog:
v0.3.0
- small gameplay tweaks
- added audio
- added gameover and winscreen
- added xp & energypoint bar changing
v0.2.0
- update robot1 ai
- implement killing enemy
- added weapon
- build & turn on github pages
v0.1.0
- scrolling background tryout
- added collison detection
- added first version of enemy (robot1)
- added outerbounds
- added controls
- fixed player class reload bug
- added level & background
- added placeholder graphics
v0.0.2
- added player class
- small visual tweaks
- added multiple characters
- added UI class
v0.0.1
- groundworks of game (pixi + game class)
- initial commit
Hier een voorbeeld van een van de dev iteraties:
- Kijk vanuit de design repository op de todolist en kies een van de items. (https://github.com/cmgt-thegame/cmgt_thegame-design/blob/main/planning.md)
- Checkout naar de dev branch en pull de laatste changes
git checkout dev
&git pull
- Werk aan het item
- Commit de changes elke keer wanneer er een groot gedeelte af is (of als de werkdag over is)
- Wanneer de changes gedaan zijn, getest zijn en voldoen aan de def of done: begin het merge process naar production
- Checkout naar prod
checkout prod
- Merge dev naar prod
merge dev
- Vervolgens kan je bij grote changes een archive branch maken
git branch prod-v1.x.x
- Herhaal dit process