feat(boards): python_skulpt_car添加 分步执行 与 关卡选择
This commit is contained in:
@@ -1,13 +1,64 @@
|
||||
import { app, Nav, Debug } from 'mixly';
|
||||
import $ from 'jquery';
|
||||
import * as Blockly from 'blockly/core';
|
||||
import {
|
||||
app,
|
||||
Nav,
|
||||
Debug,
|
||||
HTMLTemplate,
|
||||
Msg,
|
||||
Workspace,
|
||||
Storage
|
||||
} from 'mixly';
|
||||
import PythonShell from './python-shell';
|
||||
import LEVEL_SELECTOR_TEMPLATE from '../templates/html/level-selector.html';
|
||||
|
||||
|
||||
const NavExt = {};
|
||||
const LEVELS = [
|
||||
`<xml>
|
||||
<block type="initSettedMap_1"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_2"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_3"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_4"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_5"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_6"></block>
|
||||
</xml>`,
|
||||
`<xml>
|
||||
<block type="initSettedMap_7"></block>
|
||||
</xml>`
|
||||
];
|
||||
NavExt.$shadow = $('<div style="position:absolute;z-index:1000;width:100%;background:transparent;bottom:0px;top:var(--nav-height);"></div>');
|
||||
NavExt.count = 0;
|
||||
|
||||
NavExt.init = function () {
|
||||
PythonShell.init();
|
||||
const nav = app.getNav();
|
||||
|
||||
nav.register({
|
||||
icon: 'icon-play-circled',
|
||||
title: '',
|
||||
id: 'python-steprun-btn',
|
||||
displayText: Blockly.Msg.MSG['step_run'],
|
||||
preconditionFn: () => {
|
||||
return true;
|
||||
},
|
||||
callback: () => {
|
||||
PythonShell.steprun().catch(Debug.error);
|
||||
},
|
||||
scopeType: Nav.Scope.LEFT,
|
||||
weight: 4
|
||||
});
|
||||
|
||||
nav.register({
|
||||
icon: 'icon-play-circled',
|
||||
title: '',
|
||||
@@ -20,7 +71,7 @@ NavExt.init = function () {
|
||||
PythonShell.run().catch(Debug.error);
|
||||
},
|
||||
scopeType: Nav.Scope.LEFT,
|
||||
weight: 4
|
||||
weight: 5
|
||||
});
|
||||
|
||||
nav.register({
|
||||
@@ -35,8 +86,41 @@ NavExt.init = function () {
|
||||
PythonShell.stop().catch(Debug.error);
|
||||
},
|
||||
scopeType: Nav.Scope.LEFT,
|
||||
weight: 5
|
||||
weight: 6
|
||||
});
|
||||
|
||||
const template = new HTMLTemplate(LEVEL_SELECTOR_TEMPLATE);
|
||||
const $selector = $(template.render());
|
||||
nav.getBoardSelector().before($selector);
|
||||
$selector.select2({
|
||||
width: '90px',
|
||||
minimumResultsForSearch: Infinity,
|
||||
dropdownCssClass: `mixly-scrollbar mixly-${template.getId()}`,
|
||||
dropdownAutoWidth: true,
|
||||
placeholder: '',
|
||||
language: Msg.nowLang
|
||||
});
|
||||
for (let i = 0; i < LEVELS.length; i++) {
|
||||
const option = new Option(`关卡 ${i + 1}`, i);
|
||||
$selector.append(option);
|
||||
}
|
||||
$selector.on('select2:select', (event) => {
|
||||
const { data } = event.params;
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||
editor.setValue(LEVELS[parseInt(data.id)], '.mix');
|
||||
});
|
||||
$selector.on('select2:opening', () => {
|
||||
NavExt.count += 1;
|
||||
$(document.body).append(NavExt.$shadow);
|
||||
});
|
||||
$selector.on('select2:closing', () => {
|
||||
NavExt.count -= 1;
|
||||
!NavExt.count && NavExt.$shadow.detach();
|
||||
});
|
||||
$selector.trigger('change');
|
||||
Storage.board('mix', LEVELS[0]);
|
||||
Storage.board('path', '');
|
||||
}
|
||||
|
||||
export default NavExt;
|
||||
@@ -268,6 +268,53 @@ export default class PyEngine {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given python code, resetting the console and Trace Table.
|
||||
* 分步调试代码
|
||||
*/
|
||||
steprun(code) {
|
||||
// Reset everything
|
||||
this.reset();
|
||||
if (code.indexOf('import blocklygame') !== -1
|
||||
|| code.indexOf('from blocklygame import') !== -1) {
|
||||
PyGameZero.reset();
|
||||
$(Sk.TurtleGraphics.target).empty();
|
||||
}
|
||||
//如果是第五关、第七关,则需要把检查循环次数的代码加进去
|
||||
if (code.indexOf("settedMap(4") != -1 | code.indexOf("settedMap(6") != -1) {
|
||||
if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码
|
||||
code = code + "actor.isCirculationRight()\n"
|
||||
}
|
||||
}
|
||||
|
||||
//除了第六关,其他把检查是否成功代码加进去
|
||||
if (code.indexOf("settedMap(5)") == -1) {
|
||||
if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码
|
||||
code = code + "actor.isSuccess()\n"
|
||||
}
|
||||
}
|
||||
this.programStatus['running'] = true;
|
||||
Sk.misceval.asyncToPromise(() => Sk.importMainWithBody("<stdin>", false, code, true))
|
||||
.then(() => {
|
||||
// window.SPRITE.running = false;
|
||||
this.programStatus['running'] = false;
|
||||
this.#events_.run('finished');
|
||||
})
|
||||
.catch((error) => {
|
||||
Debug.error(error);
|
||||
// window.SPRITE.running = false;
|
||||
this.programStatus['running'] = false;
|
||||
this.#events_.run('error', error);
|
||||
var original = prettyPrintError(error);
|
||||
this.#events_.run('finished');
|
||||
//hack for kill program with time limiterror
|
||||
if (original.indexOf("TimeLimitError") !== -1) {
|
||||
return;
|
||||
}
|
||||
this.executionEnd_();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given python code, resetting the console and Trace Table.
|
||||
*/
|
||||
@@ -308,6 +355,19 @@ export default class PyEngine {
|
||||
}
|
||||
}
|
||||
}
|
||||
//如果是第五关、第七关,则需要把检查循环次数的代码加进去
|
||||
if (code.indexOf("settedMap(4") != -1 | code.indexOf("settedMap(6") != -1) {
|
||||
if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码
|
||||
code = code + "actor.isCirculationRight();\n"
|
||||
}
|
||||
}
|
||||
|
||||
//除了第六关,其他把检查是否成功代码加进去
|
||||
if (code.indexOf("settedMap(5)") == -1) {
|
||||
if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码
|
||||
code = code + "actor.isSuccess();\n"
|
||||
}
|
||||
}
|
||||
this.programStatus['running'] = true;
|
||||
Sk.misceval.asyncToPromise(() => Sk.importMainWithBody("<stdin>", false, code, true))
|
||||
.then(() => {
|
||||
|
||||
@@ -12,6 +12,13 @@ class PythonShell {
|
||||
this.pythonShell = new PythonShell();
|
||||
}
|
||||
|
||||
this.steprun = function () {
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||
const code = editor.getCode();
|
||||
return this.pythonShell.steprun(code);
|
||||
}
|
||||
|
||||
this.run = function () {
|
||||
const mainWorkspace = Workspace.getMain();
|
||||
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||
@@ -77,7 +84,6 @@ class PythonShell {
|
||||
this.#statusBarTerminal_ = this.#statusBarsManager_.getStatusBarById('output');
|
||||
this.#statusBarImage_ = this.#statusBarsManager_.getStatusBarById('images');
|
||||
this.#pyEngine_ = new PyEngine({}, new MixpyProject());
|
||||
console.log(this.#statusBarImage_.getContent().children())
|
||||
this.#pyEngine_.loadEngine(this.#statusBarImage_.getContent().children()[0]);
|
||||
this.#addEventsListener_();
|
||||
}
|
||||
@@ -157,6 +163,19 @@ class PythonShell {
|
||||
this.#enterInput_();
|
||||
}
|
||||
|
||||
async steprun(code) {
|
||||
await this.stop();
|
||||
this.#statusBarsManager_.changeTo('output');
|
||||
this.#statusBarsManager_.show();
|
||||
this.#statusBarTerminal_.setValue(`${Msg.Lang['shell.running']}...\n`);
|
||||
this.#running_ = true;
|
||||
if (code.indexOf('import blocklygame') !== -1
|
||||
|| code.indexOf('from blocklygame import') !== -1) {
|
||||
this.#statusBarsManager_.changeTo('images');
|
||||
}
|
||||
this.#pyEngine_.steprun(code);
|
||||
}
|
||||
|
||||
async run(code) {
|
||||
await this.stop();
|
||||
this.#statusBarsManager_.changeTo('output');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
var $builtinmodule = function (name) {
|
||||
let mod= {__name__: new Sk.builtin.str("blocklygame")};
|
||||
|
||||
var svg = d3.select('#blocklySVG').append('svg');
|
||||
var svg = d3.select(Sk.TurtleGraphics.target).append('svg');
|
||||
|
||||
//其他变量设置
|
||||
var map=//迷宫布局
|
||||
@@ -63,7 +63,8 @@ var $builtinmodule = function (name) {
|
||||
marker_num:0,
|
||||
oil:1,//表示小车有充足的油量(为了适应教材而新增的变量)
|
||||
traffic_light:22,//表示红绿灯为绿灯
|
||||
circulation_num:0//小车在赛道中循环的次数
|
||||
circulation_num:0,//小车在赛道中循环的次数
|
||||
invisible_mark:0//在地图中不可显示的标记数目,用于检测小车是否沿着特定路线走。规定不可见的标记点INVIMAKER=24
|
||||
};
|
||||
//迷宫变量
|
||||
var maze_SQUARE_SIZE = 50;
|
||||
@@ -84,7 +85,8 @@ var $builtinmodule = function (name) {
|
||||
OPEN: 1,
|
||||
START: 2,
|
||||
FINISH: 3,
|
||||
AWARD:4//金币奖励
|
||||
AWARD:4,//金币奖励
|
||||
INVIMAKER:24
|
||||
},
|
||||
//迷宫部分参数指定
|
||||
MAZE_WIDTH : maze_SQUARE_SIZE * maze_COLS,
|
||||
@@ -92,7 +94,8 @@ var $builtinmodule = function (name) {
|
||||
PATH_WIDTH : maze_SQUARE_SIZE / 3,
|
||||
result : ResultType.UNSET,
|
||||
finish : {x:0,y:0},
|
||||
type:1//类型为用户自定义的
|
||||
type:1,//类型为用户自定义的
|
||||
INVIMNUM : 0
|
||||
};
|
||||
|
||||
//已经设置好的关卡的map
|
||||
@@ -363,7 +366,7 @@ var $builtinmodule = function (name) {
|
||||
* @param {number=} opt_angle Optional angle (in degrees) to rotate Pegman.
|
||||
*/
|
||||
var displayPegman = function(x, y, d, opt_angle) {
|
||||
var pegmanIcon = $('#pegman');
|
||||
var pegmanIcon = $(Sk.TurtleGraphics.target).find('#pegman');
|
||||
if(actor.type=='animate'){
|
||||
if(maze.type==0){
|
||||
pegmanIcon.attr('x', x * maze_SQUARE_SIZE - d * actor.width+ 1);
|
||||
@@ -383,7 +386,7 @@ var $builtinmodule = function (name) {
|
||||
pegmanIcon.attr('x', x * maze_SQUARE_SIZE + 1);
|
||||
pegmanIcon.attr('y', maze_SQUARE_SIZE * (y + 0.5) - actor.height / 2 );
|
||||
}
|
||||
var clipRect = $('#clipRect');
|
||||
var clipRect = $(Sk.TurtleGraphics.target).find('#clipRect');
|
||||
clipRect.attr('x', x * maze_SQUARE_SIZE + 1);
|
||||
clipRect.attr('y', pegmanIcon.attr('y'));
|
||||
};
|
||||
@@ -391,7 +394,8 @@ var $builtinmodule = function (name) {
|
||||
var initPegman=function(){
|
||||
// Pegman's clipPath element, whose (x, y) is reset by Maze.displayPegman
|
||||
svg.append('clipPath').attr('id','pegmanClipPath')
|
||||
d3.select("#pegmanClipPath").append('rect').attr('id','clipRect').attr('width', actor.width).attr('height', actor.height)
|
||||
const elem = Sk.TurtleGraphics.target.querySelector("#pegmanClipPath");
|
||||
d3.select(elem).append('rect').attr('id','clipRect').attr('width', actor.width).attr('height', actor.height)
|
||||
|
||||
if(actor.type=="animate"){
|
||||
if(maze.type==0){
|
||||
@@ -462,13 +466,13 @@ var $builtinmodule = function (name) {
|
||||
var top = tile_SHAPES[tileShape][1];
|
||||
// Tile's clipPath element.
|
||||
svg.append('clipPath').attr('id','tileClipPath' + tileId)
|
||||
d3.select("#tileClipPath" + tileId).append('rect').attr('x', x * maze_SQUARE_SIZE).attr('y', y * maze_SQUARE_SIZE).attr('width', maze_SQUARE_SIZE).attr('height', maze_SQUARE_SIZE)
|
||||
|
||||
const elem = Sk.TurtleGraphics.target.querySelector("#tileClipPath" + tileId);
|
||||
d3.select(elem).append('rect').attr('x', x * maze_SQUARE_SIZE).attr('y', y * maze_SQUARE_SIZE).attr('width', maze_SQUARE_SIZE).attr('height', maze_SQUARE_SIZE)
|
||||
if(maze.type==0){//非用户自定义
|
||||
// Tile sprite.
|
||||
if ((map[y][x] != maze.SquareType.WALL) && (map[y][x] != maze.SquareType.OIL_STATION) && (map[y][x] != maze.SquareType.TRAFFIC_LIGHT)&& (map[y][x] != maze.SquareType.LIGHT_GREEN)&& (map[y][x] != maze.SquareType.LIGHT_RED)) {
|
||||
svg.append('image').attr('x', x * maze_SQUARE_SIZE).attr('y', y * maze_SQUARE_SIZE).attr('width',maze_SQUARE_SIZE ).attr('height',maze_SQUARE_SIZE )
|
||||
.attr('clip-path', 'url(#tileClipPath' + tileId + ')').attr('xlink:href',maze.tiles)
|
||||
.attr('clip-path', 'url(#tileClipPath' + tileId + ')').attr('xlink:href',maze.tiles);
|
||||
tileId++;
|
||||
}
|
||||
}else{
|
||||
@@ -519,7 +523,7 @@ var $builtinmodule = function (name) {
|
||||
actor.y= y;
|
||||
} else if (map[y][x] == maze.SquareType.FINISH) {
|
||||
// Move the finish icon into position.
|
||||
var finishIcon = $('#finish');
|
||||
var finishIcon = $(Sk.TurtleGraphics.target).find('#finish');
|
||||
finishIcon.attr('x', maze_SQUARE_SIZE * (x + 0.5) -
|
||||
finishIcon.attr('width') / 2);
|
||||
finishIcon.attr('y', maze_SQUARE_SIZE * (y + 0.6) -
|
||||
@@ -538,7 +542,7 @@ var $builtinmodule = function (name) {
|
||||
svg.append('image').attr('id','finish').attr('width', maze_SQUARE_SIZE*0.8).attr('height', maze_SQUARE_SIZE*0.8).attr('xlink:href',maze.marker)
|
||||
actor.x= x;
|
||||
actor.y= y;
|
||||
var finishIcon = $('#finish');
|
||||
var finishIcon = $(Sk.TurtleGraphics.target).find('#finish');
|
||||
finishIcon.attr('x', maze_SQUARE_SIZE * x+5 );
|
||||
finishIcon.attr('y', maze_SQUARE_SIZE * y+5);
|
||||
maze.finish={x:x,y:y}
|
||||
@@ -574,7 +578,6 @@ var $builtinmodule = function (name) {
|
||||
return true
|
||||
}else{
|
||||
if(maze.mlevel==5 || maze.mlevel==7 ||maze.mlevel==6){
|
||||
console.log(11)
|
||||
return true
|
||||
}else{
|
||||
if(actor.marker_num==maze_marker_num){
|
||||
@@ -713,7 +716,7 @@ var $builtinmodule = function (name) {
|
||||
var hasCoin=function(x , y) {
|
||||
if(map[y][x]==maze.SquareType.AWARD){//如果此处是金币
|
||||
setTimeout(function() {
|
||||
$('#coin'+y+x).remove()
|
||||
$(Sk.TurtleGraphics.target).find('#coin'+y+x).remove()
|
||||
}, actor.stepSpeed * 3)
|
||||
map[y][x]=maze.SquareType.OPEN
|
||||
actor.coin_point+=1
|
||||
@@ -1147,7 +1150,8 @@ var $builtinmodule = function (name) {
|
||||
map[actor.y][actor.x+1]=Math.random()>0.5? maze.SquareType.LIGHT_RED:maze.SquareType.LIGHT_GREEN;//随机刷新红绿灯的状态
|
||||
actor.traffic_light=map[actor.y][actor.x+1];
|
||||
if(actor.traffic_light==maze.SquareType.LIGHT_RED){//图像变为红灯
|
||||
d3.select("#lightgreen").remove();
|
||||
const elem = Sk.TurtleGraphics.target.querySelector("#lightgreen");
|
||||
d3.select(elem).remove();
|
||||
svg.append('image').attr('id','lightred').attr('x',(actor.x+1) * maze_SQUARE_SIZE-5).attr('y',actor.y * maze_SQUARE_SIZE+5).attr('width',maze_SQUARE_SIZE).attr('height',maze_SQUARE_SIZE)
|
||||
.attr('xlink:href','../common/js/skulpt_mixcar/pic/redlight.png')
|
||||
}
|
||||
@@ -1307,7 +1311,6 @@ var $builtinmodule = function (name) {
|
||||
Sk.builtin.pyCheckArgs("isSuccess", arguments, 1,1);
|
||||
return new Sk.misceval.promiseToSuspension(new Promise(function(resolve) {
|
||||
var state=checkFinish()
|
||||
console.log(state)
|
||||
if(state==true){
|
||||
setTimeout(function() {
|
||||
layer.alert("挑战成功!", { shade: false });
|
||||
|
||||
@@ -579,7 +579,6 @@ var $builtinmodule = function (name) {
|
||||
return true
|
||||
}else{
|
||||
if(maze.mlevel==5 || maze.mlevel==7 ||maze.mlevel==6){
|
||||
console.log(11)
|
||||
return true
|
||||
}else{
|
||||
if(actor.marker_num==maze_marker_num){
|
||||
@@ -1172,7 +1171,6 @@ var $builtinmodule = function (name) {
|
||||
Sk.builtin.pyCheckArgs("isSuccess", arguments, 1,1);
|
||||
return new Sk.misceval.promiseToSuspension(new Promise(function(resolve) {
|
||||
var state=checkFinish()
|
||||
console.log(state)
|
||||
if(state==true){
|
||||
setTimeout(function() {
|
||||
layer.alert("挑战成功!", { shade: false });
|
||||
|
||||
@@ -1166,13 +1166,6 @@
|
||||
|
||||
<!-- 游戏:需要添加 -->
|
||||
<category id="catGame" name="game" colour="270">
|
||||
<block type="initSettedMap_1"></block>
|
||||
<block type="initSettedMap_2"></block>
|
||||
<block type="initSettedMap_3"></block>
|
||||
<block type="initSettedMap_4"></block>
|
||||
<block type="initSettedMap_5"></block>
|
||||
<block type="initSettedMap_6"></block>
|
||||
<block type="initSettedMap_7"></block>
|
||||
<block type="move_direction_steps">
|
||||
<value name="times">
|
||||
<shadow type="math_number">
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<style>
|
||||
.select2-dropdown.mixly-{{d.mId}} {
|
||||
width: 100% !important;
|
||||
transform: translate(calc(-50% + 45px), calc((var(--nav-height) - var(--nav-select-height)) / 2));
|
||||
}
|
||||
|
||||
body > .select2-container:has(.mixly-{{d.mId}}) {
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
animation-name: layui-upbit;
|
||||
}
|
||||
</style>
|
||||
<select m-id={{d.mId}}></select>
|
||||
Reference in New Issue
Block a user