Options
All
  • Public
  • Public/Protected
  • All
Menu

Introduction

This documention is an overview of projects designed for building Anywhere Teacher activities (games, books, flash cards, etc.). They are based on TypeScript and are built on top of a shared School Zone framework/library.

Table of contents

  1. Tools Setup
  2. Media Asset Tools
  3. Environment
  4. Project Overview
  5. Activity Config File
  6. Getting Started with a Project
  7. Best Practices
  8. Building the Framework
  9. Git Tips
  10. Framework API

Tools Setup

IDE and Compiler

Install code editor, typescript compiler and extensions:

  1. Download and install Visual Studio Code from https://code.visualstudio.com/
  2. Install the Visual Studio Code extension Debugger for Chrome.
  3. If Typescript is not yet installed, download and install it from https://www.typescriptlang.org/index.html#download-links

Environment

Development repository

The following git repository contains source for a development web server, sz-framework, shared source and media, and source and media for all activities.

git.schoolzone.com/phaser

The wesite development.schoolzone.com has a copy of this repository for testing activities in QA and sharing with other team members.

It's common to clone this repository into the root of your primary drive: /docker/phaser

Note it's best to set 'depth' when initially cloning the project. See Git Tips for details.

SourceTree is recommended for a git GUI (https://www.sourcetreeapp.com/).

Web Server Setup

The development server can be setup using Docker or XAMPP.

Docker

Follow the steps below to setup using Docker (Note for Windows, Docker requires Windows Pro).

  1. Install Docker: https://www.docker.com/products/docker-desktop

  2. Navigate to the folder where you cloned the phaser git repository. If your structure is /docker/phaser/phaser/public_html/... then navigate to /docker/phaser

  3. Create a yaml file for the docker container. Create a file with the contents below and save as "docker-compose.yml":

     version: '3.2'
     services:
     phaser-web:
         image: webdevops/php-nginx:7.2
         ports:
         - 8086:80
         volumes:
         - type: bind
             source: ./phaser/public_html
             target: /app
         restart: always

    OPTIONAL (this may not be necessary)

     You can change the website port if you desire, make sure that you use unique ports if you plan to run multiple website containers.
    
     **phaser-web**: ports: - 8086:80 tells docker to map port 80 requests (the default http port) to port 8086.  Once running you will be able to access the website using **localhost:8086** by this example.
  4. Go to Docker and select Settings -> Resources -> File Sharing and add the path to your project directory, for example /docker/phaser:

  5. Build and Start the container - In terminal, navigate to the development directory/folder for this project - not in the repository folder - but the phaser folder that holds the docker-compose.yml file (for example: /docker/phaser ) and type:

     $ docker-compose down
     $ docker-compose up -d
     $ docker container ls

The output should look something like:

    SZP-WRK-012:phaser gregh$ cd ..
    SZP-WRK-012:phaser gregh$ ls -al
    total 32
    drwxr-xr-x   8 gregh  1075244921   256 Jul 10 15:16 .
    drwxr-xr-x   8 gregh  1075244921   256 Jul 11 15:55 ..
    drwxr-xr-x   9 gregh  1075244921   288 Jul 10 14:37 phaser
    -rwxr-xr-x@  1 gregh  1075244921   655 Jul 12 09:18 docker-compose.yml
    SZP-WRK-012:phaser gregh$ docker-compose down
    Stopping phaser_phaser-web_1 ... done
    Removing network at_default
    SZP-WRK-012:phaser gregh$ docker-compose up -d
    Creating network "phaser_default" with the default driver
    Pulling phaser-web (webdevops/php-nginx:7.2)...
    7.2: Pulling from webdevops/php-nginx
    Creating phaser_phaser-web_1 ... done
    SZP-WRK-012:phaser gregh$ docker container ls
    CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS              PORTS                                     NAMES
    0be743133e80        webdevops/php-nginx:7.2   "/entrypoint supervi…"   44 seconds ago      Up 42 seconds       443/tcp, 9000/tcp, 0.0.0.0:8086->80/tcp   phaser_phaser-web_1

docker-compose down shuts down any of the containers that are referenced by the YAML file in the event that they were already running.

docker compose-up -d builds the conatiner images and then starts them, the -d switch detaches the terminal so that when the command completes it gives back control to the terminal otherwise the terminal would be locked until the command was forcibly stopped.

docker container ls lists the running containers with their IDs, ports, status, and aliases (NAMES) and is used to see if your up command worked - alternately you can use docker ps to produce the same list

If there were no errors when bringing them up and you see the containers in your list you have a stand alone local running version of the website.

In your browser, go to http://localhost:8086/ and you should see the Phaser Development site!!



XAMPP

Follow these steps to setup using XAMPP. XAMPP is a completely free, easy to install Apache distribution containing MariaDB, PHP, and Perl.

Install/run by doing the following:

  1. Install from https://www.apachefriends.org/index.html
  2. Once installed, run the XAMPP Control Panel. In Windows, it will be: /xampp/xampp-control.exe
  3. Apache is the only service/module that needs to run for Anywhere Teacher activities.

Configure XAMPP to run Anywhere Teacher development site by doing the following within the XAMPP Control Panel:

  1. If Apache is running, click the Stop button

  2. Click the "Config" button for Apache and select "Apache (http.config)"

  3. Find the "Document Root" section and replace with the snippet below (confirm the paths match yours):

     DocumentRoot "C:/docker/phaser/phaser/public_html"
     <Directory "C:/docker/phaser/phaser/public_html">
         #
         # Possible values for the Options directive are "None", "All",
         # or any combination of:
         #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
         #
         # Note that "MultiViews" must be named *explicitly* --- "Options All"
         # doesn't give it to you.
         #
         # The Options directive is both complicated and important.  Please see
         # http://httpd.apache.org/docs/2.4/mod/core.html#options
         # for more information.
         #
         Options Indexes FollowSymLinks Includes ExecCGI
    
         #
         # AllowOverride controls what directives may be placed in .htaccess files.
         # It can be "All", "None", or any combination of the keywords:
         #   AllowOverride FileInfo AuthConfig Limit
         #
         AllowOverride All
    
         #
         # Controls who can get stuff from this server.
         #
         Require all granted
     </Directory>
  4. Save http.conf file and close.

  5. Click the Start button in the Apache row, if needed.

  6. Navigate to "localhost" in your web browser. You should see the Anywhere Teacher development site.

Build Scripts

To build typescript projects based on School Zone's framework, gulp and various gulp plugins must be installed:

  1. Install node to get npm (https://nodejs.org/en/download/). Version 11+ must be installed.
  2. Install gulp-cli, gulp and gulp plugings globally. There are prebuilt scripts for installing these and can be found in the git repository
    • Navigate to /phaser/public_html/assets/sz-framework-tools/gulp
    • For Mac/Unix, run the following script

      sh gulp-npm-install.sh

    • For Windows, run the following

      gulp-npm-install.bat

  3. For gulp scripts to run correctly, references to the gulp and the gulp plugins must be created locally to the project.
    • For Mac OS/Linux, copy ./gulp/gulp-npm-link.sh to the project root. From the project root, run:

      sh ./gulp-npm-link.sh

    • For Windows, copy ./gulp/gulp-npm-link-win.bat to the project root an run
    • A local package.json file must exist in the project directory for the gulp plugins to be found in the local node_modules folder. It basically just needs to exist and can be an empty json file.
      • Copy package.json from the app template project to the root of the project directory
      • OR create a new package.json by running the following from the project root directory (and then following the directions):

        npm init

    • Delete local gulp-npm-link*.* files when done so they don't get committed to git.

Upgrading older projects built using node 10.x or under

  • gulp4-run-sequence must be installed globally and referenced in the local project.
    • This will occur if running the latest scripts in step 2 and step 3 above. Otherwise it needs to be done manually.
  • the project's gulp.js must have the following defined at the beginning of the script:

    var runSequence = require('gulp4-run-sequence');


To have multiple versions of installed, use nvm

Media Asset Tools

Images

Several images can be combined into one image file + one json map file to define the individual images. This optimizes download. The result is called an image atlas (not to be confused with a "sprite sheet", which is a collection of animation frames for a single entity). Use the following online tool for building image atlases:

https://www.leshylabs.com/apps/sstool/

  1. Drag a collection of images to the window
  2. Try to keep the size less than 2048x2048
  3. Set a filename and Save.
  4. In the Sprite Map section, select JSON-TP-Array from the drop down.
  5. Set a filename and Save. The base filename (w/o extension) should be the same as image base filename.
  6. Move these files to the appropriate folder.

Audio

Several audio files can be stitched together into one audio file + one json map file to define the individual audio. This optimizes download. The result is called an audio sprite. Install and download the audiosprite tool: https://github.com/tonistiigi/audiosprite

Use the following command line to generate audio sprites. We will generate ogg and m4a files to support all major browsers.

audiosprite -g 1 -f howler -o outfile -e "ogg,m4a" -b 48 ./input/*.*

Notes On Asset Paths And Files

  • Paths are partial and relative to support working on different hosts
  • For image atlases and audio sprites, the "name" duals as the base filename and key (for phaser loading). This eliminates adding a key/value for each file (i.e. image file + json file, or audio.ogg + audio.m4a + audio.wav + audio.json).

Naming Conventions

To help support different file systems, file and folder names should be:

  • All lowercase with no special chars or spaces.
  • Only include letters, numbers and hyphens (and '.' for extensions), i.e. a-z,0-9,-,.

Project Overview

Projects for Anywhere Teacher have the following features:

  • Use Typescript language
  • Use gulp for build scripts
  • Uses Phaser Game engine for graphics, animations, physics, and input management
  • Uses the shared School Zone framework for such things as boot/startup, preloading assets, audio playback, UI widgets and progress tracking.

Template Projects

Reference the following template projects. Duplicate them to create a new typescript project based on the framework. They contain the necessary structure and files to build an Anywhere Teacher activity.

Game template

/phaser/public_html/assets/games/activity-template

On track template

/phaser/public_html/assets/on-track/on-track-activity-template

Important Files and Folders:

  • config/activity.config.json - config file for each activity. Modify this file to set the activity title, required assets for preloader, etc.
  • src/Activity.ts - main entry point for activity. Define the game scenes/states and game options here.
  • src/MainScene.ts - the default start scene/state
  • tsconfig.json - ts compiler config file. This file should not have to be modified.
  • gulp.js - gulp file. This file should only need to change if there are multiple variants to build (Deprecated)
  • .vscode/tasks.json - change the default build tasks (for projects with multiple variants)
  • .vscode/launch.json - change url path to point to this activity for debugging
  • assets/YYYY-MM-DD_major.minor.build - folder to store media used by the activity
    • NOTE that unprocessed media should be under a folder named raw
  • assets/runtime-config - folder to store config files that are merged with activity.config at runttime. These enable us to have multiple variation of the activity while keeping the same code base.

Generated files:

Do not edit these files directly.

  • tsconfig.gen.json - this file is generated to include activity.config.json settings and is used when compiling.
  • gen/* - all files in this folder are generated and should not be modified directly
  • bin/* - the transpiled javascript code

Build

shift + command - (Mac) runs the default gulp task defined by .vscode/launch.json
ctrl + shift + b - (Windows) runs the default gulp task defined by .vscode/launch.json
Terminal - alternatively, in the terminal from the project root directory you can run:

gulp
OR
gulp task

Debugging

Use f5 or Debug | Start Debugging to start a debug session. .vscode/launch.json must be set up correctly and the Debugger for Chrome VS Code extension must be installed.

Activity Config File

Here are the available properties in activity-config.json, with comments. The actual json file cannot have the comments in them.

{
    /* version of this json scheme */
    "scheme": 1,

    /* meta data for the activity */
    "info": {
        "title": "Template",  /* Title displayed on console */
        "date": "01/01/01",   /* The date the latest revision was made */
        "version": "1.0.0"    /* The current version of this activity */
    },

    /* default 'global' settings for the activity */
    "defaults": {
        /* [Optional] - preferred device orientation ["landscape" | "portrait"] */
        "orientation": "landscape",  

        /* Game world size */
        "size": {
            "width": 1024,
            "height": 768
        },

        /* Volume the background audio (if any) will play at */
        "backgroundVolume": 1.0,

        /* The volume the background audio (if any) will play at when the help audio is playing */
        "backgroundDuckingVolume": 0.2,

        /* [Optional] - default background for scenes */
        "background" : {
            "html": {
                /* type can be "color" | "image" | "colorAndImage" */
                "type": "color",
                /* [Optional] - color to fill in hex, #ffffff or 0xffffff */
                "color": "#e91e78",
                /* [Optional] - path to background image */
                "imageUrl": "assets/games/activity-template/assets/backgrounds",
                /* [Optional] - background image to use, relative to assets root or imageUrl (if specified) */
                "file": "images/tile.png" 
                /* [Optional] - size attribute, values can be "cover" | "contain" | "tile" | "tileH" | "tileV" . Default is "tile" */
                "size": "cover",
                /* [Optional] - opacity [0..1] */
                "opacity": 1 
            },
            /* [Optional] - Phaser stage settings are optional */
            "stage": {
                "color": "#0000ff"
            }
        }

    },

    /* define paths/urls for the activity */
    "paths": {
        /* the root path for the activity. This should exclude the domain so it works across local, development and live sites */  
        "assetsRoot": "games/activity-template/assets/2001-01-01_1.0.0",   

        /* the root path for the activities image resources. This will be relative to "assetsRoot" and appended to it. */
        "image": "images",

        /* the root path for the activities audio resources. This will be relative to "assetsRoot" and appended to it. */
        "audio": "audio",

        /* the root path for the activities animation resources. This will be relative to "assetsRoot" and appended to it. */
        "animations": "animations",

        /* the root path for the activities video resources. This will be relative to "assetsRoot" and appended to it. */
        "videos": "videos"

    },

    /* define the assets to be loaded by the preloader */
    "assets": {

        /* 
        Array of images to be preloaded. These should be pngs and will be located in the assetsRoot image path defined in "paths". So in this case 
            they would be in "games/activity-template/assets/2001-01-01_1.0.0/images"
            There are 3 types - atlas, sprite, and single file
        */
        "images": [

            /* an image atlas */
            {
                /* The cache key and base file name, i.e. use "ui" to load atlas defined by "ui.png" and "ui.json".  */
                "name": "ui",

                /*
                    [Optional] - a path within assetsRoot image path. So if set to "sub-1/sub-2" the full path would be
                    "games/activity-template/assets/2001-01-01_1.0.0/images/sub-1/sub-2/"
                */
                "path": "sub-1/sub-2",

                /* image type */
                "type": "atlas"
            },

            /* an image sprite */
            {
                /* the cache key */
                "name": "sprite",
                /* the file name */
                "file": "sprite.png",
                /*
                    [Optional] - a path within assetsRoot image path. So if set to "sub-1/sub-2" the full path would be
                    "games/activity-template/assets/2001-01-01_1.0.0/images/sub-1/sub-2/"
                */
                "path": "sub-1/sub-2",
                /* the number of frames in the sprite */
                "frames": 5,
                /* the width of each frame in pixels */
                "frameWidth": 200,
                /* the height of each frame in pixels */
                "frameHeight": 100,

                /* image type */
                "type": "sprite"

            },

            /* a single image */
            {
                /* the cache key */
                "name": "single",
                /* the file name */
                "file": "single.png",
                /*
                    [Optional] - a path within assetsRoot image path. So if set to "sub-1/sub-2" the full path would be
                    "games/activity-template/assets/2001-01-01_1.0.0/images/sub-1/sub-2/"
                */
                "path": "sub-1/sub-2",

                /* image type */
                "type": "file"
            }

        ],


        /* 
        Array of audio to be preloaded. These will be located in the assetsRoot audio path defined in "paths". So in this case 
            they would be in "games/activity-template/assets/2001-01-01_1.0.0/audio"
            There are 2 types - sprite and single file (default is single file)
            There are 2 modes - background and foreground (default is foreground)
        */
        "audio": [
            /* A background audio, single file */
            {
                /* 
                    The cache key and base filename, i.e. use "ambience" to load the supported file format (ambience.ogg|ambience.m4a|ambience.wav)
                */
                "name": "ambience",
                /*
                    [Optional] - a path within assetsRoot audio path. So if set to "sub-1/sub-2" the full path would be
                    "games/activity-template/assets/2001-01-01_1.0.0/audio/sub-1/sub-2/"
                */
                "path": "sub-1/sub-2",
                "mode": "background"
            },

            /* A foreground audio sprite */
            {
                /* 
                    The cache key and base filename, i.e. use "game-audio" to load the supported file format (game-audio.ogg|game-audio.m4a|game-audio.wav) 
                    and the sprite json file (game-audio.json)
                */
                "name": "game-audio",
                "type": "sprite"
            },

            /* A global audio sprite - This will load a sprite from the default global-audio-sprites path */
            {
                /* 
                    The cache key and base filename, i.e. use "game-audio" to load the supported file format (game-audio.ogg|game-audio.m4a|game-audio.wav) 
                    and the sprite json file (game-audio.json)
                */
                "name": "male-encouragements",
                "type": "global-sprite"
            }

            /* A global library voice audio file. This will load a file from the sz-global-audio-library voice path. */
            {
                /* 
                    [Optional] - the voice to use. If not set, 'default' will be used
                */
                "voice": "willow",

                /* 
                    The base file names
                */
                "names": ["apple", "banana"],
                /* 
                    The library name ("alphabet" | "numbers" | "wordlist", etc.)
                */
                "library": "wordlist",
                "type": "global-voice-file"
            }

            /* A global audio file. This will load a file from the default sz-global-audio-library path. */
            /* DEPRECATED - LOAD VOICE INSTEAD */
            {
                /* 
                    The base file names
                */
                "names": ["apple", "banana"],
                /* 
                    The library name ("alphabet" | "numbers" | "wordlist", etc.)
                */
                "library": "wordlist",
                "type": "global-file"
            }

        ],
        "animations": [

            /* an apng */
            {
                /* The cache key and base file name, i.e. use "anim" to load apng defined by "anim.png"  */
                "name": "anim",

                /*
                    [Optional] - a path within assetsRoot animations path. So if set to "sub-1/sub-2" the full path would be
                    "games/activity-template/assets/2001-01-01_1.0.0/animations/sub-1/sub-2/"
                */
                "path": "sub-1/sub-2",

                /* image type */
                "type": "apng"
            },
        ]
    }
}

Custom json/properites can be added and then accessed via:

SZ.System.instance.settings.activity.config

Or, from MainScene (or a class derived from SZ.Scene)

this.settings.activity.config

Getting Started with a Project

Config setup

Modify the following info settings first

  • title - set the title of the activity,
  • date - set the date that the revision began
  • version - increment the version each time there will be new published changes

Modify paths.assetsRoot to include the activity folder name and the correct assets version #. If assets do not change from the previous info.version, there is no need to dupicate the assets folder and give it a new date/version.

Media Assets

A folder should be added to the assets folder for each version of the assets. It should be named "YYYY-MM-DD_major.minor.build". Add unprocessed assets to the raw subfolder. Create a /raw subfolder for each file collection intended to be an image atlas or audio sprite.

Accessing sz-framework

The framework has several singleton subsystems that can be accessed via SZ.System.instance:

// audio manager
SZ.System.instance.audio

// Phaser game engine
SZ.System.instance.game

// Progress tracking for user
SZ.System.instance.progress

// Activity settings
SZ.System.instance.settings

There are several helper properties in SZ.Scene for accessing these subsystems too:

MainScene
...

// audio manager
this.audio

// Phaser game engine
this.game

// Progress tracking for user
this.progress

// Activity settings
this.settings

Main Source Files

Here are some of the main source files within an Anywhere Teacher project.

src Folder

Any .ts file put in the src folder will automatically be picked up and compiled

Activity.ts

This class embodies the main entry point to the activity. It kicks off starting the framework and Phaser 'boot' process. Most change will be to define your scenes and dynamically add media files to the preloader.

export class Activity {
    ...

    public startUp() {
        ...

        // initialize game
        this._game = new SZ.Game(options);

        // Optionally add a callback after the boot process is completed but before the preloader starts. This gives activities the opportunity to dynamically load media.
        this._game.onBootCompleted.add(this.onBootCompleted, this);

        //
        // Add your scenes/states here. Give a name/key to the scene class.
        //
        this._game.state.add('MainScene', SZ.MainScene, false);
        ...

        // start activity with the specified scene
        this._game.startUp("MainScene");
    }

    ...
    public onBootCompleted() {
        // add more files to the preloader
        ...
    }
}

MainScene.ts

MainScene is the default scene in the template project. Any class derived from SZ.Scene has the same abilities. The onCreate() function is a common place to setup your scene.

public onCreate() {
    // Make sure you call the super/base class        
    super.onCreate();

    ...
}

Accessing Phaser game engine

All Phaser classes and subsystems can be accessed by this.game.

// basic image
let background = this.game.add.image(0, 0, "game", "background");

// image that can recieve input and respond 
let goButton = this.game.add.image(179, 447, 'game', 'go-button');
goButton.anchor.setTo(0.5);
goButton.inputEnabled = true;
goButton.events.onInputDown.add(this.onGoSelected, this);

We currently use Phaser 2.6.2. Documentation on this version of the game engine can be found here: https://phaser.io/docs/2.6.2/index

Popular Phaser Classes/Objects

We frequently use the following Phaser classes.

Phaser.Image
Phaser Definition:
An Image is a light-weight object you can use to display anything that doesn't need physics or animation. It can still rotate, scale, crop and receive input events. This makes it perfect for logos, backgrounds, simple buttons and other non-Sprite graphics.

Example:

    let image = this.game.add.image(0, 0, "game", "background");

Phaser.Sprite
Phaser Definition:
At its most basic a Sprite consists of a set of coordinates and a texture that is rendered to the canvas.They also contain additional properties allowing for physics motion (via Sprite.body), input handling (via Sprite.input), events (via Sprite.events), animation (via Sprite.animations), camera culling and more.

Notes:
Essentially, Phaser.Sprite is a Phaser.Image that can support sprite animations (via sprite sheet) and physics.

Example:

    let sprite = this.game.add.sprite(0, 0, "game", "asteroid");

Phaser.Tween
Phaser Definition:
A Tween allows you to alter one or more properties of a target object over a defined period of time. This can be used for things such as alpha fading Sprites, scaling them or motion. Use Tween.to or Tween.from to set-up the tween values. You can create multiple tweens on the same object by calling Tween.to multiple times on the same Tween. Additional tweens specified in this way become "child" tweens and are played through in sequence.

Example:

    let sprite = this.game.add.sprite(0, 0, "game", "asteroid");
    // move diagonally across the screen
    let moveTween = this.game.add.tween(sprite).to({ x: 1024, y: 768 }, 1500, Phaser.Easing.Default, true);
    moveTween.onComplete.add( function() {
        // called when tween finished
    }, this);
    // scale up and down forever
    let scaleTween = this.game.add.tween(sprite.scale).to({ x: 1.25, y: 1.25 }, 500, Phaser.Easing.Linear.None, true, 0, -1, true);

Audio

Audio playback can be done thru the audio manager

// play single audio from audio sprite
this.audio.play("game-audio", "bang");

// play a series of audio files
this.audio.playSequence([
    {
        name: "one",
        sprite: "game-audio"
    },
    {
        name: "two",
        sprite: "game-audio",
        delay: 500
    },
    {
        name: "three",
        sprite: "game-audio",
        delay: 250
    }
]);    

// greater control of audio playback
let audioClip = this.audio.play("game-audio", "tweet");
audioClip.volume = 0.5;
audioClip.onStop = function() {

}.bind(this);
audioClip.stop();

Setup Help

There are functions specifically for playing activity help/instructions. It has some additional features, like automatically lowering the volume of the background music while the help is playing so it is more clear. Generally help autoplays when an activity first starts, and then it can be replayed by the user pressing the '?' overlay html button. These are usually configured in onCreate():

public onCreate() {
    // Make sure you call the super/base class        
    super.onCreate();
    ...

    // at startup, play instruction audio + click/touch to start audio
    // playHelpAudio plays help directly
    this.playHelpAudio([
        {name: "game-audio", sprite: "instructions"},
        {name: "globalAudio", sprite: (this.game.device.desktop) ? "click_to_start" : "touch_to_start", delay: 500}
    ]);

    // set default help audio to just instructions
    // setDefaultHelpAudio connects the audio to the '?' overlay button
    this.setDefaultHelpAudio(
        {name: "game-audio", sprite: "instructions"}
    );
}

Exiting

If the activity has an end, once it is reached you can close/exit the activity by calling:

this.exitActivity();

Progress Tracker

The progress tracker has a few purposes:

  • It tracks how far the user is within the activity
  • It tracks attempts to measure how well the user is doing
  • It tracks state so the activity can resume where it left off (if they leave the page and return).

Progress/completion is measured with numeric values that has a total (# of points/problems to finish the activity) and completed (the # of points/problems the user has completed). This may be a little different for each activity, so total/completed is going to be values best fitted for the activity. Some can be easily broken down into chunks, like math problems, and others might not be broken down at all and represent a single problem, like jigsaw puzzles. The state is json that really only has meaning to the activity itself, and stores just enough data to be able to revive the users position and resume. Note that the framework will load the activity progress for the active user when starting. It will be loaded before the first scene is created.

The progress tracker can be accessed by SZ.System via: SZ.System.instance.progress

Or by the SZ.Scene helper property this.progress

Common properties of SZ.ProgressTracker are:

  • total - The total number of problems/points that represent 100% completion
  • completed - The number of promblems/points completed by user
  • state - activity specific state data that can be used to restore/resume an activity

Common usage (framework < 1.1.0):

Here is code used quite a bit when starting a new activity or resuming one:

private _state:any = undefined;

public onCreate() {
    // Make sure you call the super/base class        
    super.onCreate();
    ...

    // this.progress is loaded by framework automatically (if there is any started)
    if (!this.progress.isInitialized || this.progress.isDone) {
        // start new
        this.progress.initialize(totalProblems, optionalInitialState);
    }
    else {
        // resume
        this._state = this.progress.state;
        ...
    }

}

Common usage (framework >= 1.1.0):

When the Preloader scene is completed the framework checks for existing progress and compares its version to the current activity's version. Progress is deemed incompatable if:

  • Progress state 'major' is more than one version behind activity 'major' version -> create new progress
  • Progress state 'major' version is less than the activity 'major' version -> attempt to transform progress

It will then dispatch onNewProgress or onTransformProgress Phaser.Signals. Activity.ts:startUp() can bind a callback to these Phaser.Signals to handle the respective scenarios.

  • onNewProgress callback should initialize SZ.ProgressTracker and set progress.state as needed by the activity.
  • onTransformProgress callback should transform existing progress state then set SZ.Game.progressTransformSuccessful depending on whether the transform was successful or not.

Example:

public startUp() {
    const options = new SZ.GameOptions();
    this._game = new SZ.Game(options);

    // Add our states.
    this._game.state.add("MenuScene", SZ.MenuScene, false);
    this._game.state.add("MainScene", SZ.MainScene, false);

    // Handles progress scenarios
    this._game.onNewProgress.add(this.onNewProgress, this);
    this._game.onTransformProgress.add(this.onTransformProgress, this);

    this._game.startUp("MenuScene");
}

private onNewProgress(){
    let progress = SZ.System.instance.progress;
    let configInfo = SZ.System.instance.settings.activity.config.info;

    progress.initialize(configInfo.numOfRounds, new SZ.ProgressData());

    // Set initial state here
    progress.state.words = "cat,sat,pat,mat,rat";
    progress.save();
}

private onTransformProgress(){
    let progress = SZ.System.instance.progress;

    // Perhaps the new version uses an array instead of a string
    let array = progress.state.words.split(",");
    progress.state.words = array;

    if(Array.isArray(array) && array.length > 0){
        SZ.System.instance.game.progressTransformSuccessful = true;
    } else {
        SZ.System.instance.game.progressTransformSuccessful = false;
    }
}

When the activity is restarted from the html overlay, we'll need to make sure the progress gets reinitialized. Activity:onNewProgress will be dispatched by default through Scene:onRestartGame provided that Scene:isActivityFinished is set to true.

Here is some common calls when progress changes:

onGoSelected(isCorrect:boolean) {
    ...

    // the user made an attempt to solve a problem:
    this.progress.addAttempt(isCorrect);

    // the user has completed a problem. 
    // Note some problems do not complete until the correct answer is given, like in Three Letter Words, so 'completed' is not incremented until the answer is correct
    this.progress.completed++;

    // commit changes
    this.progress.save();
}

Profile Rewards

Users earn tokens by playing activities. These can be redeemed to buy backgrounds and stickers from the Play Zone. The framework handles earning tokens for just starting an activity. The activity is responsible for giving tokens when a user completes it. This can be done as follows

// The value passed to profile rewards is multiplied by 10
this.profileRewards.earnRewardsForCompletedActivity(2);

Reporting Analytics

Analytics can be sent back to the server. Time reporting (time spent playing the activity) is automatically sent by the framework. Critical error exceptions are reported automatically too. Answer attempts/results are reported by the activity. Note that the answer attempts are being deprecated in favor of progress tracking.

// report a single answer
this.reporter.logAnswers(true);

// report multiple answers
this.reporter.logAnswers([true, true, false]);

Best Practices

Source file names

File names should be CamelCase (first letter capitalized) with alpha characters only, and perferrably named after the source file's main class or interface (i.e. MainScene.ts)

Code conventions

Class and interface names should be CamelCase (first letter capitalized).

Class private properties/variables should be prefixed with "_" and be CamelCase (first letter lowercase).

Functions should be CamelCase (first letter lowercase).

Function parameters should be CamelCase (first letter lowercase).

Local variables should be CamelCase (first letter lowercase).

Documentation

Follow typeDoc | jsDoc comment conventions. https://typedoc.org/guides/doccomments/

The 'this' context

Bind function callbacks to ensure the right 'this' context is preserved:

audio.onStop = function() {

}.bind(this);

let vs var

Prefer 'let' over 'var' as it follows standard scoping conventions.

Typing

Prefer typed variables and parameters. Avoid type 'any'.

Building sz-framework

Setup

Before building the framework do the following:

  • Change directory to: public_html\assets\sz-framework-tools\library
  • Run the following on the command line (on first use)
     npm install

To start a new version of the framework do the following:

  • Set a new version and date in sz-framework-info.js.
  • Add an entry to history.txt with a description of the update.

Gulp commands

You have several Gulp commands available on the commmand line:

Return the processed version number derived from the sz-framework-info.js

gulp version    

Run both of the tsc commands

gulp build  

Derive the version number and move the dist directory to the global sz-framework in the appropriately versioned folder (creates automatically if doesn't exist)

gulp publish    

Update documentation

Create/update the documentation website by running the following:

build-docs.bat

The website files will be created in the folder ./docs/website. Upload all the files/subfolders in this folder to the framework.doc.anywhereteacher s3 bucket, i.e. https://s3.amazonaws.com/framework.doc.anywhereteacher. It's safest to delete all files and folders in this s3 bucket before uploading the new files.

The documentation will be accessible thru the browser at http://framework.doc.anywhereteacher.s3-website-us-east-1.amazonaws.com/

Git Tips

When using the git command line directly, these tips can be useful.

Change Depth for initial clone

When cloning the repository, it's best to set the 'depth' to a number < 20. The depth is the number of recent commits, so if not specified the entire git history will be cloned. With all the media files in this repository, it could take a long time to clone. Here is an example:

git clone http://username:password@git.schoolzone.com/phaser --depth 20

Update your own local copy of the repo often

git checkout master
git status
git pull

pull grabs all the updates from the server

Create a new feature or working branch

git checkout -b myfeature

this creates a branch called myfeature and checks it out

Make commits on your working branch

git status
git log -10 --oneline

Where the -10 flag limits the log to the last 10 log entries and the --oneline flag writes the log in shorthand Use this before writing a commit so you can write it in the same format as prior commits.

    git add .
    git status
    git commit -m "Books - All About Bears - adds book audio"

Merge your branch into master

    git checkout master
    git pull

Pull any new changes into your master

    git merge --no-ff myfeature -m "project – my message"

Merge your work into the master branch The --no-ff flag causes the merge to always create a new commit object, even if the merge could be performed with a fast forward. This allows us to keep a proper branch structure

    git push

Push your changes to development

    git checkout mybranch

Return to your branch to continue working

Index

Namespaces