mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-03-13 17:04:39 -04:00
Merge pull request #1148 from kchadha/stage-layering
Layer group ordering.
This commit is contained in:
commit
a85f641d25
11 changed files with 79 additions and 40 deletions
|
@ -2,6 +2,7 @@ const Cast = require('../util/cast');
|
|||
const Clone = require('../util/clone');
|
||||
const RenderedTarget = require('../sprites/rendered-target');
|
||||
const uid = require('../util/uid');
|
||||
const StageLayering = require('../engine/stage-layering');
|
||||
|
||||
/**
|
||||
* @typedef {object} BubbleState - the bubble state associated with a particular target.
|
||||
|
@ -91,7 +92,7 @@ class Scratch3LooksBlocks {
|
|||
_onTargetWillExit (target) {
|
||||
const bubbleState = this._getBubbleState(target);
|
||||
if (bubbleState.drawableId && bubbleState.skinId) {
|
||||
this.runtime.renderer.destroyDrawable(bubbleState.drawableId);
|
||||
this.runtime.renderer.destroyDrawable(bubbleState.drawableId, StageLayering.SPRITE_LAYER);
|
||||
this.runtime.renderer.destroySkin(bubbleState.skinId);
|
||||
bubbleState.drawableId = null;
|
||||
bubbleState.skinId = null;
|
||||
|
@ -195,10 +196,9 @@ class Scratch3LooksBlocks {
|
|||
bubbleState.onSpriteRight = false;
|
||||
}
|
||||
|
||||
bubbleState.drawableId = this.runtime.renderer.createDrawable();
|
||||
bubbleState.drawableId = this.runtime.renderer.createDrawable(StageLayering.SPRITE_LAYER);
|
||||
bubbleState.skinId = this.runtime.renderer.createTextSkin(type, text, bubbleState.onSpriteRight, [0, 0]);
|
||||
|
||||
this.runtime.renderer.setDrawableOrder(bubbleState.drawableId, Infinity);
|
||||
this.runtime.renderer.updateDrawableProperties(bubbleState.drawableId, {
|
||||
skinId: bubbleState.skinId
|
||||
});
|
||||
|
|
|
@ -12,6 +12,7 @@ const TargetType = require('../extension-support/target-type');
|
|||
const Thread = require('./thread');
|
||||
const log = require('../util/log');
|
||||
const maybeFormatMessage = require('../util/maybe-format-message');
|
||||
const StageLayering = require('./stage-layering');
|
||||
|
||||
// Virtual I/O devices.
|
||||
const Clock = require('../io/clock');
|
||||
|
@ -919,6 +920,7 @@ class Runtime extends EventEmitter {
|
|||
*/
|
||||
attachRenderer (renderer) {
|
||||
this.renderer = renderer;
|
||||
this.renderer.setLayerGroupOrdering(StageLayering.LAYER_GROUPS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
29
src/engine/stage-layering.js
Normal file
29
src/engine/stage-layering.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
class StageLayering {
|
||||
static get BACKGROUND_LAYER () {
|
||||
return 'background';
|
||||
}
|
||||
|
||||
static get VIDEO_LAYER () {
|
||||
return 'video';
|
||||
}
|
||||
|
||||
static get PEN_LAYER () {
|
||||
return 'pen';
|
||||
}
|
||||
|
||||
static get SPRITE_LAYER () {
|
||||
return 'sprite';
|
||||
}
|
||||
|
||||
// Order of layer groups relative to each other,
|
||||
static get LAYER_GROUPS () {
|
||||
return [
|
||||
StageLayering.BACKGROUND_LAYER,
|
||||
StageLayering.VIDEO_LAYER,
|
||||
StageLayering.PEN_LAYER,
|
||||
StageLayering.SPRITE_LAYER
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StageLayering;
|
|
@ -7,6 +7,7 @@ const formatMessage = require('format-message');
|
|||
const MathUtil = require('../../util/math-util');
|
||||
const RenderedTarget = require('../../sprites/rendered-target');
|
||||
const log = require('../../util/log');
|
||||
const StageLayering = require('../../engine/stage-layering');
|
||||
|
||||
/**
|
||||
* Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
|
||||
|
@ -87,15 +88,6 @@ class Scratch3PenBlocks {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Place the pen layer in front of the backdrop but behind everything else.
|
||||
* We should probably handle this somewhere else... somewhere central that knows about pen, backdrop, video, etc.
|
||||
* Maybe it should be in the GUI?
|
||||
* @type {int}
|
||||
*/
|
||||
static get PEN_ORDER () {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum and maximum allowed pen size.
|
||||
|
@ -136,8 +128,7 @@ class Scratch3PenBlocks {
|
|||
_getPenLayerID () {
|
||||
if (this._penSkinId < 0 && this.runtime.renderer) {
|
||||
this._penSkinId = this.runtime.renderer.createPenSkin();
|
||||
this._penDrawableId = this.runtime.renderer.createDrawable();
|
||||
this.runtime.renderer.setDrawableOrder(this._penDrawableId, Scratch3PenBlocks.PEN_ORDER);
|
||||
this._penDrawableId = this.runtime.renderer.createDrawable(StageLayering.PEN_LAYER);
|
||||
this.runtime.renderer.updateDrawableProperties(this._penDrawableId, {skinId: this._penSkinId});
|
||||
}
|
||||
return this._penSkinId;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
const StageLayering = require('../engine/stage-layering');
|
||||
|
||||
class Video {
|
||||
constructor (runtime) {
|
||||
this.runtime = runtime;
|
||||
|
@ -144,11 +146,7 @@ class Video {
|
|||
if (this._skinId === -1 && this._skin === null && this._drawable === -1) {
|
||||
this._skinId = renderer.createPenSkin();
|
||||
this._skin = renderer._allSkins[this._skinId];
|
||||
this._drawable = renderer.createDrawable();
|
||||
renderer.setDrawableOrder(
|
||||
this._drawable,
|
||||
Video.ORDER
|
||||
);
|
||||
this._drawable = renderer.createDrawable(StageLayering.VIDEO_LAYER);
|
||||
renderer.updateDrawableProperties(this._drawable, {
|
||||
skinId: this._skinId
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@ const StringUtil = require('../util/string-util');
|
|||
const specMap = require('./sb2_specmap');
|
||||
const Variable = require('../engine/variable');
|
||||
const MonitorRecord = require('../engine/monitor-record');
|
||||
const StageLayering = require('../engine/stage-layering');
|
||||
|
||||
const {loadCostume} = require('../import/load-costume.js');
|
||||
const {loadSound} = require('../import/load-sound.js');
|
||||
|
@ -400,7 +401,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip)
|
|||
}
|
||||
|
||||
// Create the first clone, and load its run-state from JSON.
|
||||
const target = sprite.createClone();
|
||||
const target = sprite.createClone(topLevel ? StageLayering.BACKGROUND_LAYER : StageLayering.SPRITE_LAYER);
|
||||
|
||||
const getVariableId = generateVariableIdGetter(target.id, topLevel);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ const vmPackage = require('../../package.json');
|
|||
const Blocks = require('../engine/blocks');
|
||||
const Sprite = require('../sprites/sprite');
|
||||
const Variable = require('../engine/variable');
|
||||
const StageLayering = require('../engine/stage-layering');
|
||||
const log = require('../util/log');
|
||||
const uid = require('../util/uid');
|
||||
|
||||
|
@ -779,7 +780,7 @@ const parseScratchObject = function (object, runtime, extensions, zip) {
|
|||
// process has been completed.
|
||||
});
|
||||
// Create the first clone, and load its run-state from JSON.
|
||||
const target = sprite.createClone();
|
||||
const target = sprite.createClone(object.isStage ? StageLayering.BACKGROUND_LAYER : StageLayering.SPRITE_LAYER);
|
||||
// Load target properties from JSON.
|
||||
if (object.hasOwnProperty('tempo')) {
|
||||
target.tempo = object.tempo;
|
||||
|
|
|
@ -2,6 +2,7 @@ const log = require('../util/log');
|
|||
const MathUtil = require('../util/math-util');
|
||||
const StringUtil = require('../util/string-util');
|
||||
const Target = require('../engine/target');
|
||||
const StageLayering = require('../engine/stage-layering');
|
||||
|
||||
/**
|
||||
* Rendered target: instance of a sprite (clone), or the stage.
|
||||
|
@ -155,10 +156,11 @@ class RenderedTarget extends Target {
|
|||
|
||||
/**
|
||||
* Create a drawable with the this.renderer.
|
||||
* @param {boolean} layerGroup The layer group this drawable should be added to
|
||||
*/
|
||||
initDrawable () {
|
||||
initDrawable (layerGroup) {
|
||||
if (this.renderer) {
|
||||
this.drawableID = this.renderer.createDrawable();
|
||||
this.drawableID = this.renderer.createDrawable(layerGroup);
|
||||
}
|
||||
// If we're a clone, start the hats.
|
||||
if (!this.isOriginal) {
|
||||
|
@ -813,18 +815,22 @@ class RenderedTarget extends Target {
|
|||
/**
|
||||
* Move to the front layer.
|
||||
*/
|
||||
goToFront () {
|
||||
goToFront () { // This should only ever be used for sprites
|
||||
if (this.renderer) {
|
||||
this.renderer.setDrawableOrder(this.drawableID, Infinity);
|
||||
// Let the renderer re-order the sprite based on its knowledge
|
||||
// of what layers are present
|
||||
this.renderer.setDrawableOrder(this.drawableID, Infinity, StageLayering.SPRITE_LAYER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to the back layer.
|
||||
*/
|
||||
goToBack () {
|
||||
goToBack () { // This should only ever be used for sprites
|
||||
if (this.renderer) {
|
||||
this.renderer.setDrawableOrder(this.drawableID, -Infinity, false, 1);
|
||||
// Let the renderer re-order the sprite based on its knowledge
|
||||
// of what layers are present
|
||||
this.renderer.setDrawableOrder(this.drawableID, -Infinity, StageLayering.SPRITE_LAYER, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -834,7 +840,7 @@ class RenderedTarget extends Target {
|
|||
*/
|
||||
goForwardLayers (nLayers) {
|
||||
if (this.renderer) {
|
||||
this.renderer.setDrawableOrder(this.drawableID, nLayers, true, 1);
|
||||
this.renderer.setDrawableOrder(this.drawableID, nLayers, StageLayering.SPRITE_LAYER, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -844,7 +850,7 @@ class RenderedTarget extends Target {
|
|||
*/
|
||||
goBackwardLayers (nLayers) {
|
||||
if (this.renderer) {
|
||||
this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1);
|
||||
this.renderer.setDrawableOrder(this.drawableID, -nLayers, StageLayering.SPRITE_LAYER, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -855,8 +861,8 @@ class RenderedTarget extends Target {
|
|||
goBehindOther (other) {
|
||||
if (this.renderer) {
|
||||
const otherLayer = this.renderer.setDrawableOrder(
|
||||
other.drawableID, 0, true);
|
||||
this.renderer.setDrawableOrder(this.drawableID, otherLayer);
|
||||
other.drawableID, 0, StageLayering.SPRITE_LAYER, true);
|
||||
this.renderer.setDrawableOrder(this.drawableID, otherLayer, StageLayering.SPRITE_LAYER);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -925,7 +931,7 @@ class RenderedTarget extends Target {
|
|||
newClone.effects = JSON.parse(JSON.stringify(this.effects));
|
||||
newClone.variables = JSON.parse(JSON.stringify(this.variables));
|
||||
newClone.lists = JSON.parse(JSON.stringify(this.lists));
|
||||
newClone.initDrawable();
|
||||
newClone.initDrawable(StageLayering.SPRITE_LAYER);
|
||||
newClone.updateAllDrawableProperties();
|
||||
// Place behind the current target.
|
||||
newClone.goBehindOther(this);
|
||||
|
@ -1062,7 +1068,9 @@ class RenderedTarget extends Target {
|
|||
this.runtime.stopForTarget(this);
|
||||
this.sprite.removeClone(this);
|
||||
if (this.renderer && this.drawableID !== null) {
|
||||
this.renderer.destroyDrawable(this.drawableID);
|
||||
this.renderer.destroyDrawable(this.drawableID, this.isStage ?
|
||||
StageLayering.BACKGROUND_LAYER :
|
||||
StageLayering.SPRITE_LAYER);
|
||||
if (this.visible) {
|
||||
this.emit(RenderedTarget.EVENT_TARGET_VISUAL_CHANGE, this);
|
||||
this.runtime.requestRedraw();
|
||||
|
|
|
@ -3,6 +3,7 @@ const Blocks = require('../engine/blocks');
|
|||
const {loadSoundFromAsset} = require('../import/load-sound');
|
||||
const {loadCostumeFromAsset} = require('../import/load-costume');
|
||||
const StringUtil = require('../util/string-util');
|
||||
const StageLayering = require('../engine/stage-layering');
|
||||
|
||||
class Sprite {
|
||||
/**
|
||||
|
@ -47,7 +48,7 @@ class Sprite {
|
|||
*/
|
||||
this.clones = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add an array of costumes, taking care to avoid duplicate names.
|
||||
* @param {!Array<object>} costumes Array of objects representing costumes.
|
||||
|
@ -95,15 +96,19 @@ class Sprite {
|
|||
|
||||
/**
|
||||
* Create a clone of this sprite.
|
||||
* @param {string=} optLayerGroup Optional layer group the clone's drawable should be added to
|
||||
* Defaults to the sprite layer group
|
||||
* @returns {!RenderedTarget} Newly created clone.
|
||||
*/
|
||||
createClone () {
|
||||
createClone (optLayerGroup) {
|
||||
const newClone = new RenderedTarget(this, this.runtime);
|
||||
newClone.isOriginal = this.clones.length === 0;
|
||||
this.clones.push(newClone);
|
||||
newClone.initAudio();
|
||||
if (newClone.isOriginal) {
|
||||
newClone.initDrawable();
|
||||
// Default to the sprite layer group if optLayerGroup is not provided
|
||||
const layerGroup = typeof optLayerGroup === 'string' ? optLayerGroup : StageLayering.SPRITE_LAYER;
|
||||
newClone.initDrawable(layerGroup);
|
||||
this.runtime.fireTargetWasCreated(newClone);
|
||||
} else {
|
||||
this.runtime.fireTargetWasCreated(newClone, this.clones[0]);
|
||||
|
|
4
test/fixtures/fake-renderer.js
vendored
4
test/fixtures/fake-renderer.js
vendored
|
@ -42,7 +42,7 @@ FakeRenderer.prototype.getBounds = function (d) { // eslint-disable-line no-unus
|
|||
return {left: this.x, right: this.x, top: this.y, bottom: this.y};
|
||||
};
|
||||
|
||||
FakeRenderer.prototype.setDrawableOrder = function (d, a, optA, optB) { // eslint-disable-line no-unused-vars
|
||||
FakeRenderer.prototype.setDrawableOrder = function (d, a, optG, optA, optB) { // eslint-disable-line no-unused-vars
|
||||
if (d === 999) return 1; // fake for test case
|
||||
if (optA) {
|
||||
a += this.order;
|
||||
|
@ -63,4 +63,6 @@ FakeRenderer.prototype.isTouchingColor = function (a, b) { // eslint-disable-lin
|
|||
return false;
|
||||
};
|
||||
|
||||
FakeRenderer.prototype.setLayerGroupOrdering = function (a) {}; // eslint-disable-line no-unused-vars
|
||||
|
||||
module.exports = FakeRenderer;
|
||||
|
|
|
@ -302,7 +302,7 @@ test('colorIsTouchingColor', t => {
|
|||
t.end();
|
||||
});
|
||||
|
||||
test('layers', t => {
|
||||
test('layers', t => { // TODO this tests fake functionality. Move layering tests into Render.
|
||||
const s = new Sprite();
|
||||
const r = new Runtime();
|
||||
const renderer = new FakeRenderer();
|
||||
|
@ -315,9 +315,11 @@ test('layers', t => {
|
|||
a.goBackwardLayers(2);
|
||||
t.equals(a.renderer.order, 3);
|
||||
a.goToBack();
|
||||
t.equals(a.renderer.order, 1);
|
||||
// Note, there are only sprites in this test, no stage, and the addition
|
||||
// of layer groups, goToBack no longer specifies a minimum order number
|
||||
t.equals(a.renderer.order, 0);
|
||||
a.goForwardLayers(1);
|
||||
t.equals(a.renderer.order, 2);
|
||||
t.equals(a.renderer.order, 1);
|
||||
o.drawableID = 999;
|
||||
a.goBehindOther(o);
|
||||
t.equals(a.renderer.order, 1);
|
||||
|
|
Loading…
Reference in a new issue