StoryBoard Basics

Bird Demo using Storyboard

The storyboard allows for state logic to be written into specific classes and eliminates the need to override the hamonengine.core.engine class. Creating a frame is as simple as overriding the hamonengine.events.frame class and responds to many of the same events. This demo provides a more complex implementation of the storyboard with nested frames.

Once the demo starts, hovering over a bird will show its name and clicking on it will cause it to sing or perform a call.


Canvas tag not supported

NOTES:

  • This demo demonstrates how to navigate between frames using the storyboard.jump, storyboard.goNext, & storyboard.goPrev methods.
  • This demo uses a startFrame to manage all global resources.
  • This demo uses a titleFrame to mange the title screen logic.
  • This demo uses a menuFrame to mange the menu screen logic.
  • This demo uses a birdFrame to mange the main bird demo logic.
  • This startFrame sets the property options.resourceFrame to true to force the engine to block until all its needed resources are loaded.
  • Resource frames are given loading priority to the other frames, which will load in parallel once the resource frame has completed loading.
  • Frames support the following events:
    • onFrameInitiating - Logic the 1st time a frame runs. This event occurs only once.
    • onFrameStarting - Logic that is called when switching to the current frame but before rendering has started. This event can be ignored.
    • onFrame - The core logic of the current frame.
    • onFrameStopping - Logic that is called when switching away from the current frame. This event can be ignored.

The Setup

The setup is a bit more complicated with one parent frame and several child frames.

  • Note each frame is unique.
  • The preloadAllFrames argument is true so that all other frames will wait on the resourceFrame to complete loading.
const storyboard = new hamonengine.events.storyboard({ preloadAllFrames: true, frames: [
    new startFrame({ frames: [
        new titleFrame(),
        new menuFrame(),
        new birdFrame()
    ]})
]});

(await new hamonengine.core.engine({storyboard}).load()).start();
                        

startFrame

The startFrame is the 1st registered frame in the storyboard so will be the 1st to run. Note that this frame is marked as a resourceFrame meaning all other frames will wait for it to complete loading. This allows for global resource data, such as spritesheets, audio, etc to load that the other frames may be dependent upon.

constructor(options = {}) {
    //NOTE: This is a resource frame.  All other frames will wait on this one until it has completed loading.
    //Non-resource frames will be loaded in parallel.
    options.resourceFrame = true;
    options.name = 'start';
    super(options);
    ...
}
/**
* An event that occurs when attempting to load resources.
* @param {object} storyboard calling the load operation.
* @return {Object} a promise that the resource has loaded successfully.
*/
async onloadResources(storyboard) {
   //Wait for the webfonts to complete loading or fetching metrics for a font will return the wrong information.
   const customFont = document.fonts.load("24px Walter Turncoat");

   //Load all of our sprites and music early.
   const spriteSheetPromise = this.birdReferences.spriteSheet.load();
   const albumPromise = this.birdReferences.album.load();
   const terrainPromise = this.terrain.load();
   return Promise.all([terrainPromise, spriteSheetPromise, albumPromise, customFont]);
}
                        

Once the startFrame runs it will jump to the the titleFrame. Hierarchy syntax in the jump method uses periods to separate each level.

/**
* An onFrame event that is triggered when this item is active.
* @param {number} elapsedTimeInMilliseconds since the last frame.
* @param {object} storyboard used to invoke this onFrame event.
* @param {number} totalTimeInMilliseconds is the total time that has elapsed since the engine has started.
* @param {object} lastFrame contains the last frame before transitioning to this one.  NOTE: This can be null or undefined.
*/
onFrame(elapsedTimeInMilliseconds, storyboard, totalTimeInMilliseconds, lastFrame) {
    //Go directly to the title after resources have loaded.
    storyboard.jump('start.title');
}
                        

titleFrame

The titleFrame uses the event onFrameInitiating to setup the title. This logic runs only once.

constructor(options = {}) {
    options.name = 'title';
    super(options);
    ...
}
/**
* An onFrameInitiating event that is triggered when frame is starting for the 1st time.
* This event occurs only once.
* @param {number} elapsedTimeInMilliseconds since the last frame.
* @param {object} storyboard used to invoke this onFrame event.
* @param {number} totalTimeInMilliseconds is the total time that has elapsed since the engine has started.
* @param {object} lastFrame contains the last frame before transitioning to this one.  NOTE: This can be null or undefined.
*/
onFrameInitiating(elapsedTimeInMilliseconds, storyboard, totalTimeInMilliseconds, lastFrame) {
   //Set the initial state of the birds.
   this.birds.reset(this.terrain, storyboard.engine.primaryScreen);
}
                        

The frame will then transition to the menuFrame based on the proper user input.

/**
* Processes keyboard events.
* @param {object} storyboard used to invoke this onKeyEvent event.
* @param {string} type of keyboard event such as 'up' or 'down' for keyup and keydown.
* @param {string} keyCode of the key (see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code)
* @param {object} e KeyboardEvent (see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent)
* @param {object} caller that triggered the event that can be a HTMLElement, instance of the HamonEngine, or a screen (see hamonengine.graphics.screen).
*/
onKeyEvent(storyboard, type, keyCode, e, caller) {
    if (type === 'down') {
        switch (keyCode) {
            //End the demo.
            case 'Escape':
                storyboard.engine.stop();
                break;
        }
    }
}
/**
* Processes mouse & touch events if captureTouchAsMouseEvents is set to true.
* @param {object} storyboard used to invoke this onMouseEvent event.
* @param {string} type of mouse event such as: 'click', 'up', 'down', 'move', 'enter', 'leave'.
* @param {object} v an instance of vector2 object that contain the x & y coordinates (see hamonengine.math.vector2).
* @param {object} e MouseEvent (see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
* @param {object} caller that triggered the event that can be a HTMLElement, instance of the HamonEngine, or a screen (see hamonengine.graphics.screen).
*/
onMouseEvent(storyboard, type, v, e, caller) {
    if (type === 'up') {
        //Go to the menuFrame.
        storyboard.goNext();
    }
}
                        

menuFrame

The menuFrame uses the onFrameStarting event to toggle specific menu options based on global conditions. Note that this event is called every time the storyboard loads this frame.

constructor(options = {}) {
    options.name = 'menu';
    super(options);
    ...
}
/**
* An onFrameStarting event that is triggered when frame is starting.
* @param {number} elapsedTimeInMilliseconds since the last frame.
* @param {object} storyboard used to invoke this onFrame event.
* @param {number} totalTimeInMilliseconds is the total time that has elapsed since the engine has started.
* @param {object} lastFrame contains the last frame before transitioning to this one.  NOTE: This can be null or undefined.
*/
onFrameStarting(elapsedTimeInMilliseconds, storyboard, totalTimeInMilliseconds, lastFrame) {
   //Only capture the screen from the birdFrame, as the titleFrame has too much text that can conflict with the menu items.
   this.lastScreen = lastFrame?.name === 'bird' ? storyboard.engine.primaryScreen.toLayer() : this.terrain.terrainLayer;

   //Hide the Start mneu item once we start and replace it with the resume option.
   if (this.resources.globalStateData.hasStarted) {
       this._menuItems.find(item => item.name === 'start').visible = false;
       this._menuItems.find(item => item.name === 'resume').visible = true;
   }

   //NOTE: Since this method overrides the base class event, then the base class event needs to be invoked to complete the frame starting event. 
   super.onFrameStarting(elapsedTimeInMilliseconds, storyboard, totalTimeInMilliseconds);
}
                        

Bird Sprites

Bird Sprites created by Refuzzle (https://opengameart.org/content/winter-birds)

Birds Metadata File

Grass Sprites

Grass Sprites created by KnoblePersona (https://opengameart.org/content/grassdirt-connecting-tileset-16x16)

Grass Metadata File

Forest Sprites

Forest Sprites created by ScratchIO and resized by me (https://opengameart.org/content/forest-level-decorations-pixel-art)

Forest Metadata File

Birds Audio Preview

Birds

Album Metadata File

Mario Paint - Flower Garden

Composers by Hirokazu Tanaka, Ryoji Yoshitomi, Kazumi Totaka.

© 1992 Nintend of America, Inc.