@uap-external/get-creative-create-app-shared
v3.2.4
Published
Shared code used across Get Creative menu and Goodboy's make machines
Downloads
3
Readme
CBeebies Get Creative Documentation
Status update
This package isn't actively maintained, but is still used by Get Creative (PickNMix). It's especially used by the Make Machines (Get Creative games).
This used to be deployed to @bbc/get-creative-create-app-shared
, but has been updated to deploy to @uap-external/get-creative-create-app-shared
in Verdaccio. During this change, the package version was reset to 0.0.x-dev
, and that's what is currenty used.
There are some changes on the develop
branch that aren't on the master
branch, and I'm not really sure what's going on there. I ended up doing changes on the master
branch.
I wasn't able to find an automated way of deploying this to Verdaccio. So I used the following process:
- Bump version in package.json
npm install
- Commit & Push
npm publish --registry https://uap-verdaccio.tools.bbc.co.uk
Introduction
The CBeebies Get Creative application is a collection of creative mini games (make machines) which allow users to make creations, and save them to a gallery. The make machines and gallery are accessed via a central hub (the menu). Each make machine can be presented with different branding through a collection of external assets loaded at runtime (a brand pack). The brand is selected via the menu before the user is presented with the make machines that are available for that brand.
Whilst in the menu, users may also be presented with an optional challenge, which consists of a suggestion of something they may wish to create. Challenges are associated with a particular make machine, and either one or all available brands. Accepting the challenge will open the make machine with the appropriate brand pack and prompt the user to fulfil the challenge. Creations saved to the gallery can also be viewed and edited in the relevant make machine.
The application is delivered via the BBC Pick n Mix framework, and new make machines, brand packs and challenges can be added to the application via this framework. This document outlines the various features described above, how they fit together within the Pick n Mix framework, how new Make Machines, brand packs and challenges can be added and configured, and what requirements new Make Machines must fulfil in order to function within the application.
Running locally
To build and run the menu locally see the readme file in the menu repository - https://github.com/bbc/cbeebies-get-creative-app-menu, configuration instructions are below.
App Configuration
How to configure the application to pull in all the required content and information
The Get Creative application is delivered using the BBC Pick n Mix framework This section deals with the Get Creative specific configuration within this framework - details of the framework itself can be found here.
app-config.json
Must specify the bundled menu
package as the init
package, as well as valid values for the availablePackagesEndpoint
and the configEndpoint
.
bundled-packages.json
Must specify the menu
as a bundled package, this is currently the only bundled package in the application.
available-packages.json
machine-assets
The menu requires a package with "packageId":"machine-assets"
- this is simply a collection of assets used in the menu for make machine specific elements: the make machine boxes on the factory floor, the title banner and a piece of voiceover for the make machine title. This means a new make machine can be added without having to create a new version of the menu - simply add the new assets to this package.
{
"packageId": "machine-assets",
"remoteUrl": "{{$URL}}",
"type": "content-pack",
"version": "1.0.1",
"dependencies": [],
"metadata": "",
"tags": []
},
Menu brand packs
Each brand must have an associated brand pack for use in the menu. This would be created using the CMS. The package description should have a "type":"content-pack"
, and "metadata":"menu.[brand]
where [brand]
is the brand slug used for the brand throughout the rest of the app. It is these two properties that allow the menu to identify the packcage as a menu brand pack, and to link the pack to the brand.
{
"packageId": "brand-hey-duggee",
"remoteUrl": "{{$URL}}",
"type": "content-pack",
"version": "1.0",
"dependencies": [],
"metadata": "menu.hey-duggee",
"tags": ["brand-go-jetters", "brand-bitz-and-bob", "brand-waffle-the-wonder-dog"]
}
Each brand should also have 3 other available brands associated with it in the tags
property, these define the brand's "friends" that are used in determining which brands are initially visible in the menu buddy picker.
Make Machines
Make machine packages must have a unique packageId
, used to link brand packs to make machines, "type": "experience"
and the experience.launch
property set to "machine"
.
{
"packageId": "magic-paint",
"remoteUrl": "{{$URL}}",
"type": "experience",
"experience": {
"launch": "machine",
"experienceDir": "/",
"config": {}
},
"version": "1.0.1",
"dependencies": [],
"metadata": "",
"tags": []
},
Make Machine Brand Packs
A machine brand pack is defined by having "type": "content-pack"
and "metadata": "[machine].[brand]"
, where [machine]
is the packageId
of the make machine package and [brand]
is the slug used to define the brand throughout the rest of the app.
{
"packageId": "magic-paint-assets-duggee",
"remoteUrl": "{{$URL}}",
"type": "content-pack",
"version": "1.0.1",
"dependencies": [],
"metadata": "magic-paint.duggee",
"tags": []
},
Challenges
The only requirement for a challenge package description is "metadata": "challenge"
, this is what tells the menu that it is a challenge. The brand and make machine information is defined in the config.json
file contained within the challenge package zip created via the CMS.
{
"packageId": "challenge-generic",
"remoteUrl": "{{$URL}}",
"type": "content-pack",
"version": "1.0.1",
"dependencies": [],
"metadata": "challenge",
"tags": []
},
Custom Config
The following files need to be present at the location defined in app-config.json's configEndpoint, they are used by the menu to determine the visibility and order of items within the various menu sections, as well as any seasonal theme that should be used. These files need to exist in the correct folders within folder that is the configEndpoint as these paths are hard coded within the menu.
order-config/id/buddy-picker-order.json
This determines the visibility and order of the brands when they are presented for selection in the opening screen of the menu and takes the following form:
{
"results": [
{
"slotId": 1,
"objects": [
{
"id": "menu-assets-duggee",
"visible": true
}
]
},
{
"slotId": 2,
"objects": [
{
"id": "menu-assets-gojetters",
"visible": true
}
]
}
]
}
slotId
(number) determines the order the brands will be shown in (these are sorted using parseInt so be careful if using string values) .Note that the menu displays the brands in a continuous loop with 3 visible at any one time. The initial state of the brand menu will display the first three items defined by the order of slotId
, from left to right, with the 2nd brand in the center (the most prominent position visually).
The objects
property is an array consisting of a single object:
The id
(string) property must match the packageId
of the menu brand pack associated with this brand.
The visible
property determines if the brand is to be displayed in the menu or not.
key-value-config/id/buddy-picker-priorities.json
Describes a promoted brand to display in the buddy picker and 2 brands to show in addition to the promoted brand when the application is launched for the first time. Brands are specified by the packageId
of the brand's menu brand pack in available-packages.json
, e.g:
{
"launch-brand-1": "brand-hey-duggee",
"launch-brand-2": "brand-go-jetters",
"promoted-brand": "brand-bitz-and-bob"
}
order-config/id/[brand]-factory-floor-order.json
Each brand used in the menu must have a corresponding [brand]-factory-floor-order.json file, where [brand] is unique the brand slug used to identify the brand throughout the rest of the app. This file describes the order in which make machines appear on the menu factory floor:
{
"results": [
{
"slotId": 1,
"objects": [
{
"id": "magic-paint-assets-duggee",
"visible": true
}
]
},
{
"slotId": 2,
"objects": [
{
"id": "terrific-toys-assets-duggee",
"visible": true
}
]
},
{
"slotId": 3,
"objects": [
{
"id": "play-puppets-assets-duggee",
"visible": true
}
]
},
{
"slotId": 4,
"objects": [
{
"id": "sound-doodles-assets-duggee",
"visible": true
}
]
}
]
}
slotId
(number) determines the order the machines will be shown in.
The objects
property is an array consisting of a single object:
The id
(string) property must match the packageId
of the make machine.
The visible
property determines if the machine is to be displayed in the menu or not.
order-config/id/challenges-order.json
Detemines the order in which challenges are offered to the user. Note that other factors also play into this logic - in order for a challenge to be displayed it must also be valid for the currently selected brand, and the make machine with which it is associated must have been already played to the point where a the on-boarding has been completed.
{
"results": [
{
"slotId": "1",
"objects": [
{
"id": "challenge-3",
"visible": true
}
]
},
{
"slotId": "2",
"objects": [
{
"id": "challenge-2",
"visible": true
}
]
},
{
"slotId": "3",
"objects": [
{
"id": "challenge-1",
"visible": true
}
]
},
{
"slotId": "4",
"objects": [
{
"id": "challenge-all",
"visible": true
}
]
}
]
}
slotId
(number) determines the order the challenges will be shown in, subject to other criteria being met.
The objects
property is an array consisting of a single object:
The id
(string) property must match the packageId
of the challenge.
The visible
property determines if the challenge is eligible to be displayed in the menu or not.
key-value-config/id/theme.json
Determines which of the seasonal visual themes are used in the menu, current themes are "basic"
(default), or "christmas"
. This json file is a single name value pair:
{
"theme": "christmas"
}
Menu
- https://github.com/bbc/cbeebies-get-creative-app-menu
Designed to be standalone, so make machines can be added externally via the Pick n Mix configuration files and the machine-assets package, and seasonal skins set, without having to edit or recompile the menu.
Machine-assets package
The menu requires a few assets in order to display a make machine, these are kept in an external package which can be updated independently of the menu, so that adding a new make machine will not require deploying a new version of the menu, just the machine-assets package. Files required for a make machine are:
- vo/[machine].mp3 (audio file played when machine box is opened)
- vo/[machine].ogg (audio file played when machine box is opened)
- image/jackbox-box-[machine].jpg (512 x 512 texture for machine box on factory floor)
The existing textures were created using this psd file. Open the file in Photoshop and double-click the
Mat - Default Texture
entry underLayer 1
. - image/jackbox-right-top-[machine].jpg (128 x 128 texture for machine box top on factory floor)
- image/title-[machine].png (title banner shown when box is opened)
where [machine] is the packageId of the make machine package. See the current machine-assets package for sample files.
Menu startup
Initial downloading of assets, machine-assets, menu brand packs and challenges before showing brand select.
Background download of make machines and make machine brand packs
Once the menu has loaded and sorted out everything it needs in order to startup, it will start checking to see if there are any packages (make machines and make machine brand packs) that can be downloaded and installed, and if there are, it will work out if there is space and do so. The logic used to determine all this is outlined below:
Displaying Make Machines on the Factory Floor
It is possible that make machines can be displayed on the factory floor when their associated make machine package and brand packs have not yet been downloaded and installed. This is handled by the following logic:
Challenge Logic
Describe the logic that determines whether and what challenge is offered on the factory floor
Details of all challenges are loaded when the menu first starts up. The challenges are filtered and ordered according to the order and visibility specified in the challenge custom config. The first challenge in this list that fulfills all the following criteria will be offered to the user. If no challenges fulfill all the criteria then no challenge will be offered. If a challenge has already been accepted during the current user session (a user session runs for the duration that the application is open) then no further challenges will be offered during the current user session.
- The challenge is for the current brand, or for all brands
- The challenge has not already been accepted
- The make machine associated with the challenge has had it's onboarding completed
Buddy Picker Logic
The buddy picker opens with 3 brands visible. The brands are selected depending on several criteria:
- Whether this is the first ever visit to the buddy picker since installing the app, the first visit since opening the app, or a return visit to the buddy picker in the current app session.
- The promoted and launch brands specified in the
buddy-picker-priorities
custom config. - "Friends" of the last selected brand - each brand can have 3 other brands associated with it in the
tags
property of the menu brand pack entry for each brand inavailable-packages.json
. Friends for each brand are cycled through in order to ensure they all get the same exposure.
In all cases the remaining slots on the buddy picker are filled with all the remaining brands, based on the order specified in the buddy-picker-order
config.
If there are any problems detected with the data used in this logic - missing or invalid buddy-picker-priorities
config data or missing or invalid friends, then the buddy picker will fall back to using the order specified in the buddy-picker-order
config.
First visit after installing the app:
The buddy picker will show the three brands specified in buddy-picker-priorities
custom config, with the promoted brand in the middle.
First visit after opening the app:
The buddy picker will show the promoted brand in the middle,the last used brand on the right and one of the last used brand's friends on the left. If the promoted brand is also the last brand used, then the middle slot will be taken by another of the last used brand's friends instead.
Return to buddy picker during an app session:
The last used brand will again be shown on the right, with a friend in the middle and the promoted brand on the left. Once again, if the promoted brand is the same as the last used brand then another friend will be displayed instead.
Make Machines
A make machine is an almost blank slate and can be built pretty much however the developer wishes within the confines of what is possible in the Pick n Mix environment. Source code for existing Make Machines is available, should developers wish to look at or reuse existing functionality, particular with regard to recreating similar looking UI. Instructions on how to run these can be found in the readMe of the machine repos.
- https://github.com/bbc/cbeebies-get-creative-app-magic-paint
- https://github.com/bbc/cbeebies-get-creative-app-terrific-toys
- https://github.com/bbc/cbeebies-get-creative-app-sound-doodles
- https://github.com/bbc/cbeebies-get-creative-app-play-puppets
There are a few requirements that need to be fulfilled in order to ensure smooth running within the Get Creative architecture, these require the use of two modules from this repo: system.StorageManager
and app.system.OnboardingCompleteSystem
:
Data passed from the menu to a make machine
When a make machine is opened from the menu, the object returned by gmi.experiences.getParams() is populated with information about the machine, the prand pack to use for the machine, and optionally either details of a saved creation (if opened from the gallery), or a challenge (if opened by accepting a challenge).
Brand pack info
gmi.experiences.getParams().machineData.packageId
will return the packageId of the brand pack that contains the brand assets to load and use.
const brandPackageId = gmi.experiences.getParams().machineData.packageId;
const brandAssetUrl = gmi.packages.urlFor( brandPackageId );
Loading Gallery Data
When a machine is opened from the menu gallery, gmi.experiences.getParams().creation
will contain the UID for the saved creation, this data can then be loaded via StorageManager.loadData( uid ). The machine can then use this data to recreate and display the creation. The uid should only ever be used or retrieved in this way - it is generated by the application and passed to the make machine so there should never be any need
const uid = gmi.experiences.getParams().creation;
if( uid )
{
const savedData = StorageManager.loadData( uid );
}
Displaying a challenge
When a machine is opened by accepting a challenge in the menu, gmi.packages.getParams().challenge
will be populated with an object derived from the config.json of the challenge package, as well as properties to retrieve the associated image and voiceover assets:
{
description: "[Some text describing the challenge]",
textureUrl: "[Path to the challenge's image asset]",
audioUrl: "[Path to the challenge's audio asset]"
}
Saving Gallery Data/Thumbnail
Use StorageManager class to save image used in menu gallery, and any data necessary to recreate the creation when loaded from the gallery.
StorageManager.saveItem( packageId, image64, data, id = null )
- packageId: String - the value of window.PACKAGE_ID -
[machine].[brand]
(see Storing Machine Usage, below) - image64: String - a string representing a 500 x 290 pixel base64 encoded image that will be used as the thumbnail in the menu gallery. Sample code for generating this using PIXI can be seen in the getSaveImage() method in app/CreatUtils.js.
- data: Object - an object containing data that can be used to recreate the creation when loaded from the gallery
- id: for new creations a unique id will be generated by the StorageManager. If the user is editing an existing item, then a reference to the uid passed into the make machine at startup should be stored and used here, this will then be used to overwrite the current entry saved with that id.
Passing data from Make Machine back to Menu
Exiting a make machine
The menu expects to receive back the same data it sent when opening the machine, so it can know if the user is returning from a machine, and if so, which one. Exiting a make machine should therefore be implemented by calling:
gmi.experiences.pop(gmi.experiences.getParams());
Saved data shared between Make Machine and Menu
Rewards
Awarded within make machines and menu via StorageManager.setUserDataByKey('score', score)
. Rewards go up to 100, and are then reset to 0, the score is saved just to keep track of it between menu and machines and back again, so the progress bar can be displayed correctly. The app does not keep track of a total score (i.e. how many times the progress bar has been filled), it just cycles between 0 and 100.
Storing Machine Usage
The menu will only show challenges for make machines that have had their onboarding completed. This information is set and retrieved via the OnboardingCompleteSystem. For simplicity, the current package id should always be retrievable via window.PACKAGE_ID, this should consist of a string: [machine].[brand]
where [machine is the packageId of the make machine, and [brand] is the brand slug of the current brand, e.g. magic-paint.hey-duggee
. The OnboardingCompleteSystem references window.PACKAGE_ID.
OnboardingCompleteSystem.complete()
Once on-boarding for the make machine is considered completed, then calling OnboardingCompleteSystem.complete()
will store the onboarding for the current make machine as being complete, and challenges for the current make machine will become eligible to be offered to the user.
OnboardingCompleteSystem.isComplete()
Returns whether or not the current machine's onboarding has been completed.
The system can also be used to store any other arbitrary value as "complete", just pass any string in (apart from a machine packageId obviously!) e.g. OnboardingCompleteSystem.complete('level1')
will save level1
as complete. Calling OnboardingCompleteSystem.isComplete(level1
) will now return true;
Platform specific builds
Make machine package sizes can be reduced by supplying separate builds for ios and android, and stripping out any assets not used by that platform - e.g. ogg or mp3 files depending on the approach used to play audio. For example we use mp3 for all vo, and either ogg or mp3 for all other audio, depending on whether running on android or ios - see tools/compressPlatformBuilds.js
for an example of duplicating and modifying the builds in this way before zipping them up separately.
Shared code
This repository also contains a lot of shared code that Goodboy used across the menu and the 4 x Make Machines produced in house. This code is supplied as is, and may provide a useful reference if trying to recreate similar UI elements or layouts. The bulk of the generic UI code can be found in the /app/screens, /app/overlay, /header and /ui folders, look in constructors for initial setup and in resize() methods for layout code. The /tutorials folder contains the poiting hands used in onboarding.
WheelMenu
Update: The wheel menu has been updated for improved handling of multitouch and launching of control/notification center on ios, update the @uap-external/get-creative-create-app-shared package to 1.2.3 to receive these changes, there should be no changes required to client code.
constructor( menuData )
A WheelMenu displays one or more linked WheelSlices, which display items in a circular loop that can be spun to see more items. Content and navigation is defined by menuData:
const menuData = {
"main": [ // the id for this menu slice
{
"id": "tape", // required - unique id for this element
"label": "tape", // optional
"icon": "icon_tape.png", // texture for icon
"vo": "menu-tape", // optional - might want to play some vo when an item is selected
"link": { // optional - denotes that this item will navigate to another slice when tapped
"type": "menu", // currently the only link type
"id": "tape" // id of the slice to navigate
}
},
{
"id": "blob",
"label": "blob",
"icon": "icon_pattern.png",
"vo": "menu-blob",
"link": {
"type": "menu",
"id": "blob"
}
},
{
"id": "marker",
"label": "marker",
"icon": "icon_pen.png",
"vo": "menu-marker",
"link": {
"type": "menu",
"id": "marker"
}
},
{
"id": "erasers",
"label": "erasers",
"icon": "icon_eraser.png",
"vo": "menu-eraser",
"link": {
"type": "menu",
"id": "eraser"
}
}
],
"eraser": [
{
"id": "eraser-thin",
"label": "thin eraser",
"icon": "icon_eraser.png",
"type": "eraser",
"draggable": true, // optional - means an item can be dragged from the wheel
"thickness": 0.5 // add any other arbitrary data that might be useful to receive when the item is selected
},
{
"id": "eraser-med",
"label": "eraser",
"icon": "icon_eraser.png",
"type": "eraser",
"thickness": 1
},
{
"id": "eraser-thick",
"label": "thick eraser",
"icon": "icon_eraser.png",
"type": "eraser",
"thickness": 2
}
],
//...
//... blob & marker slices would need defining here...
//...
}
const wheelMenu = new WheelMenu( menuData );
home()
Shows the first menu slice
showSlice( id )
Shows the menu slice with the specified id
popMenu()
Shows the previous menu slice
spin( speed = 200 )
Spins the wheel with initial velocity of speed
selectLastOrDefault()
Selects (highlights) the last selected item on the current menu slice, or the first item
hide( snap = false )
Hides the wheel, instantly if snap set to true.
show( snap = false )
Shows the wheel, instantly if snap set to true.
highlight( id, cb, incorrectCb, allowIncorrect=false, lockWheel=true, hideBack=true )
Prompts user to select the menu item with the specified id. If null
is passed as the id, a generic prompt to select any item from the wheel is shown.
Executes cb() when the item is selected.
Executes incorrectCb() if an item other than that specified is selected.
allowIncorrect: set to true to allow another item to be selected, otherwise incorrect selections are ignored.
lockWheel: set to false to allow wheel to be spinnable whilst item is being highlighted
hideBack: set to false to enable the back button to be used whilst item is being highlighted
highlightBack( cb, incorrectCb )
Prompts user to tap the back button. Executes cb() when the item is selectedback button is tapped. Executes incorrectCb() if another item is selected.
signal : onItemSelected
Dispatches when an item is selected, handler is called with arguments ( data, dragEvent, element ) - data is the data object for the item, dragEvent will be present if the item was dragged off the wheel.
signal : onSliceChanged
Dispatches when the menu slice changes, handler is called with argument ( userTriggered ), set to true if the change is a direct result of user interaction with the wheel.
resize( w, h )
Should be called whenever the application resizes, where w & h are the width and height of the canvas
Accessibility
Note that when the app detects that accessibility should be enabled via gmi, the app will behave as if the Motion setting has been disabled - this is to prevent unwanted changes in focus due to changes in the position of accessible buttons. The actual motion setting will in the settings menu will not be affected by this, so when accessibility mode is disabled, the app will maintain whatever setting for Motion has been explicitly selected in the Settings menu.