路由
下一篇
历史回顾
前言
前段时间想要做一个web端的图形化积木式编程(类似少儿编程)的案例,网上冲浪了一圈又一圈,终于技术选型好,然后代码一顿敲,终于出来了一个雏形。
TIPS:该案例设计主要参考iRobot Coding,只用做学习用途,侵删。
https://code.irobot.com/#/
最终实现效果
本文实现效果
- 按钮实现
完整代码
- 只实现了按钮点击响应,相关操作逻辑会在后面的文章中介绍
<template>
<div style="height: 100%;width: 100%;">
<div>
<canvas id="renderCanvas"></canvas>
</div>
<div style="display: flex;justify-content: space-around;width: 680px;">
<div>
<label>alpha:</label>
<button type="button" class="btn"
@click="setCameraPosition('alpha',Math.PI/10)">+
</button>
<button type="button" class="btn"
@click="setCameraPosition('alpha',-Math.PI/10)">-
</button>
</div>
<div>
<label>beta:</label>
<button type="button" class="btn"
@click="setCameraPosition('beta',Math.PI/10)">+
</button>
<button type="button" class="btn"
@click="setCameraPosition('beta',-Math.PI/10)">-
</button>
</div>
<div>
<label>radius:</label>
<button type="button" class="btn"
@click="setCameraPosition('radius',Math.PI)">+
</button>
<button type="button" class="btn"
@click="setCameraPosition('radius',-Math.PI)">-
</button>
</div>
</div>
</div>
</template>
<script>
import * as BABYLON from 'babylonjs';
import * as BABYLON_MATERAIAL from "babylonjs-materials"
import * as GUI from 'babylonjs-gui';
import ammo from "ammo.js";
import utils from "./utils";
const url = "http://localhost:8088/static/simulator/"
var scene = null
var engine = null
var camera = null
var plane = null
var ground = null
var skybox = null
var car = null
var cubeParent = null
var startingPoint = new BABYLON.Vector3(0, 0, 0)
const bodyMass = 0.5, bodyFriction = 0.5, bodyRestitution = 0.9;
const groundFriction = 0.8, groundRestitution = 0.5;
let speedSelect = null
let buttonClicked = false
async function loadScene() {
scene = initScene()
customLoadingUI()
await initRobot()
dragListening()
await initAmmo()
addPhysicEffect()
initCubes()
setTimeout(() => {
hideLoadingUI()
}, 1000)
setTimeout(function () {
console.log(camera.alpha, camera.beta, camera.radius)
ArcAnimation(-1.5649881922490174, 0, 68.84955592153878)
}, 1500)
initButtons()
}
function initButtons() {
var advancedTexture = GUI.AdvancedDynamicTexture.CreateFullscreenUI("btnsUI", true, scene);
var restartBtn = GUI.Button.CreateImageOnlyButton(
"but",
url + "restart.png"
);
restartBtn.height = "60px";
restartBtn.width = "60px";
restartBtn.thickness = 0;
restartBtn.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT
restartBtn.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM
restartBtn.top = "-20px"
restartBtn.left = "-20px"
restartBtn.onPointerClickObservable.add(function () {
console.log("重启引擎")
});
speedSelect = new GUI.Rectangle("speedSelect");
speedSelect.height = "110px";
speedSelect.width = "60px";
speedSelect.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_LEFT
speedSelect.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM
speedSelect.top = "-90px"
speedSelect.left = "20px"
speedSelect.thickness = 3;
speedSelect.color = "#505781";
speedSelect.background = "white";
speedSelect.cornerRadius = 15;
speedSelect.isVisible = false
var slowImg = GUI.Button.CreateImageOnlyButton(
"slowlyBtn",
url + "turtle.png"
);
slowImg.width = "30px";
slowImg.height = "30px";
slowImg.thickness = 0;
slowImg.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP
slowImg.top = 15;
slowImg.onPointerClickObservable.add(function () {
console.log("slowImg click")
image.source = url + "turtle.png"
console.log('设置移动速度:1')
speedSelect.isVisible = false
});
var fastImg = GUI.Button.CreateImageOnlyButton(
"fastBtn",
url + "rabbit.png"
);
fastImg.width = "30px";
fastImg.height = "30px";
fastImg.thickness = 0;
fastImg.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM
fastImg.top = -15;
fastImg.onPointerClickObservable.add(function () {
console.log("fastImg click")
image.source = url + "rabbit.png"
console.log('设置移动速度:6')
speedSelect.isVisible = false
});
speedSelect.addControl(slowImg)
speedSelect.addControl(fastImg)
var speedBtn = new GUI.Button("speedBtn");
speedBtn.height = "60px";
speedBtn.width = "60px";
speedBtn.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_LEFT
speedBtn.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM
speedBtn.top = "-20px"
speedBtn.left = "20px"
speedBtn.thickness = 0;
speedBtn.onPointerClickObservable.add(function () {
speedSelect.isVisible = !speedSelect.isVisible
if(speedSelect.isVisible){
buttonClicked = true
}
});
var speedBtnBorder = new GUI.Ellipse();
speedBtnBorder.width = "60px"
speedBtnBorder.height = "60px";
speedBtnBorder.color = "#505781";
speedBtnBorder.thickness = 3;
speedBtnBorder.background = "white";
var image = new GUI.Image("currentSpeedBtn", url + "turtle.png");
image.width = "30px";
image.height = "30px";
image.thickness = 0;
speedBtn.addControl(speedBtnBorder);
speedBtn.addControl(image);
advancedTexture.addControl(restartBtn);
advancedTexture.addControl(speedBtn);
advancedTexture.addControl(speedSelect);
}
function ArcAnimation(toAlpha, toBeta, toRadius) {
let animCamAlpha = new BABYLON.Animation("animCam",
"alpha",
30,
BABYLON.Animation.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
);
let begin = 0, end = 100
let keysAlpha = [];
keysAlpha.push({
frame: begin,
value: camera.alpha
});
keysAlpha.push({
frame: end,
value: toAlpha
});
animCamAlpha.setKeys(keysAlpha)
let animCamBeta = new BABYLON.Animation("animCam", "beta", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT)
let keysBeta = []
keysBeta.push({frame: begin, value: camera.beta})
keysBeta.push({frame: end, value: toBeta})
animCamBeta.setKeys(keysBeta)
let animCamRadius = new BABYLON.Animation("animCam", "radius", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT)
let keysRadius = [];
keysRadius.push({frame: begin, value: camera.radius})
keysRadius.push({frame: end, value: toRadius})
animCamRadius.setKeys(keysRadius)
camera.animations.push(animCamAlpha, animCamBeta, animCamRadius)
scene.beginAnimation(
camera,
begin,
end,
false,
6,
() => {
console.log('camera')
}
)
}
function hideLoadingUI() {
engine.hideLoadingUI()
document.getElementById("customLoadingScreenDiv").remove()
}
function customLoadingUI() {
BABYLON.DefaultLoadingScreen.prototype.displayLoadingUI = function () {
if (document.getElementById("customLoadingScreenDiv")) {
document.getElementById("customLoadingScreenDiv").style.display = "initial";
return;
}
this._loadingDiv = document.createElement("div");
this._loadingDiv.id = "customLoadingScreenDiv";
this._loadingDiv.style.background = "#505781";
this._loadingDiv.style.height = "100%";
this._loadingDiv.style.width = "700px";
this._loadingDiv.style.alignContent = "center";
this._loadingDiv.style.zIndex = "10006"
var img = new Image()
img.src = url + "loading.gif";
img.style.padding = "15%";
img.style.paddingTop = "30%";
this._loadingDiv.appendChild(img);
this._resizeLoadingUI();
window.addEventListener("resize", this._resizeLoadingUI);
document.body.appendChild(this._loadingDiv);
};
engine.displayLoadingUI();
}
async function initAmmo() {
const Ammo = await ammo();
console.log("Ammo", Ammo)
scene.enablePhysics(new BABYLON.Vector3(0, -10, 0), new BABYLON.AmmoJSPlugin(true, Ammo));
scene.onReadyObservable.add(function () {
console.log(scene.getPhysicsEngine()._physicsPlugin.bjsAMMO.btDefaultCollisionConfiguration());
console.log(scene.getPhysicsEngine()._physicsPlugin._collisionConfiguration);
console.log(scene.getPhysicsEngine()._physicsPlugin._dispatcher);
console.log(scene.getPhysicsEngine()._physicsPlugin._solver);
console.log(scene.getPhysicsEngine()._physicsPlugin.world);
});
}
function addPhysicEffect() {
plane.physicsImpostor = new BABYLON.PhysicsImpostor(plane, BABYLON.PhysicsImpostor.BoxImpostor, {
mass: 0,
restitution: groundRestitution,
friction: groundFriction
}, scene);
var robotBody = utils.getMeshFromMeshs(car, "Glass_Plane.006")
console.log('robotBody', robotBody)
var robotSize = utils.getMeshSize(robotBody)
var robotScale = 50
const robotScalingFactor = robotScale / 10;
var physicsRoot = makePhysicsObjects(car, scene, robotScalingFactor, robotSize)
car = physicsRoot
}
function makePhysicsObjects(newMeshes, scene, scaling, size) {
var physicsRoot = new BABYLON.Mesh("robot", scene);
newMeshes.forEach((m) => {
if (m.parent == null) {
physicsRoot.addChild(m)
}
})
physicsRoot.getChildMeshes().forEach((m) => {
m.scaling.x = Math.abs(m.scaling.x)
m.scaling.y = Math.abs(m.scaling.y)
m.scaling.z = Math.abs(m.scaling.z)
m.physicsImpostor = new BABYLON.PhysicsImpostor(m, BABYLON.PhysicsImpostor.BoxImpostor, {mass: 0.1}, scene);
})
physicsRoot.scaling.scaleInPlace(scaling)
physicsRoot.physicsImpostor = new BABYLON.PhysicsImpostor(physicsRoot, BABYLON.PhysicsImpostor.NoImpostor, {
mass: bodyMass,
friction: bodyFriction,
restitution: bodyRestitution
}, scene);
const impostorOffset = -(size.y) / 1.1
physicsRoot.physicsImpostor.setDeltaPosition(new BABYLON.Vector3(0, impostorOffset, 0));
physicsRoot.position.subtractInPlace(new BABYLON.Vector3(0, -impostorOffset, 0));
return physicsRoot
}
function initCubes() {
var scale = 1
const scalingFactor = scale / 10;
cubeParent = new BABYLON.TransformNode("cubes");
const cubeHeight = 80 * scalingFactor
var cube = createBasicRoundedBox(scene, "cube", cubeHeight)
cube.position._y += cubeHeight / 2
cube.position._x -= 100
cube.material = new BABYLON.StandardMaterial("amaterial", scene);
cube.material.diffuseColor = new BABYLON.Color3(16 / 255.0, 156 / 255.0, 73 / 255.0);
cubeParent[0] = cube
var cube2 = createBasicRoundedBox(scene, "cube2", cubeHeight)
cube2.position._y += cubeHeight / 2
cube2.position._x -= 100
cube2.position._z += cubeHeight * 2
cube2.material = new BABYLON.StandardMaterial("amaterial", scene);
cube2.material.diffuseColor = new BABYLON.Color3(48 / 255.0, 102 / 255.0, 150 / 255.0);
cubeParent[1] = cube2
var cube3 = createBasicRoundedBox(scene, "cube3", cubeHeight)
cube3.position._y += cubeHeight / 2
cube3.position._x -= 100
cube3.position._z -= cubeHeight * 2
cube3.material = new BABYLON.StandardMaterial("amaterial", scene);
cube3.material.diffuseColor = new BABYLON.Color3(199 / 255.0, 88 / 255.0, 93 / 255.0);
cubeParent[2] = cube3
let actionManager = new BABYLON.ActionManager(scene);
cube.actionManager = actionManager;
cube2.actionManager = actionManager;
cube3.actionManager = actionManager;
var hl = new BABYLON.HighlightLayer("hl1", scene);
actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPointerOverTrigger, function (evn) {
var hover_cube = evn.meshUnderPointer.id
if (hover_cube == cube.name) {
hl.addMesh(cube, BABYLON.Color3.White());
} else if (hover_cube == cube2.name) {
hl.addMesh(cube2, BABYLON.Color3.White());
} else if (hover_cube == cube3.name) {
hl.addMesh(cube3, BABYLON.Color3.White());
}
}));
actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPointerOutTrigger, function (evn) {
var hover_cube = evn.meshUnderPointer.id
if (hover_cube == cube.name) {
hl.removeMesh(cube);
} else if (hover_cube == cube2.name) {
hl.removeMesh(cube2);
} else if (hover_cube == cube3.name) {
hl.removeMesh(cube3);
}
}));
scene.freezeMaterials();
}
function createBasicRoundedBox(scene, name, size) {
let mass = 0.25, restitution = 0.5, friction = 0.5
const boxSide = size;
const sphereSide = boxSide * 3.1 / 2;
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {diameter: sphereSide, segments: 16}, scene);
const box = BABYLON.Mesh.CreateBox('box', boxSide, scene);
const intersection = BABYLON.CSG.FromMesh(box).intersect(BABYLON.CSG.FromMesh(sphere));
sphere.dispose();
box.dispose();
const roundedBox = intersection.toMesh(
name,
new BABYLON.StandardMaterial('roundedBoxMaterial', scene),
scene
);
roundedBox.draggable = true;
roundedBox.physicsImpostor = new BABYLON.PhysicsImpostor(
roundedBox,
BABYLON.PhysicsImpostor.BoxImpostor,
{mass: mass, restitution: restitution, friction: friction}
);
roundedBox.material.freeze();
roundedBox.material.specularColor = new BABYLON.Color3(0, 0, 0);
roundedBox.freezeWorldMatrix()
return roundedBox;
}
function dragListening() {
var canvas = engine.getRenderingCanvas();
var currentMesh;
var getGroundPosition = function () {
var pickinfo = scene.pick(scene.pointerX, scene.pointerY, function (mesh) {
return (mesh == ground || mesh == plane);
});
if (pickinfo.hit) {
return pickinfo.pickedPoint;
}
return null;
}
var onPointerDown = function (evt) {
if (evt.button !== 0) {
return;
}
var pickInfo = scene.pick(scene.pointerX, scene.pointerY, function (mesh) {
return (mesh !== ground && mesh !== plane && mesh !== skybox);
});
if (pickInfo.hit) {
currentMesh = pickInfo.pickedMesh;
if (currentMesh.parent == null) {
console.log("no parent")
} else if (currentMesh.parent.name == car.name) {
currentMesh = currentMesh.parent
}
console.log("currentMesh", currentMesh)
startingPoint = getGroundPosition(evt);
if (startingPoint) {
setTimeout(function () {
camera.detachControl(canvas);
}, 0);
}
}
}
var onPointerMove = function (evt) {
if (!startingPoint) {
return;
}
if (!currentMesh) {
return;
}
var current = getGroundPosition(evt);
if (!current) {
return;
}
console.log('startingPoint', startingPoint)
var diff = current.subtract(startingPoint);
console.log('diff', diff)
currentMesh.position.addInPlace(diff);
console.log("currentMesh.name", currentMesh.name)
startingPoint = current;
}
var onPointerUp = function () {
if(buttonClicked){
buttonClicked = false
speedSelect.isVisible = false
}
if (startingPoint) {
camera.attachControl(canvas, true);
startingPoint = null;
return;
}
}
canvas.addEventListener("pointerdown", onPointerDown, false);
canvas.addEventListener("pointerup", onPointerUp, false);
canvas.addEventListener("pointermove", onPointerMove, false);
}
async function initRobot() {
console.log('initRobot')
const url = "http://localhost:8088/static/model/"
const modelName = "sportcar.babylon"
var result = await BABYLON.SceneLoader.ImportMeshAsync(null, url, modelName, scene);
var meshes = result.meshes
console.log("meshes", meshes)
car = meshes
}
function initScene() {
var canvas = document.getElementById("renderCanvas");
engine = new BABYLON.Engine(canvas, true);
var scene = new BABYLON.Scene(engine);
engine.runRenderLoop(function () {
scene.render();
});
window.addEventListener("resize", function () {
engine.resize();
});
camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 0, new BABYLON.Vector3(0, 0, 0), scene);
camera.alpha = 1.1383885512243588
camera.beta = 1.3642551964995249
camera.radius = 50
camera.upperBetaLimit = 1.5;
camera.lowerRadiusLimit = 50;
camera.upperRadiusLimit = 1500;
camera.wheelPrecision = 1;
camera.pinchPrecision = 20;
scene.activeCamera.panningSensibility = 100;
camera.attachControl(canvas, true);
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 10, 0), scene);
light.specular = new BABYLON.Color3(0, 0, 0);
light.intensity = 1
var materialPlane = new BABYLON.StandardMaterial("texturePlane", scene);
materialPlane.diffuseColor = new BABYLON.Color3(152 / 255.0, 209 / 255.0, 115 / 255.0)
materialPlane.backFaceCulling = false;
materialPlane.freeze()
plane = BABYLON.MeshBuilder.CreateDisc("ground", {radius: 3000}, scene);
plane.rotation.x = Math.PI / 2;
plane.material = materialPlane;
plane.position.y = -0.1;
plane.freezeWorldMatrix()
const groundSide = 144;
ground = BABYLON.Mesh.CreateGround("ground", groundSide, groundSide, 1, scene, true);
var groundMaterial = new BABYLON_MATERAIAL.GridMaterial("grid", scene);
groundMaterial.mainColor = BABYLON.Color3.White();
groundMaterial.alpha = 1;
const gridLineGray = 0.95;
groundMaterial.lineColor = new BABYLON.Color3(gridLineGray, gridLineGray, gridLineGray);
groundMaterial.backFaceCulling = true;
groundMaterial.majorUnitFrequency = 16;
groundMaterial.minorUnitVisibility = 0;
const gridOffset = 8;
groundMaterial.gridOffset = new BABYLON.Vector3(gridOffset, 0, gridOffset);
groundMaterial.freeze();
ground.material = groundMaterial
ground.freezeWorldMatrix()
var skyMaterial = new BABYLON_MATERAIAL.SkyMaterial("skyMaterial", scene);
skyMaterial.inclination = 0
skyMaterial.backFaceCulling = false;
skybox = BABYLON.Mesh.CreateBox("skyBox", 5000.0, scene);
skybox.material = skyMaterial;
return scene
}
export default {
name: "test",
data() {
return {}
},
async mounted() {
await loadScene()
},
methods: {
setCameraPosition(type, value) {
console.log(type, value)
switch (type) {
case 'alpha':
camera.alpha += value
break;
case 'beta':
camera.beta += value
break
case 'radius':
camera.radius += value
break
}
let {alpha, beta, radius} = camera
console.log(`更改后的值:${alpha},${beta},${radius}`)
}
}
}
</script>
<style scoped>
#renderCanvas {
width: 680px;
height: 680px;
touch-action: none;
z-index: 10000;
border-radius: 10px;
}
.btn {
background-color: #D9D9D9;
padding: 2px 15px;
margin: 5px;
border-radius: 4px;
width: 50px;
}
</style>
var utils = {
getMeshFromMeshs(newMeshes, name) {
var mesh = null
newMeshes.forEach(m => {
if (m.name == name) {
mesh = m
}
})
return mesh
},
getMeshSize(checkmesh) {
const sizes = checkmesh.getHierarchyBoundingVectors()
const size = {
x: (sizes.max.x - sizes.min.x),
y: (sizes.max.y - sizes.min.y),
z: (sizes.max.z - sizes.min.z)
}
return size
},
}
export default utils;
代码分解
本文要实现的功能: 1、按钮初始化 2、按钮细节优化
0.babylonjs-gui库安装与引入
npm install babylonjs-gui --save
import * as GUI from 'babylonjs-gui';
1.按钮初始化
- 要使用2d的平面按钮,需要创建一个AdvancedDynamicTexture的对象,我们需要使用该对象中的全屏模式(设置该对象会在整个的前景中)
- 速度选择的弹出窗口通过speedSelect这个按钮的isVisible属性来实现
- 通过按钮对象的onPointerClickObservable.add来添加点击回调事件,用于点击后的操作逻辑处理
function initButtons() {
var advancedTexture = GUI.AdvancedDynamicTexture.CreateFullscreenUI("btnsUI", true, scene);
var restartBtn = GUI.Button.CreateImageOnlyButton(
"but",
url + "restart.png"
);
restartBtn.height = "60px";
restartBtn.width = "60px";
restartBtn.thickness = 0;
restartBtn.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT
restartBtn.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM
restartBtn.top = "-20px"
restartBtn.left = "-20px"
restartBtn.onPointerClickObservable.add(function () {
console.log("重启引擎")
});
speedSelect = new GUI.Rectangle("speedSelect");
speedSelect.height = "110px";
speedSelect.width = "60px";
speedSelect.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_LEFT
speedSelect.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM
speedSelect.top = "-90px"
speedSelect.left = "20px"
speedSelect.thickness = 3;
speedSelect.color = "#505781";
speedSelect.background = "white";
speedSelect.cornerRadius = 15;
speedSelect.isVisible = false
var slowImg = GUI.Button.CreateImageOnlyButton(
"slowlyBtn",
url + "turtle.png"
);
slowImg.width = "30px";
slowImg.height = "30px";
slowImg.thickness = 0;
slowImg.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP
slowImg.top = 15;
slowImg.onPointerClickObservable.add(function () {
console.log("slowImg click")
image.source = url + "turtle.png"
console.log('设置移动速度:1')
speedSelect.isVisible = false
});
var fastImg = GUI.Button.CreateImageOnlyButton(
"fastBtn",
url + "rabbit.png"
);
fastImg.width = "30px";
fastImg.height = "30px";
fastImg.thickness = 0;
fastImg.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM
fastImg.top = -15;
fastImg.onPointerClickObservable.add(function () {
console.log("fastImg click")
image.source = url + "rabbit.png"
console.log('设置移动速度:6')
speedSelect.isVisible = false
});
speedSelect.addControl(slowImg)
speedSelect.addControl(fastImg)
var speedBtn = new GUI.Button("speedBtn");
speedBtn.height = "60px";
speedBtn.width = "60px";
speedBtn.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_LEFT
speedBtn.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM
speedBtn.top = "-20px"
speedBtn.left = "20px"
speedBtn.thickness = 0;
speedBtn.onPointerClickObservable.add(function () {
speedSelect.isVisible = !speedSelect.isVisible
if(speedSelect.isVisible){
buttonClicked = true
}
});
var speedBtnBorder = new GUI.Ellipse();
speedBtnBorder.width = "60px"
speedBtnBorder.height = "60px";
speedBtnBorder.color = "#505781";
speedBtnBorder.thickness = 3;
speedBtnBorder.background = "white";
var image = new GUI.Image("currentSpeedBtn", url + "turtle.png");
image.width = "30px";
image.height = "30px";
image.thickness = 0;
speedBtn.addControl(speedBtnBorder);
speedBtn.addControl(image);
advancedTexture.addControl(restartBtn);
advancedTexture.addControl(speedBtn);
advancedTexture.addControl(speedSelect);
}
2.按钮细节优化
- 按钮组是在2d前景的界面中,而原来的3d界面的点击或操作并不会触发2d前景界面的事件回调,所以当2d速度选择窗口显示后,再操作3d界面,这时速度选择窗口并不回收起。
- 本文中通过全局的变量来判断速度选择窗口是否显示,如果显示,则在操作3d界面时触发的点击响应中将速度选择窗口隐藏
var onPointerUp = function () {
if(buttonClicked){
buttonClicked = false
speedSelect.isVisible = false
}
}
后续计划
Babylon.js
Blockly
- 入门使用blockly
- 自定义block块
- blockly第三方组件使用
- 接入js-interpreter,步骤运行block块
- …(想到啥写啥)
开源项目GitHub链接
https://github.com/Wenbile/Child-Programming-Web
资源下载链接
你的点赞是我继续编写的动力
|