Browse Source

Startup, gamepads, and UI/UX

- reorg and refactor the startup screen into the core engine
- Engine now drives game startup
- Gamepad detection and input support via NiceInput
- Changed how and when NiceAudio inits and music starts
- Large reorg of how overlays are handled
- .nice-button created for overlays
- Element size tweaking in overlays and UI/UX
- The system menu has been created for use during startup
- game controller SVG added for startup view
- very rudimentary Flexbox support added to startup view
develop
Rob Colbert 2 years ago
parent
commit
e440a9d218
  1. 62
      game/assets/img/game-controller.svg
  2. 140
      game/js/game-app.js
  3. 11
      game/js/lib/game-player.js
  4. 18
      game/less/lib/button.less
  5. 12
      game/less/lib/flex.less
  6. 16
      game/less/lib/game-view.less
  7. 18
      game/less/lib/responsive.less
  8. 12
      game/less/lib/system-menu.less
  9. 1
      game/less/style.less
  10. 25
      game/views/game-view.pug

62
game/assets/img/game-controller.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

140
game/js/game-app.js

@ -46,26 +46,8 @@ export default class GameApp extends NiceGame {
},
},
};
}
initMenuOverlays ( ) {
/*
* Attach to each expected menu overlay
*/
this.overlays = {
systemMenu: document.querySelector('.menu-overlay#system-menu'),
mainMenu: document.querySelector('.menu-overlay#main-menu'),
gameControls: document.querySelector('.menu-overlay#game-controls'),
};
this.startButton = this.overlays.mainMenu.querySelector('button#start-button');
this.startButton.onclick = this.onStartGame.bind(this);
this.bootButton = this.overlays.systemMenu.querySelector('button#boot-button');
this.bootButton.onclick = this.bootGameCartridge.bind(this);
this.gameDisplay.removeChild(this.overlays.mainMenu);
this.gameDisplay.removeChild(this.overlays.gameControls);
document.addEventListener('ngsdk:game-boot', this.onGameBoot.bind(this));
}
async run ( ) {
@ -75,10 +57,9 @@ export default class GameApp extends NiceGame {
);
}
async bootGameCartridge (event) {
async onGameBoot (event) {
NiceInputTools.cancelEvent(event);
this.gameDisplay.removeChild(this.overlays.systemMenu);
this.gameDisplay.appendChild(this.overlays.mainMenu);
/*
@ -93,19 +74,32 @@ export default class GameApp extends NiceGame {
this.input.addButton('moveRight', '#btn-move-right');
this.input.addButton('throwEgg', '#btn-throw-egg');
this.audio.start();
this.input.addGamepadButton('system:start', 9);
this.input.addGamepadButton('system:back', 8);
this.input.addGamepadButton('throwEgg', 0);
this.input.addGamepadButton('moveLeft', 14);
this.input.addGamepadButton('moveRight', 15);
this.startMusicStream('/dist/assets/audio/music/blueberries');
await this.loadGameAssets();
this.mode = 'menu';
this.background.setMode('story');
this.startUpdateLoop();
}
async onStartGame (event) {
NiceInputTools.cancelEvent(event);
this.startNewGame();
}
async startMusicStream (url) {
await this.audio.setMusicStream(url);
this.audio.playMusicStream();
this.audio.musicVolume = 0.4;
}
startNewGame ( ) {
this.mode = 'loading';
this.gameDisplay.removeChild(this.overlays.mainMenu);
@ -126,12 +120,6 @@ export default class GameApp extends NiceGame {
this.background.setMode('play');
}
async startMusicStream (url) {
await this.audio.setMusicStream(url);
this.audio.playMusicStream();
this.audio.musicVolume = 0.4;
}
startLevel ( ) {
const now = performance.now();
this.nextSpawnInterval = 1000 * 2;
@ -163,7 +151,11 @@ export default class GameApp extends NiceGame {
}
}
updateMenu (/* elapsed, now */) { }
updateMenu (/* elapsed, now */) {
if (this.input.isInputPressed('system:start')) {
this.startNewGame();
}
}
renderMenu (ctx) {
this.background.render(ctx);
@ -262,6 +254,64 @@ export default class GameApp extends NiceGame {
formation.addTier(7);
}
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) {
const fieldWidth = this.playfield.width - (this.playfield.margin * 2) - (padding * 2);
return this.playfield.margin + padding + (Math.random() * fieldWidth);
}
initMenuOverlays ( ) {
/*
* Attach to each expected menu overlay
*/
this.addOverlay('debugDisplay', '.menu-overlay#debug-display');
this.addOverlay('mainMenu', '.menu-overlay#main-menu');
this.addOverlay('gameControls', '.menu-overlay#game-controls');
document.addEventListener('ngsdk:gamepad-connected', this.onGamepadConnected.bind(this));
this.startButton = this.overlays.mainMenu.querySelector('button#start-button');
this.startButton.onclick = this.onStartGame.bind(this);
this.gameDisplay.removeChild(this.overlays.mainMenu);
this.overlays.mainMenu.removeAttribute('hidden');
this.gameDisplay.removeChild(this.overlays.gameControls);
this.overlays.gameControls.removeAttribute('hidden');
this.overlays.systemMenu.removeAttribute('hidden');
}
async onGamepadConnected (event) {
const { gamepad } = event.detail;
this.log.info('onGamepadConnected', 'gamepad connected', { gamepad });
const gamepadFeedback = this.overlays.systemMenu.querySelector('span#gamepad-feedback');
gamepadFeedback.textContent = 'CONNECTED';
}
async loadGameAssets ( ) {
const jobs = [ ];
@ -309,34 +359,6 @@ 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) {
const fieldWidth = this.playfield.width - (this.playfield.margin * 2) - (padding * 2);
return this.playfield.margin + padding + (Math.random() * fieldWidth);
}
}
window.addEventListener('load', async ( ) => {

11
game/js/lib/game-player.js

@ -52,12 +52,12 @@ export default class GamePlayer extends NiceSprite {
update ( ) {
const { input } = this.game;
if (input.buttons.moveLeft.isPressed || input.keys.moveLeft.isPressed) {
if (input.isInputPressed('moveLeft')) {
if (this.position.x > (this.game.playfield.margin + this.margin)) {
this.position.x -= this.moveSpeed;
}
}
if (input.buttons.moveRight.isPressed || input.keys.moveRight.isPressed) {
if (input.isInputPressed('moveRight')) {
if (this.position.x < (this.game.playfield.width - this.game.playfield.margin - this.margin)) {
this.position.x += this.moveSpeed;
}
@ -65,7 +65,12 @@ export default class GamePlayer extends NiceSprite {
}
playRandomQuip ( ) {
this.game.audio.playSound(this.getRandomQuipId());
if (this.playingQuip) {
return;
}
const { source } = this.game.audio.playSound(this.getRandomQuipId());
source.addEventListener('ended', ( ) => { this.playingQuip = false; });
this.playingQuip = true;
}
getRandomQuipId ( ) {

18
game/less/lib/button.less

@ -13,10 +13,11 @@ button.nice-button {
-ms-user-select: none;
user-select: none;
font-size: 40px;
&.direction-button {
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;
@ -27,18 +28,11 @@ button.nice-button {
&.active {
background: var(--direction-btn-color-80pct);
}
img.button-icon {
display: block;
height: 0.8em;
margin: 0 auto;
}
}
&.action-button {
background-color: var(--brand-color-50pct);
color: white;
font-size: 2em;
font-family: apple color emoji,segoe ui emoji,noto color emoji,android emoji,emojisymbols,emojione mozilla,twemoji mozilla,segoe ui symbol;
&[hidden] {
@ -71,5 +65,11 @@ button.nice-button {
position: absolute;
bottom: 120px;
right: 20px;
}
}
img.button-icon {
display: block;
height: 1em;
margin: 0 auto;
}
}

12
game/less/lib/flex.less

@ -0,0 +1,12 @@
.nice-flex {
display: flex;
&.nice-flex-middle {
align-items: center;
}
.nice-flex-grow-1 {
width: 50%;
}
}

16
game/less/lib/game-view.less

@ -56,6 +56,19 @@
background: none;
}
.menu-panel {
display: block;
font-size: 24px;
}
img.menu-item-icon {
display: block;
width: 100%;
max-width: 240px;
height: auto;
margin: 0 auto;
}
button.menu-button {
display: block;
width: 240px;
@ -68,8 +81,7 @@
background-color: var(--brand-color);
color: white;
font-size: 24px;
font-weight: bold;
font-size: inherit;
cursor: pointer;
}

18
game/less/lib/responsive.less

@ -33,13 +33,16 @@
border-radius: 8px;
font-size: 18px;
}
.menu-panel {
font-size: 16px;
}
button.menu-button {
display: block;
width: 180px;
padding: 0 10px;
border-radius: 4px;
font-size: 16px;
}
}
@ -48,17 +51,10 @@
}
}
#game-controls button {
button.nice-button {
width: 50px;
height: 50px;
}
button.direction-button {
font-size: 2em;
}
button.action-button {
font-size: 1.5em;
font-size: 25px;
}
button.action-button#btn-throw-egg {

12
game/less/lib/system-menu.less

@ -1,3 +1,7 @@
.menu-overlay#debug-display {
pointer-events: none;
}
.menu-overlay#system-menu {
background-color: blue;
font-family: 'Mono Regular', monospace;
@ -31,14 +35,6 @@
}
}
img.nice-cartdridge-icon {
display: block;
width: 100%;
max-width: 240px;
height: auto;
margin: 0 auto;
}
button.menu-button#boot-button {
background: none;
color: lightgray;

1
game/less/style.less

@ -13,4 +13,5 @@
@import 'lib/navbar.less';
@import 'lib/system-menu.less';
@import 'lib/flex.less';
@import 'lib/responsive.less';

25
game/views/game-view.pug

@ -4,19 +4,32 @@ block game-view
#game-display.game-display-wrapper
canvas(id="game-display" width="960" height="540").game-display
#system-menu.menu-overlay
#system-menu(hidden).menu-overlay
.os-info
.os-version NICE-OS #{pkg.version}
.game-sdk-version SDK #{niceGameSdk.version}
.game-description #{pkg.description}
button(type="button").menu-button#boot-button
img(src="/dist/assets/img/game-cartdridge.svg", alt="Game Cartdridge Icon", width=120).nice-cartdridge-icon.icon-large
span BOOT CARTRIDGE
#main-menu.menu-overlay.menu-transparent
.nice-flex.nice-flex-middle
.nice-flex-grow-1.text-center
.menu-panel.text-center
img(src="/dist/assets/img/game-controller.svg", alt="Game Controller Icon", width="120").menu-item-icon.icon-large
span#gamepad-feedback MOVE D-PAD
.nice-flex-grow-1
.menu-panel
button(type="button").menu-button#boot-button
img(src="/dist/assets/img/game-cartdridge.svg", alt="Game Cartdridge Icon", width=120).menu-item-icon.icon-large
span BOOT CARTRIDGE
#main-menu(hidden).menu-overlay.menu-transparent
button(type="button").menu-button#start-button Start Game
#debug-display(hidden).menu-overlay.menu-transparent
div Gamepad buttons
#debug-gamepad-buttons --------------------------------
#game-controls.menu-overlay.menu-transparent
#game-controls(hidden).menu-overlay.menu-transparent
button(type="button").nice-button.direction-button#btn-move-left
img(src='/dist/assets/img/btn-left.svg', alt='Left button icon').button-icon

Loading…
Cancel
Save