Browse Source

[wip checkpoint]

develop
Rob Colbert 2 years ago
parent
commit
62d1fc0e00
  1. 2
      README.md
  2. BIN
      game/assets/img/beach/butterfly-001.png
  3. BIN
      game/assets/img/beach/butterfly-002.png
  4. BIN
      game/assets/img/beach/butterfly-003.png
  5. BIN
      game/assets/img/beach/butterfly-004.png
  6. BIN
      game/assets/img/torba-level80.png
  7. 54
      game/js/game-app.js
  8. 26
      game/js/lib/game-background.js
  9. 32
      game/js/lib/game-buttterfly.js
  10. 2
      game/js/lib/game-egg-simulator.js
  11. 23
      game/js/lib/game-enemies.js
  12. 50
      game/js/lib/game-enemy-behaviors.js
  13. 27
      game/js/lib/game-enemy.js
  14. 13
      game/js/lib/game-object-behaviors.js
  15. 64
      game/js/lib/game-object.js
  16. 45
      game/js/lib/game-zoomer.js
  17. 126
      game/js/lib/object-behaviors/basic-navigator.js
  18. 50
      game/js/lib/object-behaviors/descend-to-surface.js
  19. 1
      package.json
  20. 5
      yarn.lock

2
README.md

@ -83,7 +83,7 @@ export default class GameApp extends NiceGame {
}
```
## License
# License
```
Copyright © 2022 Rob Colbert @rob@nicecrew.digital

BIN
game/assets/img/beach/butterfly-001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
game/assets/img/beach/butterfly-002.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
game/assets/img/beach/butterfly-003.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
game/assets/img/beach/butterfly-004.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
game/assets/img/torba-level80.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

54
game/js/game-app.js

@ -88,7 +88,7 @@ export default class GameApp extends NiceGame {
async onStartGame (event) {
NiceInputTools.cancelEvent(event);
this.startNewGame();
await this.startNewGame();
}
async startMusicStream (url) {
@ -97,14 +97,14 @@ export default class GameApp extends NiceGame {
this.audio.musicVolume = 0.4;
}
startNewGame ( ) {
async startNewGame ( ) {
this.mode = 'loading';
this.gameDisplay.removeChild(this.overlays.mainMenu);
this.gameDisplay.appendChild(this.overlays.gameControls);
this.startMusicStream('/dist/assets/audio/music/idra');
this.loadAudio();
await this.loadAudio();
this.player.moveSpeed = 2;
@ -121,8 +121,10 @@ export default class GameApp extends NiceGame {
startLevel ( ) {
const now = performance.now();
this.nextSpawnInterval = 1000 * 2;
this.spawnRandomEnemy();
this.nextSpawnInterval = 60000 * 2;
this.nextSpawnTime = now + this.nextSpawnInterval;
this.formations = [ ];
}
@ -175,31 +177,15 @@ export default class GameApp extends NiceGame {
}
this.oldWantsThrowEgg = wantsThrowEgg;
this.eggs.update();
this.enemies.update();
this.eggs.update(elapsed, now);
this.enemies.update(elapsed, now);
/*
* Spawn an enemy from the groyper army at scheduled intervals.
*/
if (now >= this.nextSpawnTime) {
this.nextSpawnTime += this.nextSpawnInterval;
switch (NiceMath.randomRangeInt(1, 4)) {
case 1:
this.enemies.spawnBeardson();
break;
case 2:
this.enemies.spawnGroyper();
break;
case 3:
this.enemies.spawnTorba();
break;
case 4:
this.enemies.spawnFuentesFagFace();
break;
}
this.spawnRandomEnemy();
}
/*
@ -210,6 +196,26 @@ export default class GameApp extends NiceGame {
}
}
spawnRandomEnemy ( ) {
switch (2 /*NiceMath.randomRangeInt(1, 4)*/) {
case 1:
this.enemies.spawnBeardson();
break;
case 2:
this.enemies.spawnGroyper();
break;
case 3:
this.enemies.spawnTorba();
break;
case 4:
this.enemies.spawnFuentesFagFace();
break;
}
}
renderGame (ctx) {
this.background.render(ctx);
@ -364,7 +370,7 @@ export default class GameApp extends NiceGame {
jobs.push(this.audio.loadSound('beardson-touchdown', '/dist/assets/audio/sfx/pew-pew-pew.wav'));
await Promise.all(jobs);
return Promise.all(jobs);
}
}

26
game/js/lib/game-background.js

@ -5,6 +5,9 @@
'use strict';
import { NiceColor, NiceEasing, NiceSprite, NiceTween, NiceVector2d } from 'dtp-nice-game';
import GameButterfly from './game-buttterfly.js';
import GameObject from './game-object.js';
const SKY_HEIGHT = 200;
const WATER_HEIGHT = 150;
@ -168,6 +171,11 @@ export default class GameBackground {
jobs.push(this.game.loadImage('bg:cloud-005', '/dist/assets/img/beach/cloud-005.png', 141, 50));
jobs.push(this.game.loadImage('bg:cloud-006', '/dist/assets/img/beach/cloud-006.png', 119, 50));
jobs.push(this.game.loadImage('bg:butterfly-001', '/dist/assets/img/beach/butterfly-001.png', 27, 24));
jobs.push(this.game.loadImage('bg:butterfly-002', '/dist/assets/img/beach/butterfly-002.png', 27, 24));
jobs.push(this.game.loadImage('bg:butterfly-003', '/dist/assets/img/beach/butterfly-003.png', 27, 24));
jobs.push(this.game.loadImage('bg:butterfly-004', '/dist/assets/img/beach/butterfly-004.png', 27, 24));
await Promise.all(jobs);
}
@ -384,9 +392,19 @@ export default class GameBackground {
this.tweens.treeRightIn.delay(2000).run();
this.tweens.plantLeftIn.delay(2000).run();
this.tweens.plantRightIn.delay(2000).run();
this.butterfly = new GameButterfly(
this.game,
this.game.images['bg:butterfly-002'],
new NiceVector2d(64, 120),
);
this.butterfly.navigator.setMoveSpeed(40.0);
this.butterfly.navigator.setTargetPosition(new NiceVector2d(503, 386));
break;
case 'play':
delete this.butterfly;
this.tweens.foregroundOut.run();
this.tweens.treeLeftOut.run();
this.tweens.treeRightOut.run();
@ -419,6 +437,10 @@ export default class GameBackground {
}
tween.update(elapsed, now);
}
if (this.butterfly) {
this.butterfly.update(elapsed, now);
}
}
spawnBoat ( ) {
@ -535,6 +557,10 @@ export default class GameBackground {
const sprite = this.sprites[spriteKey];
sprite.render(ctx);
}
if (this.butterfly) {
this.butterfly.render(ctx);
}
}
setEnvironmentMode (mode) {

32
game/js/lib/game-buttterfly.js

@ -0,0 +1,32 @@
// lib/game-butterfly.js
// Copyright (C) 2022 Rob Colbert @[email protected]
// License: Apache-2.0
'use strict';
import { NiceVector2d } from 'dtp-nice-game';
import GameObjectBehaviors from './game-object-behaviors.js';
import GameObject from './game-object.js';
export default class GameButterfly extends GameObject {
constructor (game, image, position) {
super(game, image, position);
this.registration = new NiceVector2d(
this.image.width / 2,
this.image.height / 2,
);
this.navigator = new GameObjectBehaviors.BasicNavigator(game, this);
this.navigator.setMoveSpeed(30.0);
this.navigator.setRotateSpeed(1.0);
this.navigator.setTerminateAtTarget(false);
this.addBehavior(this.navigator);
}
update (elapsed, now) {
super.update(elapsed, now);
}
}

2
game/js/lib/game-egg-simulator.js

@ -94,7 +94,7 @@ export default class GameEggSimulator extends NiceSprite {
/**
* Tests if an enemy (NiceSprite) collides with any egg(s) in the simulation.
* @param {GameEnemy} enemy The enemy to be tested for collision with any
* @param {GameObject} enemy The enemy to be tested for collision with any
* egg(s).
*/
collidesWith (enemy) {

23
game/js/lib/game-enemies.js

@ -8,8 +8,10 @@ const DTP_COMPONENT_NAME = 'game-enemies';
import { NiceLog, NiceColor, NiceVector2d } from 'dtp-nice-game';
import GameEnemy from './game-enemy.js';
import { DescendToSurface } from './game-enemy-behaviors.js';
import GameObject from './game-object.js';
import GameObjectBehaviors from './game-object-behaviors.js';
import GameZoomer from './game-zoomer.js';
const QUIP_PROBABILITY = 0.75;
@ -55,9 +57,9 @@ export default class GameEnemies {
return Promise.all(jobs);
}
update ( ) {
update (elapsed, now) {
const eliminatedEnemies = this.enemies.filter((enemy) => {
enemy.update();
enemy.update(elapsed, now);
if (!this.game.eggs.collidesWith(enemy)) {
return false;
}
@ -114,16 +116,23 @@ export default class GameEnemies {
spawnGroyper ( ) {
this.log.debug('spawnGroyper', 'a wild groyper approaches from the north');
this.spawnDescender(this.game.images.groyper);
this.spawnZoomer(this.game.images.groyper);
this.game.audio.playSound('enemy-spawn');
this.game.flashBorder(new NiceColor(0, 255, 0, 1.0));
}
spawnDescender (image) {
const enemy = new GameEnemy(this.game, image, this.getRandomSpawnPoint(image.width / 2));
enemy.addBehavior(new DescendToSurface(this.game, enemy, 0.25 + (Math.random() * 0.75)));
const enemy = new GameObject(this.game, image, this.getRandomSpawnPoint(image.width / 2));
enemy.addBehavior(new GameObjectBehaviors.DescendToSurface(this.game, enemy, 0.25 + (Math.random() * 0.75)));
this.enemies.push(enemy);
return enemy;
}
spawnZoomer (image) {
const enemy = new GameZoomer(this.game, image, new NiceVector2d(-image.width, 120));
this.enemies.push(enemy);
enemy.navigator.setTargetPosition(new NiceVector2d(this.game.playfield.width / 2.0, 32 + (Math.random() * 200)));
return enemy;
}

50
game/js/lib/game-enemy-behaviors.js

@ -1,50 +0,0 @@
// lib/game-enemy-behaviors.js
// Copyright (C) 2022 Rob Colbert @[email protected]
// License: Apache-2.0
'use strict';
export class DescendToSurface {
constructor (game, enemy, moveSpeed) {
this.game = game;
this.enemy = enemy;
this.moveSpeed = moveSpeed;
this.isDescending = true;
this.targetX = this.enemy.position.x;
}
update (/* elapsed, now */) {
if (this.isDescending) {
if (this.enemy.position.y < 480) {
this.enemy.position.y += this.moveSpeed;
} else {
this.enemy.position.y = 480;
this.isDescending = false;
this.game.audio.playSound('beardson-touchdown');
return false; // end of behavior
}
}
if (Math.random() > 0.998) {
this.targetX = this.game.getRandomPlayfieldX(this.enemy.image.width / 2);
}
if (this.enemy.position.x > this.targetX) {
this.enemy.position.x -= this.moveSpeed;
if (this.enemy.position.x < this.targetX) {
this.enemy.position.x = this.targetX;
}
}
if (this.enemy.position.x < this.targetX) {
this.enemy.position.x += this.moveSpeed;
if (this.enemy.position.x > this.targetX) {
this.enemy.position.x = this.targetX;
}
}
return true; // keep running
}
}

27
game/js/lib/game-enemy.js

@ -1,27 +0,0 @@
// lib/game-enemy.js
// Copyright (C) 2022 Rob Colbert @[email protected]
// License: Apache-2.0
'use strict';
import NiceSprite from 'dtp-nice-game/lib/nice-sprite.js';
export default class GameEnemy extends NiceSprite {
constructor (game, image, position) {
super(game, position);
this.image = image;
this.behaviors = [ ];
}
addBehavior (behavior) {
this.behaviors.push(behavior);
}
update (elapsed, now) {
const behavior = this.behaviors[0];
if (behavior && !behavior.update(elapsed, now)) {
this.behaviors.shift();
}
}
}

13
game/js/lib/game-object-behaviors.js

@ -0,0 +1,13 @@
// lib/game-object-behaviors.js
// Copyright (C) 2022 Rob Colbert @[email protected]
// License: Apache-2.0
'use strict';
import { DescendToSurface } from "./object-behaviors/descend-to-surface.js";
import { BasicNavigator } from "./object-behaviors/basic-navigator.js";
export default {
DescendToSurface,
BasicNavigator,
};

64
game/js/lib/game-object.js

@ -0,0 +1,64 @@
// lib/game-object.js
// Copyright (C) 2022 Rob Colbert @[email protected]
// License: Apache-2.0
'use strict';
import NiceSprite from 'dtp-nice-game/lib/nice-sprite.js';
export default class GameObject extends NiceSprite {
constructor (game, image, position) {
super(game, position);
this.image = image;
this.behaviors = [ ];
}
addBehavior (behavior) {
this.behaviors.push(behavior);
}
update (elapsed, now) {
const behavior = this.behaviors[0];
if (behavior && !behavior.update(elapsed, now)) {
this.behaviors.shift();
}
}
render (ctx) {
super.render(ctx);
if (this.navigator) {
ctx.beginPath();
ctx.fillStyle = '#ff0000';
ctx.ellipse(
this.navigator.targetPosition.x,
this.navigator.targetPosition.y,
8,
8,
0,
0,
Math.PI * 2,
true
);
ctx.fill();
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(
this.position.x + (Math.cos(this.navigator.desiredRotation) * this.navigator.moveSpeed),
this.position.y + (Math.sin(this.navigator.desiredRotation) * this.navigator.moveSpeed)
);
ctx.strokeStyle = '#ff0000';
ctx.lineWidth = 2.0;
ctx.stroke();
const oldTextAlign = ctx.textAlign;
ctx.textAlign = 'center';
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 12px Larabie';
ctx.fillText(`${this.navigator.desiredRotation}`, this.position.x, this.position.y);
ctx.textAlign = oldTextAlign;
}
}
}

45
game/js/lib/game-zoomer.js

@ -0,0 +1,45 @@
// lib/game-zoomer.js
// Copyright (C) 2022 Rob Colbert @[email protected]
// License: Apache-2.0
'use strict';
import { NiceVector2d } from 'dtp-nice-game';
import GameObjectBehaviors from './game-object-behaviors.js';
import GameObject from './game-object.js';
export default class GameZoomer extends GameObject {
constructor (game, image, position) {
super(game, image, position);
this.registration = new NiceVector2d(
this.image.width / 2,
this.image.height / 2,
);
this.navigator = new GameObjectBehaviors.BasicNavigator(game, this);
this.navigator.setMoveSpeed(120.0);
this.navigator.setRotateSpeed(5);
this.navigator.setTerminateAtTarget(false);
this.addBehavior(this.navigator);
this.chooseNewTarget();
}
chooseNewTarget ( ) {
this.navigator.setTargetPosition(new NiceVector2d(
this.game.getRandomPlayfieldX(this.image.width / 2.0),
32 + (Math.random() * 200),
));
}
update (elapsed, now) {
super.update(elapsed, now);
if (this.navigator.idle) {
this.chooseNewTarget();
}
}
}

126
game/js/lib/object-behaviors/basic-navigator.js

@ -0,0 +1,126 @@
// lib/behaviors/basic-navigator.js
// Copyright (C) 2022 Rob Colbert @[email protected]
// License: Apache-2.0
'use strict';
import { NiceMath } from "dtp-nice-game";
export class BasicNavigator {
constructor (game, gameObject) {
this.game = game;
this.gameObject = gameObject;
this.moveSpeed = 2;
this.rotateSpeed = 0.01;
this.targetPosition = gameObject.position.clone();
this.terminateAtTarget = true;
this.desiredRotation = 0.0;
this.idle = false;
}
/**
* Sets the move speed of the gameObject.
* @param {Number} moveSpeed The speed at which the gameObject will be moved
* in units per second.
*/
setMoveSpeed (moveSpeed) {
this.moveSpeed = moveSpeed;
}
/**
* Sets the rotation speed of the character in radians per second.
* @param {Number} rotateSpeed The rotation speed of the gameObject in radians
* per second.
*/
setRotateSpeed (rotateSpeed) {
this.rotateSpeed = rotateSpeed;
}
/**
* Sets the destination/target to which the navigator will navigate.
* @param {NiceVector2d} targetPosition The position to which the sprite
* should navigate
*/
setTargetPosition (targetPosition) {
this.targetPosition = targetPosition;
this.idle = false;
}
/**
* Indicates whether the behavior should terminate when the gameObject reaches
* the target position or remain active but idle.
* @param {Boolean} shouldTerminate true when the behavior should terminate
* when it reaches the target position or false if the behavior should persist
* and remain idle.
* @returns this for method call chaining
*/
setTerminateAtTarget (shouldTerminate = true) {
this.terminateAtTarget = shouldTerminate;
}
/**
* Orients the gameObject sprite towards the target position and takes one
* step along the gameObject sprite's current forward vector (rotation). If
* at the target position, will idle or terminate as configured.
* @param {Number} elapsed The number of milliseconds that have elapsed since
* the last call
* @returns true to let the behavior keep running, false to end the behavior.
*/
update (elapsed/*, now */) {
const TIME_SCALE = elapsed / 1000.0;
const MOVE_AMOUNT = this.moveSpeed * TIME_SCALE;
const TURN_AMOUNT = this.rotateSpeed * TIME_SCALE;
const AT_DESTINATION = this.gameObject.position.equals(this.targetPosition);
/*
* Orient and move towards the target position
*/
// set our desired rotation to point at the target position
if (!AT_DESTINATION) {
this.desiredRotation = this.gameObject.position.angleTo(this.targetPosition);
}
// update gameObject rotation towards the desired rotation
if (this.gameObject.rotation !== this.desiredRotation) {
const ldist = Math.abs(this.gameObject.rotation - this.desiredRotation);
const rdist = Math.abs(this.desiredRotation - this.gameObject.rotation);
if (ldist <= rdist) {
this.gameObject.rotation += TURN_AMOUNT;
} else {
this.gameObject.rotation -= TURN_AMOUNT;
}
if (NiceMath.isAngleNear(this.gameObject.rotation, this.desiredRotation, TURN_AMOUNT)) {
this.gameObject.rotation = this.desiredRotation;
}
}
if (AT_DESTINATION) {
return !this.terminateAtTarget; // terminate or be idle at the target position
}
/*
* take this frame's step towards the target position in the direction of
* the gameObject's current rotation
*/
let deltaX = Math.cos(this.gameObject.rotation) * this.moveSpeed * TIME_SCALE;
let deltaY = Math.sin(this.gameObject.rotation) * this.moveSpeed * TIME_SCALE;
this.gameObject.position.x += deltaX;
this.gameObject.position.y += deltaY;
if (this.gameObject.position.isNear(this.targetPosition, MOVE_AMOUNT)) {
this.gameObject.position = this.targetPosition.clone();
this.desiredRotation = 0;
this.idle = true;
}
return true;
}
}

50
game/js/lib/object-behaviors/descend-to-surface.js

@ -0,0 +1,50 @@
// lib/behaviors/descend-to-surface.js
// Copyright (C) 2022 Rob Colbert @[email protected]
// License: Apache-2.0
'use strict';
export class DescendToSurface {
constructor (game, gameObject, moveSpeed) {
this.game = game;
this.gameObject = gameObject;
this.moveSpeed = moveSpeed;
this.isDescending = true;
this.targetX = this.gameObject.position.x;
}
update (/* elapsed, now */) {
if (this.isDescending) {
if (this.gameObject.position.y < 480) {
this.gameObject.position.y += this.moveSpeed;
} else {
this.gameObject.position.y = 480;
this.isDescending = false;
this.game.audio.playSound('beardson-touchdown');
return false; // end of behavior
}
}
if (Math.random() > 0.998) {
this.targetX = this.game.getRandomPlayfieldX(this.gameObject.image.width / 2);
}
if (this.gameObject.position.x > this.targetX) {
this.gameObject.position.x -= this.moveSpeed;
if (this.gameObject.position.x < this.targetX) {
this.gameObject.position.x = this.targetX;
}
}
if (this.gameObject.position.x < this.targetX) {
this.gameObject.position.x += this.moveSpeed;
if (this.gameObject.position.x > this.targetX) {
this.gameObject.position.x = this.targetX;
}
}
return true; // keep running
}
}

1
package.json

@ -21,6 +21,7 @@
"dtp-nice-game": "https://git.digitaltelepresence.com/digital-telepresence/dtp-nice-game.git",
"express": "^4.17.3",
"node-fetch": "^3.2.3",
"numeral": "^2.0.6",
"pug": "^3.0.2"
},
"devDependencies": {

5
yarn.lock

@ -1228,6 +1228,11 @@ npm-run-path@^4.0.1:
dependencies:
path-key "^3.0.0"
numeral@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506"
integrity sha1-StCAk21EPCVhrtnyGX7//iX05QY=
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"

Loading…
Cancel
Save