mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-01-09 14:32:07 -05:00
Straw-man implementation of targets/sprites/clones
This commit is contained in:
parent
1a48e75341
commit
809528abdc
7 changed files with 99 additions and 45 deletions
|
@ -7,6 +7,7 @@ var Thread = require('./thread');
|
|||
*/
|
||||
var execute = function (sequencer, thread) {
|
||||
var runtime = sequencer.runtime;
|
||||
var target = runtime.targetForThread(thread);
|
||||
|
||||
// Current block to execute is the one on the top of the stack.
|
||||
var currentBlockId = thread.peekStack();
|
||||
|
@ -29,13 +30,13 @@ var execute = function (sequencer, thread) {
|
|||
var argValues = {};
|
||||
|
||||
// Add all fields on this block to the argValues.
|
||||
var fields = runtime.blocks.getFields(currentBlockId);
|
||||
var fields = target.blocks.getFields(currentBlockId);
|
||||
for (var fieldName in fields) {
|
||||
argValues[fieldName] = fields[fieldName].value;
|
||||
}
|
||||
|
||||
// Recursively evaluate input blocks.
|
||||
var inputs = runtime.blocks.getInputs(currentBlockId);
|
||||
var inputs = target.blocks.getInputs(currentBlockId);
|
||||
for (var inputName in inputs) {
|
||||
var input = inputs[inputName];
|
||||
var inputBlockId = input.block;
|
||||
|
|
|
@ -10,19 +10,19 @@ var defaultBlockPackages = {
|
|||
};
|
||||
|
||||
/**
|
||||
* Manages blocks, stacks, and the sequencer.
|
||||
* @param {!Blocks} blocks Blocks instance for this runtime.
|
||||
* Manages targets, stacks, and the sequencer.
|
||||
* @param {!Array.<Target>} targets List of targets for this runtime.
|
||||
*/
|
||||
function Runtime (blocks) {
|
||||
function Runtime (targets) {
|
||||
// Bind event emitter
|
||||
EventEmitter.call(this);
|
||||
|
||||
// State for the runtime
|
||||
|
||||
/**
|
||||
* Block management and storage
|
||||
* Target management and storage.
|
||||
*/
|
||||
this.blocks = blocks;
|
||||
this.targets = targets;
|
||||
|
||||
/**
|
||||
* A list of threads that are currently running in the VM.
|
||||
|
@ -163,32 +163,13 @@ Runtime.prototype.greenFlag = function () {
|
|||
this._removeThread(this.threads[i]);
|
||||
}
|
||||
// Add all top stacks with green flag
|
||||
var stacks = this.blocks.getStacks();
|
||||
for (var j = 0; j < stacks.length; j++) {
|
||||
var topBlock = stacks[j];
|
||||
if (this.blocks.getBlock(topBlock).opcode === 'event_whenflagclicked') {
|
||||
this._pushThread(stacks[j]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Distance sensor hack
|
||||
*/
|
||||
Runtime.prototype.startDistanceSensors = function () {
|
||||
// Add all top stacks with distance sensor
|
||||
var stacks = this.blocks.getStacks();
|
||||
for (var j = 0; j < stacks.length; j++) {
|
||||
var topBlock = stacks[j];
|
||||
if (this.blocks.getBlock(topBlock).opcode ===
|
||||
'wedo_whendistanceclose') {
|
||||
var alreadyRunning = false;
|
||||
for (var k = 0; k < this.threads.length; k++) {
|
||||
if (this.threads[k].topBlock === topBlock) {
|
||||
alreadyRunning = true;
|
||||
}
|
||||
}
|
||||
if (!alreadyRunning) {
|
||||
for (var t = 0; t < this.targets.length; t++) {
|
||||
var target = this.targets[t];
|
||||
var stacks = target.blocks.getStacks();
|
||||
for (var j = 0; j < stacks.length; j++) {
|
||||
var topBlock = stacks[j];
|
||||
if (target.blocks.getBlock(topBlock).opcode ===
|
||||
'event_whenflagclicked') {
|
||||
this._pushThread(stacks[j]);
|
||||
}
|
||||
}
|
||||
|
@ -228,9 +209,6 @@ Runtime.prototype._step = function () {
|
|||
* @param {boolean} isGlowing True to turn on glow; false to turn off.
|
||||
*/
|
||||
Runtime.prototype.glowBlock = function (blockId, isGlowing) {
|
||||
if (!this.blocks.getBlock(blockId)) {
|
||||
return;
|
||||
}
|
||||
if (isGlowing) {
|
||||
this.emit(Runtime.BLOCK_GLOW_ON, blockId);
|
||||
} else {
|
||||
|
@ -238,6 +216,23 @@ Runtime.prototype.glowBlock = function (blockId, isGlowing) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the Target for a particular thread.
|
||||
* @param {!Thread} thread Thread to determine target for.
|
||||
* @return {?Target} Target object, if one exists.
|
||||
*/
|
||||
Runtime.prototype.targetForThread = function (thread) {
|
||||
// @todo This is a messy solution,
|
||||
// but prevents having circular data references.
|
||||
// Have a map or some other way to associate target with threads.
|
||||
for (var t = 0; t < this.targets.length; t++) {
|
||||
var target = this.targets[t];
|
||||
if (target.blocks.getBlock(thread.topBlock)) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* setInterval implementation that works in a WebWorker or not.
|
||||
* @param {?Function} fcn Function to call.
|
||||
|
|
|
@ -106,7 +106,7 @@ Sequencer.prototype.stepToSubstack = function (thread, substackNum) {
|
|||
substackNum = 1;
|
||||
}
|
||||
var currentBlockId = thread.peekStack();
|
||||
var substackId = this.runtime.blocks.getSubstack(
|
||||
var substackId = this.runtime.targetForThread(thread).blocks.getSubstack(
|
||||
currentBlockId,
|
||||
substackNum
|
||||
);
|
||||
|
@ -153,7 +153,8 @@ Sequencer.prototype.proceedThread = function (thread) {
|
|||
// Pop from the stack - finished this level of execution.
|
||||
thread.popStack();
|
||||
// Push next connected block, if there is one.
|
||||
var nextBlockId = this.runtime.blocks.getNextBlock(currentBlockId);
|
||||
var nextBlockId = (this.runtime.targetForThread(thread).
|
||||
blocks.getNextBlock(currentBlockId));
|
||||
if (nextBlockId) {
|
||||
thread.pushStack(nextBlockId);
|
||||
}
|
||||
|
|
20
src/engine/target.js
Normal file
20
src/engine/target.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
var Blocks = require('./blocks');
|
||||
|
||||
/**
|
||||
* @fileoverview
|
||||
* A Target is an abstract "code-running" object for the Scratch VM.
|
||||
* Examples include sprites/clones or potentially physical-world devices.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {?Blocks} blocks Blocks instance for the blocks owned by this target.
|
||||
* @constructor
|
||||
*/
|
||||
function Target (blocks) {
|
||||
if (!blocks) {
|
||||
blocks = new Blocks(this);
|
||||
}
|
||||
this.blocks = blocks;
|
||||
}
|
||||
|
||||
module.exports = Target;
|
18
src/index.js
18
src/index.js
|
@ -1,7 +1,7 @@
|
|||
var EventEmitter = require('events');
|
||||
var util = require('util');
|
||||
|
||||
var Blocks = require('./engine/blocks');
|
||||
var Sprite = require('./sprites/sprite');
|
||||
var Runtime = require('./engine/runtime');
|
||||
|
||||
/**
|
||||
|
@ -21,18 +21,22 @@ function VirtualMachine () {
|
|||
// Bind event emitter and runtime to VM instance
|
||||
// @todo Post message (Web Worker) polyfill
|
||||
EventEmitter.call(instance);
|
||||
instance.blocks = new Blocks();
|
||||
instance.runtime = new Runtime(instance.blocks);
|
||||
// @todo support multiple targets/sprites.
|
||||
// This is just a demo/example.
|
||||
var exampleSprite = new Sprite();
|
||||
var exampleTargets = [exampleSprite.clones[0]];
|
||||
instance.exampleSprite = exampleSprite;
|
||||
instance.runtime = new Runtime(exampleTargets);
|
||||
|
||||
/**
|
||||
* Event listeners for scratch-blocks.
|
||||
*/
|
||||
instance.blockListener = (
|
||||
instance.blocks.generateBlockListener(false, instance.runtime)
|
||||
exampleSprite.blocks.generateBlockListener(false, instance.runtime)
|
||||
);
|
||||
|
||||
instance.flyoutBlockListener = (
|
||||
instance.blocks.generateBlockListener(true, instance.runtime)
|
||||
exampleSprite.blocks.generateBlockListener(true, instance.runtime)
|
||||
);
|
||||
|
||||
// Runtime emits are passed along as VM emits.
|
||||
|
@ -81,7 +85,7 @@ VirtualMachine.prototype.stopAll = function () {
|
|||
*/
|
||||
VirtualMachine.prototype.getPlaygroundData = function () {
|
||||
this.emit('playgroundData', {
|
||||
blocks: this.blocks,
|
||||
blocks: this.exampleSprite.blocks,
|
||||
threads: this.runtime.threads
|
||||
});
|
||||
};
|
||||
|
@ -114,7 +118,7 @@ if (ENV_WORKER) {
|
|||
case 'getPlaygroundData':
|
||||
self.postMessage({
|
||||
method: 'playgroundData',
|
||||
blocks: self.vmInstance.blocks,
|
||||
blocks: self.vmInstance.exampleSprite.blocks,
|
||||
threads: self.vmInstance.runtime.threads
|
||||
});
|
||||
break;
|
||||
|
|
16
src/sprites/clone.js
Normal file
16
src/sprites/clone.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
var util = require('util');
|
||||
var Target = require('../engine/target');
|
||||
|
||||
function Clone(spriteBlocks) {
|
||||
Target.call(this, spriteBlocks);
|
||||
}
|
||||
util.inherits(Clone, Target);
|
||||
|
||||
// Clone-level properties
|
||||
Clone.prototype.x = 0;
|
||||
|
||||
Clone.prototype.y = 0;
|
||||
|
||||
Clone.prototype.direction = 90;
|
||||
|
||||
module.exports = Clone;
|
17
src/sprites/sprite.js
Normal file
17
src/sprites/sprite.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
var Clone = require('./clone');
|
||||
var Blocks = require('../engine/blocks');
|
||||
|
||||
function Sprite (blocks) {
|
||||
// Sprites have: shared blocks, shared costumes, shared variables, etc.
|
||||
if (!blocks) {
|
||||
// Shared set of blocks for all clones.
|
||||
blocks = new Blocks();
|
||||
}
|
||||
this.blocks = blocks;
|
||||
this.clones = [];
|
||||
|
||||
// Initial single clone with the shared blocks.
|
||||
this.clones.push(new Clone(this.blocks));
|
||||
}
|
||||
|
||||
module.exports = Sprite;
|
Loading…
Reference in a new issue