少儿编程教学环境开发之代码实战篇mp.weixin.qq.com
之前架构选型篇许诺要上自己原型demo的界面图,先把这个许诺给实现了,首先声明,这只是技术原型demo,比起http://code.org的产品来讲简陋很多:
首先对代码的整体架构做一下说明,首先我们采用的是Vue SPA框架为整个站点的前端框架,这样编辑器界面和游戏窗口界面各自被封装成Component。
下面先上右侧BlocklyEditor Component的的代码,代码解读就直接放到代码注释中了
<template>
<div>
<div id="blocklyDiv" style="position: absolute"></div>
<!--上面的blockDvi主要是容器插槽用来放置编辑器-->
<xml id="toolbox" style="display: none">
<block type="string_length"></block>
<block type="controls_if"></block>
<block type="logic_compare"></block>
<block type="controls_repeat_ext"></block>
<block type="math_number">
<field name="NUM">123</field>
</block>
<block type="math_arithmetic"></block>
<block type="text"></block>
<block type="text_print"></block>
</xml>
<!--上面的xml主要是定制block工具栏列表-->
</div>
</template>
<script>
import Blockly from 'blockly'
//下面这个代码块用来定制string_length block的形状
Blockly.Blocks['string_length'] = {
init: function() {
this.jsonInit({
"message0": '%1',
"args0": [
{
"type": "field_image",
"src": "/assets/logo.png",
"width": 100,
"height": 50,
"alt": "*"
}
],
"nextStatement": "Action",
"fillPattern":"#",
"colour": 160,
"tooltip": "Returns number of letters in the provided text.",
"helpUrl": "http://www.w3schools.com/jsref/jsref_length_string.asp"
});
}
};
//下面这个代码块用来定制string_length block的行为函数
// eslint-disable-next-line
Blockly.JavaScript['string_length'] = function(block) {
// String or array length.
//var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE',
// Blockly.JavaScript.ORDER_FUNCTION_CALL) || '''';
var argument0= "begin"
return [argument0 + '.length', Blockly.JavaScript.ORDER_MEMBER];
};
export default {
name: "BlocklyEditor",
mounted() {
//在component的这个生命周期事件上初始化整个编辑器
this.$nextTick(
function () {
var blocklyArea = document.getElementById("app");
var blocklyDiv = document.getElementById('blocklyDiv');
//往blocklyDiv容器插槽中插入blockly编辑器
var demoWorkspace = Blockly.inject(blocklyDiv,
{media: '/assets/blockly_media/',
trashcan:true,
grid:
{spacing: 20,
length: 20,
colour: '#ccc',
snap: true},
toolbox: document.getElementById('toolbox')});
//定义编辑器响应resize事件的函数,里面逻辑顺便完成了编辑器的形状设置
// eslint-disable-next-line
var onresize = function(e) {
// Compute the absolute coordinates and dimensions of blocklyArea.
var element = blocklyDiv;
// eslint-disable-next-line
var x = 0;
var y = 0;
do {
x += element.offsetLeft;
y += element.offsetTop;
element = element.offsetParent;
} while (element);
// Position blocklyDiv over blocklyArea.
blocklyDiv.style.left = 436 + 'px';
blocklyDiv.style.top = y + 'px';
blocklyDiv.style.width = blocklyArea.offsetWidth -470 + 'px';
blocklyDiv.style.height = blocklyArea.offsetHeight-125 + 'px';
Blockly.svgResize(demoWorkspace);
};
window.addEventListener('resize', onresize, false);
onresize();
Blockly.svgResize(demoWorkspace);
}
)
}
}
</script>
下面上左侧phaser.js游戏窗口的的代码
<template>
<div>
<!--下面这个div是游戏界面的插槽容器-->
<div id="phaser-wraper"></div>
<div><button type="button" class="btn btn-primary">运行 <i class="fas fa-play"></i></button></div>
</div>
</template>
<script>
import Phaser from "phaser"
var player
var platforms
//这个函数中完成游戏资源的预加载
function preload ()
{
//this.load.setBaseURL('Phaser 3 Examples');
//this.load.image('logo', 'assets/sprites/phaser3-logo.png');
//this.load.image('red', 'assets/particles/red.png');
this.load.setBaseURL('/');
this.load.image('sky', 'assets/sky.png');
this.load.image('ground', 'assets/platform.png');
this.load.multiatlas('citizen', 'assets/side_walk.json', 'assets');
this.load.multiatlas('front', 'assets/front.json', 'assets');
}
//这个函数完成游戏场景的初始化
function create ()
{
var that = this;
this.add.image(200, 200, 'sky');
platforms = this.physics.add.staticGroup();
platforms.create(200, 400, 'ground').setScale(1).refreshBody();
player = this.physics.add.sprite(215, 215, 'citizen', '0_Citizen_Walk_003.png');
player.setBounce(0.2);
player.setCollideWorldBounds(true);
this.anims.create({
key: 'turn',
frames: that.anims.generateFrameNames('front', {
start: 1, end: 1, zeroPad: 2,
prefix: '0_Citizen_Walk_0', suffix: '.png'
}),
frameRate: 20
});
this.anims.create({
key: 'down',
frames: that.anims.generateFrameNames('front', {
start: 1, end: 28, zeroPad: 2,
prefix: '0_Citizen_Walk_0', suffix: '.png'
}),
frameRate: 20
});
this.anims.create({
key: 'left',
frames: that.anims.generateFrameNames('citizen', {
start: 1, end: 28, zeroPad: 2,
prefix: '0_Citizen_Walk_0', suffix: '.png'
}),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'right',
frames: that.anims.generateFrameNames('citizen', {
start: 1, end: 28, zeroPad: 2,
prefix: '0_Citizen_Walk_0', suffix: '.png'
}),
frameRate: 10,
repeat: -1
});
this.physics.add.collider(player, platforms);
}
//这个函数完成游戏中事件行为函数的编写,完成游戏的操作交互功能
// eslint-disable-next-line
function update(time, delta) {
var cursors = this.input.keyboard.createCursorKeys();
if (cursors.left.isDown)
{
player.setVelocityX(-160);
player.anims.play('left', true);
player.setFlipX(false)
}
else if (cursors.right.isDown)
{
player.setVelocityX(160);
player.anims.play('right', true);
player.setFlipX(true)
}
else if (cursors.down.isDown)
{
player.setVelocityY(160);
player.anims.play('down', true);
player.setFlipX(false)
}
else if (cursors.up.isDown)
{
player.setVelocityY(-160);
player.anims.play('down', true);
player.setFlipX(false)
}
else
{
player.setVelocityX(0);
player.setVelocityY(0);
player.anims.play('turn');
}
//player.update(delta)
}
export default {
name: "Phaser",
mounted() {
//在vue组件的这个生命周期中完成整体游戏场景的初始化和加载
this.$nextTick(function () {
var config = {
type: Phaser.AUTO,
parent:"phaser-wraper",
width: 400,
height: 400,
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 }
}
},
scene: {
preload: preload,
create: create,
update: update,
}
};
// eslint-disable-next-line
var game = new Phaser.Game(config);
})
}
}
</script>
架构选型篇之后许诺的代码实战篇憋了好久了都没放出来,主要是代码注解一直懒得搞,让大家久等了。