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.
Install code editor, typescript compiler and extensions:
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/).
The development server can be setup using Docker or XAMPP.
Follow the steps below to setup using Docker (Note for Windows, Docker requires Windows Pro).
Install Docker: https://www.docker.com/products/docker-desktop
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
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.
Go to Docker and select Settings -> Resources -> File Sharing and add the path to your project directory, for example /docker/phaser:
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!!
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:
Configure XAMPP to run Anywhere Teacher development site by doing the following within the XAMPP Control Panel:
If Apache is running, click the Stop button
Click the "Config" button for Apache and select "Apache (http.config)"
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>
Save http.conf file and close.
Click the Start button in the Apache row, if needed.
Navigate to "localhost" in your web browser. You should see the Anywhere Teacher development site.
To build typescript projects based on School Zone's framework, gulp and various gulp plugins must be installed:
sh gulp-npm-install.sh
gulp-npm-install.bat
sh ./gulp-npm-link.sh
npm init
var runSequence = require('gulp4-run-sequence');
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/
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/*.*
To help support different file systems, file and folder names should be:
Projects for Anywhere Teacher have the following features:
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
Do not edit these files directly.
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
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.
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
Modify the following info settings first
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.
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.
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
Here are some of the main source files within an Anywhere Teacher project.
Any .ts file put in the src folder will automatically be picked up and compiled
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 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();
...
}
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
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 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();
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"}
);
}
If the activity has an end, once it is reached you can close/exit the activity by calling:
this.exitActivity();
The progress tracker has a few purposes:
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:
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:
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.
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();
}
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);
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]);
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)
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).
Follow typeDoc | jsDoc comment conventions. https://typedoc.org/guides/doccomments/
Bind function callbacks to ensure the right 'this' context is preserved:
audio.onStop = function() {
}.bind(this);
Prefer 'let' over 'var' as it follows standard scoping conventions.
Prefer typed variables and parameters. Avoid type 'any'.
Before building the framework do the following:
npm install
To start a new version of the framework do the following:
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
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/
When using the git command line directly, these tips can be useful.
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
git checkout master
git status
git pull
pull grabs all the updates from the server
git checkout -b myfeature
this creates a branch called myfeature and checks it out
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"
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