Browse Source

Background composer engine

develop
Rob Colbert 2 years ago
parent
commit
bdb3b6cb73
  1. BIN
      game/assets/img/beach/foreground-base.png
  2. BIN
      game/assets/img/beach/plant-left.png
  3. BIN
      game/assets/img/beach/plant-right.png
  4. BIN
      game/assets/img/beach/tree-left.png
  5. BIN
      game/assets/img/beach/tree-right.png
  6. BIN
      game/assets/img/beach/waves.png
  7. 109
      game/js/game-app.js
  8. 319
      game/js/lib/game-background.js
  9. 6
      game/js/lib/game-egg-simulator.js
  10. 62
      game/js/lib/game-enemies.js
  11. 12
      game/js/lib/game-enemy-beardson.js
  12. 46
      game/js/lib/game-enemy-groyper.js
  13. 46
      game/js/lib/game-enemy-torba.js
  14. 16
      game/js/lib/game-enemy.js
  15. 23
      game/js/lib/game-player.js
  16. 8
      game/less/lib/button.less
  17. 2
      game/less/lib/variables.less
  18. 246
      src/images/beach-foreground.svg
  19. 6468
      src/images/beach-plant-001.svg
  20. 417
      src/images/beach-plant-002.svg
  21. 1485
      src/images/beach-plant-003.svg
  22. 62
      src/images/beach-waves.svg
  23. 561
      src/images/palm-tree-001.svg
  24. 474
      src/images/palm-tree-002.svg

BIN
game/assets/img/beach/foreground-base.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
game/assets/img/beach/plant-left.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
game/assets/img/beach/plant-right.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
game/assets/img/beach/tree-left.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
game/assets/img/beach/tree-right.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
game/assets/img/beach/waves.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

109
game/js/game-app.js

@ -7,7 +7,13 @@
const DTP_COMPONENT_NAME = 'game-app';
import '../less/style.less';
import { NiceGame, NiceSprite, NiceVector2d, NiceEasing } from 'dtp-nice-game';
import {
NiceColor,
NiceEasing,
NiceGame,
NiceMath,
NiceVector2d,
} from 'dtp-nice-game';
import GameBackground from './lib/game-background.js';
import GamePlayer from './lib/game-player.js';
@ -28,6 +34,18 @@ export default class GameApp extends NiceGame {
this.mode = 'loading';
this.eggs = [ ];
this.playfield = {
width: 960,
height: 540,
margin: 120,
colors: {
margin: {
fill: new NiceColor(0, 0, 0, 0.6),
stroke: new NiceColor(32,32,64, 1.0),
},
},
};
}
async run ( ) {
@ -54,6 +72,7 @@ export default class GameApp extends NiceGame {
await this.loadGameAssets();
this.mode = 'menu';
this.background.setMode('story');
this.startUpdateLoop();
}
@ -72,15 +91,10 @@ export default class GameApp extends NiceGame {
this.oldWantsThrowEgg = false;
this.mode = 'game';
this.level = 1;
this.startLevel();
const tween = this
.createTween(this.torba.position, { x: -50, y: 32 }, { x: 920, y: 32 }, NiceEasing.Bounce.Out)
.delay(1000.0)
.duration(2000.0);
tween.run();
this.background.setMode('play');
}
async startMusicStream (url) {
@ -97,6 +111,8 @@ export default class GameApp extends NiceGame {
}
onGameUpdate (elapsed, now) {
this.background.update(elapsed, now);
switch (this.mode) {
case 'loading':
break;
@ -121,13 +137,12 @@ export default class GameApp extends NiceGame {
updateMenu (/* elapsed, now */) { }
renderMenu (ctx) {
this.background.draw(ctx, 0, 0);
this.background.render(ctx);
this.tex.render(ctx);
}
updateGame (elapsed, now) {
this.background.update();
this.tex.update();
this.tex.update(elapsed, now);
/*
* Use this.oldWantsThrowEgg to gate button presses and only throw the
@ -148,7 +163,17 @@ export default class GameApp extends NiceGame {
*/
if (now >= this.nextSpawnTime) {
this.nextSpawnTime += this.nextSpawnInterval;
this.enemies.spawnBeardson();
switch (NiceMath.randomRangeInt(1, 3)) {
case 1:
this.enemies.spawnBeardson();
break;
case 2:
this.enemies.spawnTorba();
break;
case 3:
this.enemies.spawnGroyper();
break;
}
}
/*
@ -160,18 +185,35 @@ export default class GameApp extends NiceGame {
}
renderGame (ctx) {
this.background.draw(ctx, 0, 0);
this.background.render(ctx);
this.enemies.render(ctx);
for (const formation of this.formations) {
formation.render(ctx);
}
this.eggs.render(ctx);
this.renderPlayfield(ctx);
this.eggs.render(ctx);
this.tex.render(ctx);
}
this.torba.render(ctx);
renderPlayfield (ctx) {
ctx.fillStyle = this.playfield.colors.margin.fill.toCssString();
ctx.fillRect(0, 0, this.playfield.margin, this.playfield.height);
ctx.fillRect(this.playfield.width - this.playfield.margin, 0, this.playfield.margin, this.playfield.height);
ctx.beginPath();
ctx.moveTo(this.playfield.margin, 0);
ctx.lineTo(this.playfield.margin, this.playfield.height);
ctx.moveTo(this.playfield.width - this.playfield.margin, 0);
ctx.lineTo(this.playfield.width - this.playfield.margin, this.playfield.height);
ctx.strokeStyle = this.playfield.colors.margin.stroke.toCssString();
ctx.lineWidth = 3.0;
ctx.stroke();
}
throwEgg ( ) {
@ -179,7 +221,7 @@ export default class GameApp extends NiceGame {
this.log.info('throwEgg', 'throwing egg');
this.eggs.throwEgg(new NiceVector2d(
this.tex.position.x,
this.tex.position.y - 16,
this.tex.position.y - 48,
), moveSpeed);
this.audio.playSound('throw-egg-001');
@ -194,12 +236,10 @@ export default class GameApp extends NiceGame {
async loadGameAssets ( ) {
const jobs = [ ];
this.background = new GameBackground(960, 540);
jobs.push(this.background.load('/dist/assets/img/bg-001.jpg'));
this.background = new GameBackground(this);
jobs.push(this.background.load());
this.tex = new GamePlayer(this);
this.tex.position.x = 480;
this.tex.position.y = 460;
jobs.push(this.tex.load());
this.eggs = new GameEggSimulator(this);
@ -208,8 +248,8 @@ export default class GameApp extends NiceGame {
this.enemies = new GameEnemies(this);
jobs.push(this.enemies.load());
this.torba = new NiceSprite(this, new NiceVector2d(480, 20));
jobs.push(this.torba.load('/dist/assets/img/torba.png', 52, 64));
jobs.push(this.loadImage('groyper', '/dist/assets/img/groyper.png', 64, 64));
jobs.push(this.loadImage('torba', '/dist/assets/img/torba.png', 52, 64));
await Promise.all(jobs);
}
@ -236,6 +276,33 @@ export default class GameApp extends NiceGame {
await Promise.all(jobs);
}
flashBorder (color) {
this
.createTween(
this.playfield.colors.margin.stroke,
{
r: color.r,
g: color.g,
b: color.b,
a: color.a,
},
{
r: this.playfield.colors.margin.stroke.r,
g: this.playfield.colors.margin.stroke.g,
b: this.playfield.colors.margin.stroke.b,
a: this.playfield.colors.margin.stroke.a,
},
NiceEasing.Bounce.InOut,
)
.duration(1000)
.run()
; // end createTween
}
getRandomPlayfieldX (padding) {
return this.playfield.margin + padding + ((this.playfield.width - padding - (this.playfield.margin * 2)) * Math.random());
}
}
window.addEventListener('load', async ( ) => {

319
game/js/lib/game-background.js

@ -4,38 +4,323 @@
'use strict';
import NiceImage from 'dtp-nice-game/lib/nice-image.js';
import { NiceEasing, NiceSprite, NiceTween, NiceVector2d } from 'dtp-nice-game';
const CANVAS_WIDTH = 960;
const CANVAS_HEIGHT = 540;
const SKY_HEIGHT = 150;
const WATER_HEIGHT = 150;
/**
* GameBackground provides an image to use as the backdrop, an update method for
* updating animated and other dynamic components, and a draw method, which can
* all be implemented to provide all sorts of dynamic effects on the background.
* GameBackground is responsible for rendering the background/environment, and
* it uses a variety of techniques to do that. Gradients are used to render the
* sky, beach, and ocean. Images are used to render the trees, plants, and
* foreground base.
*
* ...which just happens to be a NiceImage.
* GameBackground has modes, and can reconfigure itself and animate between
* them on command using reusable NiceTween instances. It currently supports
* GameBackground.MODE_STORY and GameBackground.MODE_PLAY.
*/
export default class GameBackground extends NiceImage {
export default class GameBackground {
static get MODE_STORY ( ) { return 'story'; }
static get MODE_PLAY ( ) { return 'play'; }
constructor (game) {
super();
this.game = game;
this.sprites = { };
this.tweens = { };
this.sun = {
position: new NiceVector2d(480, 140),
};
}
async load ( ) {
await this.loadImages();
this.buildSprites();
this.buildTweens();
}
async loadImages ( ) {
const jobs = [ ];
jobs.push(this.game.loadImage('bg:foreground-base', '/dist/assets/img/beach/foreground-base.png', 960, 114));
jobs.push(this.game.loadImage('bg:waves', '/dist/assets/img/beach/waves.png', 112, 40));
jobs.push(this.game.loadImage('bg:tree-left', '/dist/assets/img/beach/tree-left.png', 361, 540));
jobs.push(this.game.loadImage('bg:tree-right', '/dist/assets/img/beach/tree-right.png', 351, 540));
jobs.push(this.game.loadImage('bg:plant-left', '/dist/assets/img/beach/plant-left.png', 148, 303));
jobs.push(this.game.loadImage('bg:plant-right', '/dist/assets/img/beach/plant-right.png', 170, 211));
await Promise.all(jobs);
}
update ( ) {
buildSprites ( ) {
this.sprites.foreground = new NiceSprite(this.game, new NiceVector2d(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
this.sprites.foreground.image = this.game.images['bg:foreground-base'];
this.sprites.foreground.registration = new NiceVector2d(480, this.sprites.foreground.image.height);
this.sprites.waves1 = new NiceSprite(this.game, new NiceVector2d(CANVAS_WIDTH * 0.8, SKY_HEIGHT + 32));
this.sprites.waves1.image = this.game.images['bg:waves'];
this.sprites.waves1.registration = new NiceVector2d(
this.sprites.waves1.image.width / 2,
this.sprites.waves1.image.height / 2,
);
this.sprites.waves2 = new NiceSprite(this.game, new NiceVector2d(CANVAS_WIDTH * 0.3, SKY_HEIGHT + 48));
this.sprites.waves2.image = this.game.images['bg:waves'];
this.sprites.waves2.registration = new NiceVector2d(
this.sprites.waves2.image.width / 2,
this.sprites.waves2.image.height / 2,
);
this.sprites.treeLeft = new NiceSprite(this.game, new NiceVector2d(0, CANVAS_HEIGHT));
this.sprites.treeLeft.image = this.game.images['bg:tree-left'];
this.sprites.treeLeft.registration = new NiceVector2d(0, this.sprites.treeLeft.image.height);
this.sprites.treeRight = new NiceSprite(this.game, new NiceVector2d(CANVAS_WIDTH, 0));
this.sprites.treeRight.image = this.game.images['bg:tree-right'];
this.sprites.treeRight.registration = new NiceVector2d(this.sprites.treeRight.image.width, 0);
this.sprites.plantLeft = new NiceSprite(this.game, new NiceVector2d(0, CANVAS_HEIGHT));
this.sprites.plantLeft.image = this.game.images['bg:plant-left'];
this.sprites.plantLeft.registration = new NiceVector2d(0, this.sprites.plantLeft.image.height);
this.sprites.plantRight = new NiceSprite(this.game, new NiceVector2d(CANVAS_WIDTH, CANVAS_HEIGHT));
this.sprites.plantRight.image = this.game.images['bg:plant-right'];
this.sprites.plantRight.registration = new NiceVector2d(
this.sprites.plantRight.image.width,
this.sprites.plantRight.image.height,
);
}
buildTweens ( ) {
const FOREGROUND_DURATION = 1000;
const TREE_DURATION = 1250;
const PLANT_DURATION = 1500;
/*
* TODO: dynamic backgrounds that can animate on their own
* Yes, this is being called now. It just does nothing for now.
* FOREGROUND BASE
*/
this.tweens.foregroundIn = new NiceTween(
this.game,
this.sprites.foreground.position,
{ y: this.game.playfield.height + this.sprites.foreground.image.height},
{ y: this.game.playfield.height },
NiceEasing.Quadratic.Out,
);
this.tweens.foregroundIn.duration(FOREGROUND_DURATION);
this.tweens.foregroundOut = new NiceTween(
this.game,
this.sprites.foreground.position,
{ y: this.game.playfield.height },
{ y: this.game.playfield.height + this.sprites.foreground.image.height},
NiceEasing.Quadratic.In,
);
this.tweens.foregroundOut.duration(FOREGROUND_DURATION);
/*
* TREE LEFT
*/
this.tweens.treeLeftIn = new NiceTween(
this.game,
this.sprites.treeLeft.position,
{ x: -this.sprites.treeLeft.image.width },
{ x: 0 },
NiceEasing.Elastic.Out,
);
this.tweens.treeLeftIn.duration(TREE_DURATION);
this.tweens.treeLeftOut = new NiceTween(
this.game,
this.sprites.treeLeft.position,
{ x: 0 },
{ x: -this.sprites.treeLeft.image.width },
NiceEasing.Elastic.In,
);
this.tweens.treeLeftOut.duration(TREE_DURATION);
/*
* TREE RIGHT
*/
this.tweens.treeRightIn = new NiceTween(
this.game,
this.sprites.treeRight.position,
{ x: this.game.playfield.width + this.sprites.treeRight.image.width },
{ x: this.game.playfield.width },
NiceEasing.Elastic.Out,
);
this.tweens.treeRightIn.duration(TREE_DURATION);
this.tweens.treeRightOut = new NiceTween(
this.game,
this.sprites.treeRight.position,
{ x: this.game.playfield.width },
{ x: this.game.playfield.width + this.sprites.treeRight.image.width },
NiceEasing.Elastic.In,
);
this.tweens.treeRightOut.duration(TREE_DURATION);
/*
* PLANT LEFT
*/
this.tweens.plantLeftIn = new NiceTween(
this.game,
this.sprites.plantLeft.position,
{ x: -this.sprites.plantLeft.image.width },
{ x: 0 },
NiceEasing.Elastic.Out,
);
this.tweens.plantLeftIn.duration(PLANT_DURATION);
this.tweens.plantLeftOut = new NiceTween(
this.game,
this.sprites.plantLeft.position,
{ x: 0 },
{ x: -this.sprites.plantLeft.image.width },
NiceEasing.Elastic.In,
);
this.tweens.plantLeftOut.duration(PLANT_DURATION);
/*
* PLANT RIGHT
*/
this.tweens.plantRightIn = new NiceTween(
this.game,
this.sprites.plantRight.position,
{ x: this.game.playfield.width + this.sprites.plantRight.width },
{ x: this.game.playfield.width },
NiceEasing.Elastic.Out,
);
this.tweens.plantRightIn.duration(PLANT_DURATION);
this.tweens.plantRightOut = new NiceTween(
this.game,
this.sprites.plantRight.position,
{ x: this.game.playfield.width },
{ x: this.game.playfield.width + this.sprites.plantRight.width },
NiceEasing.Elastic.In,
);
this.tweens.plantRightOut.duration(PLANT_DURATION);
/*
* WAVES
*/
this.tweens.waves1 = this.game
.createTween(
this.sprites.waves1,
{ rotation: (Math.PI * 0.1) },
{ rotation: -(Math.PI * 0.1) },
)
.duration(3000)
.loop()
.run();
}
setMode (mode) {
this.mode = mode;
switch (this.mode) {
case 'story':
this.tweens.foregroundIn.run();
this.tweens.treeLeftIn.run();
this.tweens.treeRightIn.run();
this.tweens.plantLeftIn.run();
this.tweens.plantRightIn.run();
break;
case 'play':
this.tweens.foregroundOut.run();
this.tweens.treeLeftOut.run();
this.tweens.treeRightOut.run();
this.tweens.plantLeftOut.run();
this.tweens.plantRightOut.run();
break;
}
}
draw (ctx, x, y) {
super.draw(ctx, x, y);
update (elapsed, now) {
for (const tweenKey in this.tweens) {
const tween = this.tweens[tweenKey];
if (tween.state !== 'running') {
continue;
}
tween.update(elapsed, now);
}
}
render (ctx) {
/*
* Free to render anything we want over that here, and I will...
*
* Yes, this is being called now. I'm just not adding anything, yet.
*
* Think rain, snowflakes, moving water, clouds, and maybe "live events"
* Gradient Fills
*/
if (!this.skyGradient) {
this.skyGradient = ctx.createLinearGradient(0, 0, 0, SKY_HEIGHT);
this.skyGradient.addColorStop(0.0, '#bdd5b3');
this.skyGradient.addColorStop(1.0, '#f0f2dc');
}
ctx.fillStyle = this.skyGradient;
ctx.fillRect(0, 0, CANVAS_WIDTH, SKY_HEIGHT);
if (!this.beachGradient) {
this.beachGradient = ctx.createLinearGradient(0, SKY_HEIGHT, 0, CANVAS_HEIGHT);
this.beachGradient.addColorStop(0.0, '#e0a47f');
this.beachGradient.addColorStop(1.0, '#f6eab6');
}
ctx.fillStyle = this.beachGradient;
ctx.fillRect(0, SKY_HEIGHT, CANVAS_WIDTH, CANVAS_HEIGHT - SKY_HEIGHT);
this.drawSun(ctx);
if (!this.oceanGradient) {
this.oceanGradient = ctx.createLinearGradient(0, SKY_HEIGHT, 0, SKY_HEIGHT + WATER_HEIGHT);
this.oceanGradient.addColorStop(0.0, '#72b5ae');
this.oceanGradient.addColorStop(1.0, '#edeec800');
}
ctx.fillStyle = this.oceanGradient;
ctx.fillRect(0, SKY_HEIGHT, CANVAS_WIDTH, WATER_HEIGHT);
/*
* Image/sprite overlays
*/
for (const spriteKey in this.sprites) {
const sprite = this.sprites[spriteKey];
sprite.render(ctx);
}
}
drawSun (ctx) {
ctx.beginPath();
ctx.ellipse(
this.sun.position.x,
this.sun.position.y,
32,
32,
0,
0,
Math.PI * 2,
);
if (!this.sun.gradient) {
this.sun.gradient = ctx.createRadialGradient(
this.sun.position.x,
this.sun.position.y,
32,
this.sun.position.x + 16,
this.sun.position.y - 16,
8
);
this.sun.gradient.addColorStop(0.0, '#e0a579');
this.sun.gradient.addColorStop(1.0, '#eabf8a');
}
ctx.fillStyle = this.sun.gradient;
ctx.lineWidth = 3.0;
ctx.strokeStyle = '#593117';
ctx.fill();
ctx.stroke();
}
}

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

@ -4,6 +4,7 @@
'use strict';
import { NiceVector2d } from 'dtp-nice-game';
import NiceImage from 'dtp-nice-game/lib/nice-image.js';
import NiceSprite from 'dtp-nice-game/lib/nice-sprite.js';
@ -56,8 +57,11 @@ export default class GameEggSimulator extends NiceSprite {
const egg = new NiceSprite(this.game, position);
egg.image = this.eggImages[index];
egg.registration = new NiceVector2d(egg.image.width / 2, egg.image.height / 2);
egg.moveSpeed = moveSpeed;
egg.rotationSpeed = Math.random() * 0.2;
egg.rotationSpeed = Math.random() * 0.4;
if (Math.random() > 0.5) {
egg.rotationSpeed = -egg.rotationSpeed;
}

62
game/js/lib/game-enemies.js

@ -4,26 +4,32 @@
'use strict';
import NiceImage from 'dtp-nice-game/lib/nice-image.js';
import NiceSprite from 'dtp-nice-game/lib/nice-sprite.js';
const DTP_COMPONENT_NAME = 'game-enemies';
import { NiceLog, NiceImage, NiceColor, NiceEasing } from 'dtp-nice-game';
import GameEnemyGroyper from './game-enemy-groyper.js';
import GameEnemyBeardson from './game-enemy-beardson.js';
import GameEnemyTorba from './game-enemy-torba.js';
/**
* GameEnemies manages the instances of "enemny" characters in play. They are
* spawned into this object, which then performs their "AI" during each frame's
* update processing.
*/
export default class GameEnemies extends NiceSprite {
export default class GameEnemies {
constructor (game) {
super();
this.log = new NiceLog(DTP_COMPONENT_NAME);
this.log.enable();
this.game = game;
this.moveSpeed = 1.0;
this.enemyImages = { };
this.enemies = [ ];
this.tweens = { };
}
async load ( ) {
@ -72,11 +78,11 @@ export default class GameEnemies extends NiceSprite {
}
spawnBeardson ( ) {
this.game.log.debug('spawnBeardson', 'a wild Beardson approaches from the north');
this.log.debug('spawnBeardson', 'a wild Beardson approaches from the north');
const beardson = new GameEnemyBeardson(
this.game,
this.enemyImages.beardson,
this.getRandomSpawnPoint(),
this.getRandomSpawnPoint(this.enemyImages.beardson.width / 2),
{
x: this.moveSpeed * (1 + (Math.random() * 3)),
y: this.moveSpeed * (0.25 + (Math.random() * 0.75)),
@ -84,11 +90,49 @@ export default class GameEnemies extends NiceSprite {
);
this.enemies.push(beardson);
this.game.audio.playSound('enemy-spawn');
this.game.flashBorder(new NiceColor(255, 0, 0, 1.0));
}
spawnTorba ( ) {
this.log.debug('spawnTorba', 'a wild Torba approaches from the north');
const torba = new GameEnemyTorba(
this.game,
this.game.images.torba,
this.getRandomSpawnPoint(this.game.images.torba.width / 2),
{
x: this.moveSpeed * (1 + (Math.random() * 3)),
y: this.moveSpeed * (0.25 + (Math.random() * 0.75)),
},
);
this.enemies.push(torba);
this.game.audio.playSound('enemy-spawn');
this.game.flashBorder(new NiceColor(255, 255, 0, 1.0));
}
spawnGroyper ( ) {
this.log.debug('spawnGroyper', 'a wild groyper approaches from the north');
const groyper = new GameEnemyGroyper(
this.game,
this.game.images.groyper,
this.getRandomSpawnPoint(this.game.images.groyper.width / 2),
{
x: this.moveSpeed * (1 + (Math.random() * 3)),
y: this.moveSpeed * (0.25 + (Math.random() * 0.75)),
},
);
this.enemies.push(groyper);
this.game.audio.playSound('enemy-spawn');
this.game.flashBorder(new NiceColor(0, 255, 0, 1.0));
}
getRandomSpawnPoint ( ) {
const x = 80 + (800 * Math.random());
return { x, y: 32 };
getRandomSpawnPoint (padding = 0) {
return {
x: this.game.getRandomPlayfieldX(padding),
y: 32,
};
}
removeEnemy (enemy) {

12
game/js/lib/game-enemy-beardson.js

@ -4,24 +4,24 @@
'use strict';
import NiceSprite from 'dtp-nice-game/lib/nice-sprite.js';
import GameEnemy from './game-enemy.js';
/**
* Implements a Beardson. The Beardson moves toward the bottom of the game play
* area. When he reaches the bottom, he pulls out a rainbow-colored dildo and
* chases Tex. When he catches Tex, Tex loses a life.
*/
export default class GameEnemyBeardson extends NiceSprite {
export default class GameEnemyBeardson extends GameEnemy {
constructor (game, image, position, moveSpeed) {
super(game, position);
this.image = image;
this.game = game;
super(game, image, position);
this.targetX = position.x;
this.moveSpeed = {
x: moveSpeed.x,
y: moveSpeed.y,
};
this.isDescending = true;
}
@ -37,7 +37,7 @@ export default class GameEnemyBeardson extends NiceSprite {
}
if (Math.random() > 0.998) {
this.targetX = 80 + (Math.floor(800 * Math.random()));
this.targetX = this.game.getRandomPlayfieldX();
}
if (this.position.x > this.targetX) {

46
game/js/lib/game-enemy-groyper.js

@ -0,0 +1,46 @@
// lib/game-enemy-groyper.js
// Copyright (C) 2022 Rob Colbert @[email protected]
// License: Apache-2.0
'use strict';
import GameEnemy from './game-enemy.js';
export default class GameEnemyGroyper extends GameEnemy {
constructor (game, image, position, moveSpeed) {
super(game, image, position);
this.targetX = position.x;
this.moveSpeed = {
x: moveSpeed.x,
y: moveSpeed.y,
};
this.isDescending = true;
}
update ( ) {
if (this.isDescending) {
if (this.position.y < 480) {
this.position.y += this.moveSpeed.y;
} else {
this.position.y = 480;
this.isDescending = false;
this.game.audio.playSound('beardson-touchdown');
}
}
if (Math.random() > 0.998) {
this.targetX = this.game.getRandomPlayfieldX();
}
if (this.position.x > this.targetX) {
this.position.x -= this.moveSpeed.x;
}
if (this.position.x < this.targetX) {
this.position.x += this.moveSpeed.x;
}
}
}

46
game/js/lib/game-enemy-torba.js

@ -0,0 +1,46 @@
// lib/game-enemy-torba.js
// Copyright (C) 2022 Rob Colbert @[email protected]
// License: Apache-2.0
'use strict';
import GameEnemy from './game-enemy.js';
export default class GameEnemyTorba extends GameEnemy {
constructor (game, image, position, moveSpeed) {
super(game, image, position);
this.targetX = position.x;
this.moveSpeed = {
x: moveSpeed.x,
y: moveSpeed.y,
};
this.isDescending = true;
}
update ( ) {
if (this.isDescending) {
if (this.position.y < 480) {
this.position.y += this.moveSpeed.y;
} else {
this.position.y = 480;
this.isDescending = false;
this.game.audio.playSound('beardson-touchdown');
}
}
if (Math.random() > 0.998) {
this.targetX = this.game.getRandomPlayfieldX();
}
if (this.position.x > this.targetX) {
this.position.x -= this.moveSpeed.x;
}
if (this.position.x < this.targetX) {
this.position.x += this.moveSpeed.x;
}
}
}

16
game/js/lib/game-enemy.js

@ -0,0 +1,16 @@
// 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.game = game;
}
}

23
game/js/lib/game-player.js

@ -4,8 +4,15 @@
'use strict';
import { NiceVector2d } from 'dtp-nice-game';
import NiceSprite from 'dtp-nice-game/lib/nice-sprite.js';
const CANVAS_WIDTH = 960;
const CANVAS_HEIGHT = 540;
const CELL_WIDTH = 82;
const CELL_HEIGHT = 128;
/**
* GamePlayer is a simple 2D NiceSprite-based object that knows how to consider
* player input during an update loop to move around and do some things.
@ -13,20 +20,26 @@ import NiceSprite from 'dtp-nice-game/lib/nice-sprite.js';
export default class GamePlayer extends NiceSprite {
constructor (game) {
super();
super(game, new NiceVector2d(CANVAS_WIDTH / 2, CANVAS_HEIGHT - 20));
this.game = game;
this.moveSpeed = 2;
this.moveSpeed = 4;
this.registration = new NiceVector2d(CELL_WIDTH / 2, CELL_HEIGHT);
this.margin = CELL_WIDTH / 2;
}
async load ( ) { return super.load('/dist/assets/img/big-baja-tex.png', 82, 128); }
async load ( ) { return super.load('/dist/assets/img/big-baja-tex.png', CELL_WIDTH, CELL_HEIGHT); }
update ( ) {
const { input } = this.game;
if (input.buttons.moveLeft.isPressed || input.keys.moveLeft.isPressed) {
this.position.x -= this.moveSpeed;
if (this.position.x > (this.game.playfield.margin + this.margin)) {
this.position.x -= this.moveSpeed;
}
}
if (input.buttons.moveRight.isPressed || input.keys.moveRight.isPressed) {
this.position.x += this.moveSpeed;
if (this.position.x < (this.game.playfield.width - this.game.playfield.margin - this.margin)) {
this.position.x += this.moveSpeed;
}
}
}
}

8
game/less/lib/button.less

@ -14,12 +14,16 @@ button {
button.direction-button {
display: block;
background: rgba(0,0,0, 0.6);
background: var(--direction-btn-color-50pct);
color: white;
font-size: 4em;
font-weight: bold;
font-family: apple color emoji,segoe ui emoji,noto color emoji,android emoji,emojisymbols,emojione mozilla,twemoji mozilla,segoe ui symbol;
&.active {
background: var(--direction-btn-color-80pct);
}
img.button-icon {
display: block;
height: 0.8em;
@ -33,7 +37,7 @@ button.action-button {
font-size: 3em;
font-family: apple color emoji,segoe ui emoji,noto color emoji,android emoji,emojisymbols,emojione mozilla,twemoji mozilla,segoe ui symbol;
&:active {
&.active {
background-color: var(--brand-color-80pct);
}
}

2
game/less/lib/variables.less

@ -10,7 +10,7 @@
@direction-btn-color-80pct : rgba(0,0,0, 0.8);
@direction-btn-color-50pct : rgba(0,0,0, 0.5);
@background-color : #000000;
@background-color : #1a1a1a;
@text-color : #e8e8e8;
@default-margin : 30px;

246
src/images/beach-foreground.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

6468
src/images/beach-plant-001.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 371 KiB

417
src/images/beach-plant-002.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 82 KiB

1485
src/images/beach-plant-003.svg

File diff suppressed because it is too large

After

Width:  |  Height:  |  Size: 149 KiB

62
src/images/beach-waves.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.7 KiB

561
src/images/palm-tree-001.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 136 KiB

474
src/images/palm-tree-002.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 112 KiB

Loading…
Cancel
Save