Adding palette/colors/storyboard packages (#898)

* modify scene palette

* add colors/palette projexts

* fix game pxt.json

* reomve scenes

* hide new color blocks

* refactor color buffer

* more refactoring

* support for ARGB colors

* avoid name clashing

* fix sig

* split files

* simplify palette

* adding weight

* refactor default palette

* adding palette test

* updated test

* add storyboard library

* avoid name clash

* fix namespace

* fix extension name

* fix fade

* fix colorbuffer

* fix build issues

* avoid dup color

* remove blocks

* fix typing issue

* added test

* more storyboard work

* reset palette

* scene cleanup, remove dep on palette

* add default loader

* update blocks

* pop/push/replace

* fix pop bug

* remove microsoft animation

* fix test

* handle scene push

* pr feedback
This commit is contained in:
Peli de Halleux 2019-07-25 18:20:26 -07:00 коммит произвёл GitHub
Родитель ed37343be4
Коммит 4cb8e86a55
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 754 добавлений и 183 удалений

Просмотреть файл

@ -1,9 +1,9 @@
export interface Secrets {
interface Secrets {
connString: string;
wifi: pxt.StringMap;
}
// this is to be overridden in a separate file
export let secrets: Secrets;
let secrets: Secrets;
function test() {
const log = console.log;

3
libs/color/README.md Normal file
Просмотреть файл

@ -0,0 +1,3 @@
# Colors
Color manipulation

100
libs/color/colorbuffer.ts Normal file
Просмотреть файл

@ -0,0 +1,100 @@
namespace color {
export enum ColorBufferLayout {
/**
* 24bit RGB color
*/
RGB,
/**
* 32bit RGB color with alpha
*/
ARGB
}
/**
* A buffer of colors
*/
export class ColorBuffer {
layout: ColorBufferLayout;
buf: Buffer;
constructor(buf: Buffer, layout?: ColorBufferLayout) {
this.buf = buf;
this.layout = layout || ColorBufferLayout.RGB;
}
get stride() {
return this.layout == ColorBufferLayout.RGB ? 3 : 4;
}
get length() {
return Math.idiv(this.buf.length, this.stride);
}
color(index: number): number {
index = index | 0;
if (index < 0 || index >= this.length) return -1;
const s = this.stride;
const start = index * s;
let c = 0;
for (let i = 0; i < s; ++i)
c = (c << 8) | (this.buf[start + i] & 0xff);
return c;
}
setColor(index: number, color: number) {
index = index | 0;
if (index < 0 || index >= this.length) return;
const s = this.stride;
const start = index * s;
for (let i = s - 1; i >= 0; --i) {
this.buf[start + i] = color & 0xff;
color = color >> 8;
}
}
slice(start?: number, length?: number): ColorBuffer {
const s = this.stride;
return new ColorBuffer(this.buf.slice(start ? start * s : start, length ? length * s : length));
}
/**
* Writes the content of the src color buffer starting at the start dstOffset in the current buffer
* @param dstOffset
* @param src
*/
write(dstOffset: number, src: ColorBuffer): void {
if (this.layout == src.layout) {
const d = (dstOffset | 0) * this.stride;
this.buf.write(d, src.buf);
} else {
// different color layout
const n = Math.min(src.length, this.length - dstOffset);
for (let i = 0; i < n; ++i)
this.setColor(dstOffset + i, src.color(i));
}
}
}
/**
* Converts an array of colors into a color buffer
*/
export function createBuffer(colors: number[], layout?: ColorBufferLayout): color.ColorBuffer {
const n = colors.length;
layout = layout || ColorBufferLayout.RGB;
const stride = layout == ColorBufferLayout.RGB ? 3 : 4;
const buf = control.createBuffer(n * stride);
const p = new ColorBuffer(buf);
let k = 0;
for (let i = 0; i < n; i++) {
let color = colors[i];
for (let j = stride - 1; j >= 0; --j) {
p.buf[k + j] = color & 0xff;
color = color >> 8;
}
k += stride;
}
return p;
}
}

213
libs/color/colors.ts Normal file
Просмотреть файл

@ -0,0 +1,213 @@
/**
* Well known colors
*/
const enum Colors {
//% block=red
Red = 0xFF0000,
//% block=orange
Orange = 0xFF7F00,
//% block=yellow
Yellow = 0xFFFF00,
//% block=green
Green = 0x00FF00,
//% block=blue
Blue = 0x0000FF,
//% block=indigo
Indigo = 0x4b0082,
//% block=violet
Violet = 0x8a2be2,
//% block=purple
Purple = 0xA033E5,
//% block=pink
Pink = 0xFF007F,
//% block=white
White = 0xFFFFFF,
//% block=black
Black = 0x000000
}
/**
* Well known color hues
*/
const enum ColorHues {
//% block=red
Red = 0,
//% block=orange
Orange = 29,
//% block=yellow
Yellow = 43,
//% block=green
Green = 86,
//% block=aqua
Aqua = 125,
//% block=blue
Blue = 170,
//% block=purple
Purple = 191,
//% block=magenta
Magenta = 213,
//% block=pink
Pink = 234
}
/**
* Color manipulation
*/
//% advanced=1
namespace color {
/**
* Converts red, green, blue channels into a RGB color
* @param red value of the red channel between 0 and 255. eg: 255
* @param green value of the green channel between 0 and 255. eg: 255
* @param blue value of the blue channel between 0 and 255. eg: 255
*/
//% blockId="colorsrgb" block="red %red|green %green|blue %blue"
//% red.min=0 red.max=255 green.min=0 green.max=255 blue.min=0 blue.max=255
//% help="colors/rgb"
//% weight=19 blockGap=8
//% blockHidden=true
export function rgb(red: number, green: number, blue: number): number {
return ((red & 0xFF) << 16) | ((green & 0xFF) << 8) | (blue & 0xFF);
}
export function argb(alpha: number, red: number, green: number, blue: number): number {
return ((alpha & 0xFF) << 24) | ((red & 0xFF) << 16) | ((green & 0xFF) << 8) | (blue & 0xFF);
}
/**
* Get the RGB value of a known color
*/
//% blockId=colorscolors block="%color"
//% help="colors/well-known"
//% shim=TD_ID
//% weight=20 blockGap=8
//% blockHidden=true
export function wellKnown(color: Colors): number {
return color;
}
/**
* Convert an HSV (hue, saturation, value) color to RGB
* @param hue value of the hue channel between 0 and 255. eg: 255
* @param sat value of the saturation channel between 0 and 255. eg: 255
* @param val value of the value channel between 0 and 255. eg: 255
*/
//% blockId="colorshsv" block="hue %hue|sat %sat|val %val"
//% hue.min=0 hue.max=255 sat.min=0 sat.max=255 val.min=0 val.max=255
//% help="colors/hsv"
//% weight=17
//% blockHidden=true
export function hsv(hue: number, sat: number = 255, val: number = 255): number {
let h = (hue % 255) >> 0;
if (h < 0) h += 255;
// scale down to 0..192
h = (h * 192 / 255) >> 0;
//reference: based on FastLED's hsv2rgb rainbow algorithm [https://github.com/FastLED/FastLED](MIT)
let invsat = 255 - sat;
let brightness_floor = ((val * invsat) / 255) >> 0;
let color_amplitude = val - brightness_floor;
let section = (h / 0x40) >> 0; // [0..2]
let offset = (h % 0x40) >> 0; // [0..63]
let rampup = offset;
let rampdown = (0x40 - 1) - offset;
let rampup_amp_adj = ((rampup * color_amplitude) / (255 / 4)) >> 0;
let rampdown_amp_adj = ((rampdown * color_amplitude) / (255 / 4)) >> 0;
let rampup_adj_with_floor = (rampup_amp_adj + brightness_floor);
let rampdown_adj_with_floor = (rampdown_amp_adj + brightness_floor);
let r: number;
let g: number;
let b: number;
if (section) {
if (section == 1) {
// section 1: 0x40..0x7F
r = brightness_floor;
g = rampdown_adj_with_floor;
b = rampup_adj_with_floor;
} else {
// section 2; 0x80..0xBF
r = rampup_adj_with_floor;
g = brightness_floor;
b = rampdown_adj_with_floor;
}
} else {
// section 0: 0x00..0x3F
r = rampdown_adj_with_floor;
g = rampup_adj_with_floor;
b = brightness_floor;
}
return rgb(r, g, b);
}
/**
* Fade the color by the brightness
* @param color color to fade
* @param brightness the amount of brightness to apply to the color, eg: 128
*/
//% blockId="colorsfade" block="fade %color=neopixel_colors|by %brightness"
//% brightness.min=0 brightness.max=255
//% help="light/fade"
//% group="Color" weight=18 blockGap=8
//% blockHidden=true
export function fade(color: number, brightness: number): number {
brightness = Math.max(0, Math.min(255, brightness >> 0));
if (brightness < 255) {
let red = unpackR(color);
let green = unpackG(color);
let blue = unpackB(color);
red = (red * brightness) >> 8;
green = (green * brightness) >> 8;
blue = (blue * brightness) >> 8;
color = rgb(red, green, blue);
}
return color;
}
export function unpackR(rgb: number): number {
return (rgb >> 16) & 0xFF;
}
export function unpackG(rgb: number): number {
return (rgb >> 8) & 0xFF;
}
export function unpackB(rgb: number): number {
return (rgb >> 0) & 0xFF;
}
export function parseColor(color: string): number {
switch (color) {
case "RED":
case "red":
return Colors.Red;
case "GREEN":
case "green":
return Colors.Green;
case "BLUE":
case "blue":
return Colors.Blue;
case "WHITE":
case "white":
return Colors.White;
case "ORANGE":
case "orange":
return Colors.Orange;
case "PURPLE":
case "purple":
return Colors.Purple;
case "YELLOW":
case "yellow":
return Colors.Yellow;
case "PINK":
case "pink":
return Colors.Pink;
default:
return parseInt(color) || 0;
}
}
}

14
libs/color/pxt.json Normal file
Просмотреть файл

@ -0,0 +1,14 @@
{
"name": "color",
"description": "Color manipulation",
"files": [
"colors.ts",
"colorbuffer.ts",
"README.md"
],
"public": true,
"weight": 1,
"dependencies": {
"core": "file:../core"
}
}

Просмотреть файл

@ -328,7 +328,8 @@ namespace game {
*/
export function addScenePushHandler(handler: (oldScene: scene.Scene) => void) {
if (!_scenePushHandlers) _scenePushHandlers = [];
_scenePushHandlers.push(handler);
if (_scenePushHandlers.indexOf(handler) < 0)
_scenePushHandlers.push(handler);
}
/**
@ -351,7 +352,8 @@ namespace game {
*/
export function addScenePopHandler(handler: (oldScene: scene.Scene) => void) {
if (!_scenePopHandlers) _scenePopHandlers = [];
_scenePopHandlers.push(handler);
if (_scenePopHandlers.indexOf(handler) < 0)
_scenePopHandlers.push(handler);
}
/**

Просмотреть файл

@ -1,55 +1,3 @@
/**
* Well known colors
*/
const enum Colors {
//% block=red
Red = 0xFF0000,
//% block=orange
Orange = 0xFF7F00,
//% block=yellow
Yellow = 0xFFFF00,
//% block=green
Green = 0x00FF00,
//% block=blue
Blue = 0x0000FF,
//% block=indigo
Indigo = 0x4b0082,
//% block=violet
Violet = 0x8a2be2,
//% block=purple
Purple = 0xA033E5,
//% block=pink
Pink = 0xFF007F,
//% block=white
White = 0xFFFFFF,
//% block=black
Black = 0x000000
}
/**
* Well known color hues
*/
const enum ColorHues {
//% block=red
Red = 0,
//% block=orange
Orange = 29,
//% block=yellow
Yellow = 43,
//% block=green
Green = 86,
//% block=aqua
Aqua = 125,
//% block=blue
Blue = 170,
//% block=purple
Purple = 191,
//% block=magenta
Magenta = 213,
//% block=pink
Pink = 234
}
/**
* Different modes for RGB or RGB+W NeoPixel strips
*/
@ -195,9 +143,9 @@ namespace light {
//% advanced=true
setAll(rgb: number) {
rgb = rgb | 0;
const red = unpackR(rgb);
const green = unpackG(rgb);
const blue = unpackB(rgb);
const red = color.unpackR(rgb);
const green = color.unpackG(rgb);
const blue = color.unpackB(rgb);
const end = this._start + this._length;
const stride = this.stride();
@ -216,12 +164,12 @@ namespace light {
//% weight=79 blockGap=8
//% group="More" advanced=true blockHidden=true
setGradient(startColor: number, endColor: number, easing?: (t: number) => number) {
const sr = unpackR(startColor);
const sg = unpackG(startColor);
const sb = unpackB(startColor);
const er = unpackR(endColor);
const eg = unpackG(endColor);
const eb = unpackB(endColor);
const sr = color.unpackR(startColor);
const sg = color.unpackG(startColor);
const sb = color.unpackB(startColor);
const er = color.unpackR(endColor);
const eg = color.unpackG(endColor);
const eb = color.unpackB(endColor);
const end = this._start + this._length;
const n1 = this._length - 1;
@ -299,9 +247,9 @@ namespace light {
//% help="light/neopixelstrip/set-pixel-color"
//% weight=79 blockGap=8
//% group="More" advanced=true
setPixelColor(pixeloffset: number, color: number): void {
setPixelColor(pixeloffset: number, c: number): void {
pixeloffset = pixeloffset | 0;
color = color | 0;
c = c | 0;
if (pixeloffset < 0
|| pixeloffset >= this._length)
@ -309,9 +257,9 @@ namespace light {
const stride = this.stride();
pixeloffset = (pixeloffset + this._start) * stride;
const red = unpackR(color);
const green = unpackG(color);
const blue = unpackB(color);
const red = color.unpackR(c);
const green = color.unpackG(c);
const blue = color.unpackB(c);
this.setBufferRGB(pixeloffset, red, green, blue)
this.autoShow();
}
@ -353,7 +301,7 @@ namespace light {
break;
}
return rgb(red, green, blue);
return color.rgb(red, green, blue);
}
/**
@ -663,7 +611,7 @@ namespace light {
//% group="Photon" advanced=true
setPhotonPenHue(hue: number) {
hue = hue | 0;
this.setPhotonPenColor(hsv(hue, 0xff, 0xff));
this.setPhotonPenColor(color.hsv(hue, 0xff, 0xff));
}
//% deprecated=1 blockHidden=1
@ -821,7 +769,7 @@ namespace light {
tempColor += currChar;
if ((isSpace || i == leds.length) && tempColor) {
this.setPixelColor(pi++, parseColor(tempColor))
this.setPixelColor(pi++, color.parseColor(tempColor))
tempColor = "";
if (pi == n) {
this.show();
@ -1238,7 +1186,7 @@ namespace light {
//% help="light/rgb"
//% group="Color" weight=19 blockGap=8
export function rgb(red: number, green: number, blue: number): number {
return ((red & 0xFF) << 16) | ((green & 0xFF) << 8) | (blue & 0xFF);
return color.rgb(red, green, blue);
}
/**
@ -1252,19 +1200,6 @@ namespace light {
return color;
}
function unpackR(rgb: number): number {
let r = (rgb >> 16) & 0xFF;
return r;
}
function unpackG(rgb: number): number {
let g = (rgb >> 8) & 0xFF;
return g;
}
function unpackB(rgb: number): number {
let b = (rgb >> 0) & 0xFF;
return b;
}
/**
* Convert an HSV (hue, saturation, value) color to RGB
* @param hue value of the hue channel between 0 and 255. eg: 255
@ -1277,53 +1212,11 @@ namespace light {
//% help="light/hsv"
//% group="Color" weight=17
export function hsv(hue: number, sat: number = 255, val: number = 255): number {
let h = (hue % 255) >> 0;
if (h < 0) h += 255;
// scale down to 0..192
h = (h * 192 / 255) >> 0;
//reference: based on FastLED's hsv2rgb rainbow algorithm [https://github.com/FastLED/FastLED](MIT)
let invsat = 255 - sat;
let brightness_floor = ((val * invsat) / 255) >> 0;
let color_amplitude = val - brightness_floor;
let section = (h / 0x40) >> 0; // [0..2]
let offset = (h % 0x40) >> 0; // [0..63]
let rampup = offset;
let rampdown = (0x40 - 1) - offset;
let rampup_amp_adj = ((rampup * color_amplitude) / (255 / 4)) >> 0;
let rampdown_amp_adj = ((rampdown * color_amplitude) / (255 / 4)) >> 0;
let rampup_adj_with_floor = (rampup_amp_adj + brightness_floor);
let rampdown_adj_with_floor = (rampdown_amp_adj + brightness_floor);
let r: number;
let g: number;
let b: number;
if (section) {
if (section == 1) {
// section 1: 0x40..0x7F
r = brightness_floor;
g = rampdown_adj_with_floor;
b = rampup_adj_with_floor;
} else {
// section 2; 0x80..0xBF
r = rampup_adj_with_floor;
g = brightness_floor;
b = rampdown_adj_with_floor;
}
} else {
// section 0: 0x00..0x3F
r = rampdown_adj_with_floor;
g = rampup_adj_with_floor;
b = brightness_floor;
}
return rgb(r, g, b);
return color.hsv(hue, sat, val);
}
/**
* Fade the color by the brightness
* Use color.fade instead
* @param color color to fade
* @param brightness the amount of brightness to apply to the color, eg: 128
*/
@ -1331,52 +1224,9 @@ namespace light {
//% brightness.min=0 brightness.max=255
//% help="light/fade"
//% group="Color" weight=18 blockGap=8
//% blockHidden=true
export function fade(color: number, brightness: number): number {
brightness = Math.max(0, Math.min(255, brightness >> 0));
if (brightness < 255) {
let red = unpackR(color);
let green = unpackG(color);
let blue = unpackB(color);
red = (red * brightness) >> 8;
green = (green * brightness) >> 8;
blue = (blue * brightness) >> 8;
color = rgb(red, green, blue);
}
return color;
}
function parseColor(color: string) {
switch (color) {
case "RED":
case "red":
return Colors.Red;
case "GREEN":
case "green":
return Colors.Green;
case "BLUE":
case "blue":
return Colors.Blue;
case "WHITE":
case "white":
return Colors.White;
case "ORANGE":
case "orange":
return Colors.Orange;
case "PURPLE":
case "purple":
return Colors.Purple;
case "YELLOW":
case "yellow":
return Colors.Yellow;
case "PINK":
case "pink":
return Colors.Pink;
default:
return parseInt(color) || 0;
}
//% blockHidden=true deprecated
export function fade(c: number, brightness: number): number {
return color.fade(c, brightness);
}
/**
@ -1407,7 +1257,7 @@ namespace light {
let hueOffset = 0;
return () => {
for (let i = 0; i < n; i++) {
strip.setPixelColor(i, hsv(((i * 256) / (n - 1) + hueOffset) % 0xff, 0xff, 0xff));
strip.setPixelColor(i, color.hsv(((i * 256) / (n - 1) + hueOffset) % 0xff, 0xff, 0xff));
}
hueOffset += Math.ceil(128 / n);
if (hueOffset >= 0xff) {
@ -1447,7 +1297,7 @@ namespace light {
step++;
for (let i = 0; i < l; i++) {
const level = (Math.isin(i + step) * 127) + 128;
strip.setPixelColor(i, rgb(level * this.red / 255, level * this.green / 255, level * this.blue / 255));
strip.setPixelColor(i, color.rgb(level * this.red / 255, level * this.green / 255, level * this.blue / 255));
}
iteration++;
return true;
@ -1489,7 +1339,7 @@ namespace light {
return () => {
for (let i = 0; i < l; i++) {
offsets[i] = (offsets[i] + (step * 2)) % 255
strip.setPixelColor(i, rgb(255 - offsets[i], this.green, this.blue));
strip.setPixelColor(i, color.rgb(255 - offsets[i], this.green, this.blue));
}
step++;
if (step * 2 > 0xff) {
@ -1510,7 +1360,7 @@ namespace light {
constructor(red: number, green: number, blue: number, delay: number) {
super();
this.rgb = rgb(red, green, blue);
this.rgb = color.rgb(red, green, blue);
this.delay = delay;
}
@ -1587,7 +1437,7 @@ namespace light {
constructor(red: number, green: number, blue: number, delay: number) {
super();
this.rgb = rgb(red, green, blue);
this.rgb = color.rgb(red, green, blue);
this.delay = delay;
}

Просмотреть файл

@ -22,6 +22,7 @@
"public": true,
"dependencies": {
"core": "file:../core",
"color": "file:../color",
"jacdac": "file:../jacdac"
}
}

3
libs/palette/README.md Normal file
Просмотреть файл

@ -0,0 +1,3 @@
# Palette
Helpers to manipulate palette in games.

60
libs/palette/palette.ts Normal file
Просмотреть файл

@ -0,0 +1,60 @@
/**
* Update the current scene palette
*/
namespace palette {
/**
* The default palette buffer for the project
*/
//% whenUsed
const defaultPaletteBuffer = hex`__palette`
/**
* Returns a clone of the default palette
*/
export function defaultPalette(): color.ColorBuffer {
return new color.ColorBuffer(defaultPaletteBuffer.slice());
}
const FIELD = "__palette";
/**
* Dynamically set all or part of the game's current palette
*
* @param palette The colors to set
* @param pOffset The offset to start copying from the palette
*/
export function setColors(palette: color.ColorBuffer, pOffset = 0) {
const scene = game.currentScene();
let userPalette = scene.data[FIELD] as color.ColorBuffer;
if (!userPalette)
userPalette = scene.data[FIELD] = defaultPalette();
userPalette.write(pOffset, palette);
image.setPalette(userPalette.buf);
// make sure to clean up
game.addScenePushHandler(scenePush);
game.addScenePopHandler(scenePop);
}
function scenePush(scene: scene.Scene) {
if (scene.data[FIELD]) {
const userPalette = scene.data[FIELD] as color.ColorBuffer;
image.setPalette(userPalette.buf);
}
}
function scenePop(scene: scene.Scene) {
if (scene.data[FIELD]) {
scene.data[FIELD] = undefined;
image.setPalette(defaultPaletteBuffer);
}
}
/**
* Reset to default palette
*/
export function reset() {
const scene = game.currentScene();
scene.data[FIELD] = undefined;
image.setPalette(defaultPaletteBuffer);
}
}

17
libs/palette/pxt.json Normal file
Просмотреть файл

@ -0,0 +1,17 @@
{
"name": "palette",
"description": "Palette manipulations",
"files": [
"palette.ts",
"README.md"
],
"testFiles": [
"test.ts"
],
"public": true,
"weight": 10,
"dependencies": {
"color": "file:../color",
"game": "file:../game"
}
}

21
libs/palette/test.ts Normal file
Просмотреть файл

@ -0,0 +1,21 @@
let mySprite = sprites.create(img`
0 1 2 3
4 5 6 7
8 9 a b
c d e f
`.doubled().doubled().doubled().doubled(), SpriteKind.Player)
controller.A.onEvent(ControllerButtonEvent.Pressed, function () {
const p = palette.defaultPalette();
for (let i = 0; i < p.length; ++i) {
p.setColor(i, color.rgb(i * 16, 0, 255 - i * 16));
}
p.setColor(0, 0)
palette.setColors(p)
})
controller.B.onEvent(ControllerButtonEvent.Pressed, function () {
palette.reset()
})

Просмотреть файл

@ -0,0 +1,3 @@
# Storyboard
Orchestrate scenes

24
libs/storyboard/loader.ts Normal file
Просмотреть файл

@ -0,0 +1,24 @@
namespace storyboard {
function loader(done: () => void) {
const font = image.font8;
let m = 40;
let w = screen.width - 2 * m;
let c = 2;
let y = screen.height / 2 - c;
let x = 0;
game.onPaint(function() {
screen.printCenter("MakeCode Arcade", y - font.charHeight - c, 1, font);
screen.drawRect(m, y, w, 2 * c, 1)
screen.fillRect(m, y + 1, x, 2 * c - 2, 3);
x++;
if (x == w) done();
})
}
/**
* Default boot sequence
*/
//% block="loader" fixedInstance whenUsed
export const loaderBootSequence = new BootSequence(loader, 0);
}

17
libs/storyboard/pxt.json Normal file
Просмотреть файл

@ -0,0 +1,17 @@
{
"name": "storyboard",
"description": "Scene manager",
"files": [
"storyboard.ts",
"loader.ts",
"README.md"
],
"testFiles": [
"test.ts"
],
"public": true,
"weight": 10,
"dependencies": {
"game": "file:../game"
}
}

Просмотреть файл

@ -0,0 +1,156 @@
/**
* A manager of scenes
*/
//% icon="\uf009"
//% weight=87 color="#401255"
namespace storyboard {
export interface FrameOptions {
background?: number;
}
export class Frame {
start: () => void;
options: FrameOptions;
constructor(start: () => void, options: FrameOptions) {
this.start = start;
this.options = options || {};
}
}
//% fixedInstances
export class BootSequence {
start: (done: () => void) => void;
background: number;
constructor(start: (done: () => void) => void, background: number) {
this.start = start;
this.background = background;
}
/**
* Registers the boot sequence
*/
//% block="storyboard register %boot| boot sequence" blockId=storyboardregister
register() {
registerBootSequence(this);
}
}
let _boots: BootSequence[];
let _scenes: {
[index: string]: Frame
};
let _nav: Frame[];
function registerBootSequence(boot: BootSequence) {
if (!_boots)
_boots = [];
if (_boots.indexOf(boot) < 0)
_boots.push(boot);
}
/**
* Registers a scene
* @param name
* @param scene
*/
export function registerScene(name: string, start: () => void, options?: FrameOptions) {
if (!name) return;
if (!_scenes) {
_scenes = {};
}
_scenes[name] = new Frame(start, options);
}
function consumeBootSequence() {
// run boot sequences if any
let boot: BootSequence;
while (boot = _boots && _boots.shift()) {
game.pushScene();
let isDone = false;
boot.start(() => isDone = true);
pauseUntil(() => isDone);
game.popScene();
}
}
/**
* Starts the story board by running boot sequences then entering a scene
* @param name
*/
//% block="storyboard start at $name" blockId=storyboardstart
export function start(name?: string) {
consumeBootSequence();
// grab the first frame
push(name || (_scenes && Object.keys(_scenes)[0]));
}
function isActive(name: string): boolean {
const scene = name && _scenes && _scenes[name];
return scene && (_nav && _nav.length && _nav[_nav.length - 1] == scene);
}
/**
* Replace the current scene with the given scene
* @param name
*/
//% block="storyboard replace scene $name" blockId=storyboardreplace
export function replace(name: string) {
if (isActive(name)) return;
const scene = name && _scenes && _scenes[name];
if (!scene) return; // not found
if (!_nav) _nav = [];
if (_nav.length) {
console.log('drop current scene')
_nav.pop();
game.popScene();
}
console.log('replace scene')
_nav.push(scene);
game.pushScene();
scene.start();
}
/**
* Transition to a registered scene
* @param name
*/
//% block="storyboard push scene $name" blockId=storyboardpush
export function push(name: string) {
if (isActive(name)) return;
const scene = name && _scenes && _scenes[name];
if (!scene) return; // not found
if (!_nav) _nav = [];
if (_nav.length) {
console.log('drop scene')
game.popScene();
}
console.log(`push ${name}`)
_nav.push(scene);
game.pushScene();
scene.start();
}
/**
* Stops the current scene and restart the previous scene
*/
//% block="storyboard pop frame" blockId=storyboardpop
export function pop() {
const n = _nav && _nav.pop();
if (n) {
console.log('pop scene')
game.popScene();
}
// restart previous
if (_nav && _nav.length) {
console.log('restart scene')
const sc = _nav[_nav.length - 1];
game.pushScene();
sc.start();
}
}
}

84
libs/storyboard/test.ts Normal file
Просмотреть файл

@ -0,0 +1,84 @@
let mySprite = sprites.create(img`
. . . . . . b b b b a a . . . .
. . . . b b d d d 3 3 3 a a . .
. . . b d d d 3 3 3 3 3 3 a a .
. . b d d 3 3 3 3 3 3 3 3 3 a .
. b 3 d 3 3 3 3 3 b 3 3 3 3 a b
. b 3 3 3 3 3 a a 3 3 3 3 3 a b
b 3 3 3 3 3 a a 3 3 3 3 d a 4 b
b 3 3 3 3 b a 3 3 3 3 3 d a 4 b
b 3 3 3 3 3 3 3 3 3 3 d a 4 4 e
a 3 3 3 3 3 3 3 3 3 d a 4 4 4 e
a 3 3 3 3 3 3 3 d d a 4 4 4 e .
a a 3 3 3 d d d a a 4 4 4 e e .
. e a a a a a a 4 4 4 4 e e . .
. . e e b b 4 4 4 4 b e e . . .
. . . e e e e e e e e . . . . .
. . . . . . . . . . . . . . . .
`, SpriteKind.Player)
mySprite.x = 10
controller.moveSprite(mySprite)
storyboard.loaderBootSequence.register()
storyboard.registerScene("lemon", function () {
let mySprite2 = sprites.create(img`
4 4 4 . . 4 4 4 4 4 . . . . . .
4 5 5 4 4 5 5 5 5 5 4 4 . . . .
b 4 5 5 1 5 1 1 1 5 5 5 4 . . .
. b 5 5 5 5 1 1 5 5 1 1 5 4 . .
. b d 5 5 5 5 5 5 5 5 1 1 5 4 .
b 4 5 5 5 5 5 5 5 5 5 5 1 5 4 .
c d 5 5 5 5 5 5 5 5 5 5 5 5 5 4
c d 4 5 5 5 5 5 5 5 5 5 5 1 5 4
c 4 5 5 5 d 5 5 5 5 5 5 5 5 5 4
c 4 d 5 4 5 d 5 5 5 5 5 5 5 5 4
. c 4 5 5 5 5 d d d 5 5 5 5 5 b
. c 4 d 5 4 5 d 4 4 d 5 5 5 4 c
. . c 4 4 d 4 4 4 4 4 d d 5 d c
. . . c 4 4 4 4 4 4 4 4 5 5 5 4
. . . . c c b 4 4 4 b b 4 5 4 4
. . . . . . c c c c c c b b 4 .
`, SpriteKind.Player)
mySprite2.y = 20
controller.moveSprite(mySprite2)
controller.A.onEvent(ControllerButtonEvent.Pressed, function () {
storyboard.push("burger");
})
controller.B.onEvent(ControllerButtonEvent.Pressed, function () {
storyboard.pop();
})
})
storyboard.registerScene("burger", function () {
let mySprite3 = sprites.create(img`
. . . . c c c b b b b b . . . .
. . c c b 4 4 4 4 4 4 b b b . .
. c c 4 4 4 4 4 5 4 4 4 4 b c .
. e 4 4 4 4 4 4 4 4 4 5 4 4 e .
e b 4 5 4 4 5 4 4 4 4 4 4 4 b c
e b 4 4 4 4 4 4 4 4 4 4 5 4 4 e
e b b 4 4 4 4 4 4 4 4 4 4 4 b e
. e b 4 4 4 4 4 5 4 4 4 4 b e .
8 7 e e b 4 4 4 4 4 4 b e e 6 8
8 7 2 e e e e e e e e e e 2 7 8
e 6 6 2 2 2 2 2 2 2 2 2 2 6 c e
e c 6 7 6 6 7 7 7 6 6 7 6 c c e
e b e 8 8 c c 8 8 c c c 8 e b e
e e b e c c e e e e e c e b e e
. e e b b 4 4 4 4 4 4 4 4 e e .
. . . c c c c c e e e e e . . .
`, SpriteKind.Player)
mySprite3.y = 80
controller.moveSprite(mySprite3)
controller.A.onEvent(ControllerButtonEvent.Pressed, function () {
storyboard.replace("lemon");
})
controller.B.onEvent(ControllerButtonEvent.Pressed, function () {
storyboard.pop()
})
})
controller.A.onEvent(ControllerButtonEvent.Pressed, function () {
storyboard.start("lemon")
})
controller.B.onEvent(ControllerButtonEvent.Pressed, function () {
storyboard.start("burger")
})

Просмотреть файл

@ -45,6 +45,9 @@
"libs/esp32spi",
"libs/net",
"libs/mqtt",
"libs/azureiot"
"libs/azureiot",
"libs/color",
"libs/game",
"libs/storyboard"
]
}