美文网首页
Vue实现图形化积木式编程(一)

Vue实现图形化积木式编程(一)

作者: 温温温B | 来源:发表于2021-08-13 17:15 被阅读0次

    前言

    前段时间想要做一个web端的图形化积木式编程(类似少儿编程)的案例,网上冲浪了一圈又一圈,终于技术选型好,然后代码一顿敲,终于出来了一个雏形。

    TIPS:该案例设计主要参考iRobot Coding,只用做学习用途,侵删。

    https://code.irobot.com/#/

    最终实现效果

    最终实现效果

    本文实现效果

    • 可移动相机视角查看3d模型


      可移动相机视角

    技术选型

    1.前端

    • vuetify - 基于vue的界面框架

    • babylon.js - 3d图形引擎

    • ammo.js - 物理引擎库

    • blockly - 模块化编程工具

    1. 后端
    • ThinkJS - 基于Node.js的后端框架

    完整代码

    • 一个完整的vue文件
    <template>
      <div style="height: 100%;width: 100%;">
        <div>
          <canvas id="renderCanvas"></canvas>
        </div>
      </div>
    </template>
    
    <script>
    import * as BABYLON from 'babylonjs';
    import * as BABYLON_MATERAIAL from "babylonjs-materials"
    
    function loadScene() {
      //获取到renderCanvas这个元素
      var canvas = document.getElementById("renderCanvas");
      //初始化引擎
      var engine = new BABYLON.Engine(canvas, true);
      //初始化场景
      var scene = new BABYLON.Scene(engine);
      //注册一个渲染循环来重复渲染场景
      engine.runRenderLoop(function () {
        scene.render();
      });
      //浏览器窗口变化时监听
      window.addEventListener("resize", function () {
        engine.resize();
      });
    
      //相机初始化
      var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 5, new BABYLON.Vector3(0, 0, 10), scene);
      camera.setPosition(new BABYLON.Vector3(20, 200, 400));
      //相机角度限制
      camera.upperBetaLimit = 1.5;//最大z轴旋转角度差不多45度俯瞰
      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()
      var 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;
      var 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 blueBox = BABYLON.Mesh.CreateBox("blue", 10, scene);
      var blueMat = new BABYLON.StandardMaterial("ground", scene);
      blueMat.diffuseColor = new BABYLON.Color3(0.4, 0.4, 0.4);
      blueMat.specularColor = new BABYLON.Color3(0.4, 0.4, 0.4);
      blueMat.emissiveColor = BABYLON.Color3.Blue();
      // blueMat.wireframe = true;//网格状
      blueBox.material = blueMat;
      //起始位置坐标
      blueBox.position.x = 0;
      blueBox.position.y = 5;
      blueBox.position.z = 0;
    
      //天空盒初始化
      var skyMaterial = new BABYLON_MATERAIAL.SkyMaterial("skyMaterial", scene);
      skyMaterial.inclination = 0
      skyMaterial.backFaceCulling = false;
      var skybox = BABYLON.Mesh.CreateBox("skyBox", 5000.0, scene);
      skybox.material = skyMaterial;
    }
    
    
    export default {
      name: "test",
      data() {
        return {
        }
      },
      mounted() {
        //加载场景
        loadScene()
      },
    }
    </script>
    
    <style scoped>
    #renderCanvas {
      width: 680px;
      height: 680px;
      touch-action: none;
      z-index: 10000;
      border-radius: 10px;
    }
    </style>
    

    babylonjs入门场景学习

    0、npm安装相关依赖
    npm install babylonjs babylonjs-gui babylonjs-loaders babylonjs-materials --save
    
    • 安装的模块在package.json中生成
    "dependencies": {
     {
        "babylonjs": "^4.2.0",//babylon核心库
        "babylonjs-gui": "^4.2.0",//UI界面库(按钮等)
        "babylonjs-loaders": "^4.2.0",//开机加载库(修改启动动画的)
        "babylonjs-materials": "^4.2.0"//材质库,有些材质像是SkyMaterial,babylonjs库的默认材质中是没有这个对象的
    }
    
    1、引入模块
    import * as BABYLON from 'babylonjs';
    import * as BABYLON_MATERAIAL from "babylonjs-materials"
    
    2、场景初始化
    • template中
    <div>
         <canvas id="renderCanvas"></canvas>
    </div>
    
    • css中
    #renderCanvas {
      width: 680px;
      height: 680px;
      touch-action: none;
      z-index: 10000;
      border-radius: 10px;
    }
    
    • js中
    //获取到renderCanvas这个元素
    var canvas = document.getElementById("renderCanvas");
    //初始化引擎
    var engine = new BABYLON.Engine(canvas, true);
    //初始化场景
    var scene = new BABYLON.Scene(engine);
    //注册一个渲染循环来重复渲染场景
    engine.runRenderLoop(function () {
      scene.render();
    });
    //浏览器窗口变化时监听
    window.addEventListener("resize", function () {
      engine.resize();
    });
    
    3、ArcRotateCamera 相机初始化
    var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 5, new BABYLON.Vector3(0,0,10), scene);
    camera.setPosition(new BABYLON.Vector3(20, 200, 400));
    //相机角度限制
     camera.upperBetaLimit = 1.5;//最大z轴旋转角度差不多45度俯瞰
    camera.lowerRadiusLimit = 50;//最小缩小比例
    camera.upperRadiusLimit = 1500;//最大放大比例
    //变焦速度
    camera.wheelPrecision = 1; //电脑滚轮速度 越小灵敏度越高
    camera.pinchPrecision = 20; //手机放大缩小速度 越小灵敏度越高
    scene.activeCamera.panningSensibility = 100;//右键平移灵敏度
    // 将相机和画布关联
    camera.attachControl(canvas, true);
    
    4、灯光初始化
    //设置半球光
    var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 10, 0), scene);
    //设置高光颜色
    light.specular = new BABYLON.Color3(0, 0, 0);
    //设置灯光强度
    light.intensity = 1
    
    5、地面初始化
    5.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;//Allways show the front and the back of an element
    materialPlane.freeze()
    var plane = BABYLON.MeshBuilder.CreateDisc("ground", {radius: 6000}, scene);
    plane.rotation.x = Math.PI / 2;
    plane.material = materialPlane;
    plane.position.y = -0.01;
    plane.freezeWorldMatrix()
    
    5.2、网格地面
    //地板
      const groundSide = 144;
      var ground = BABYLON.Mesh.CreateGround("ground", groundSide, groundSide, 1, scene, true);
      var groundMaterial = new BABYLON_MATERAIAL.GridMaterial("grid", scene);
      groundMaterial.freeze(); // Optimization.
      groundMaterial.mainColor = BABYLON.Color3.White();//底板颜色
      groundMaterial.alpha = 1;//透明度
      const gridLineGray = 0.95;
      groundMaterial.lineColor = new BABYLON.Color3(gridLineGray, gridLineGray, gridLineGray);
      groundMaterial.backFaceCulling = true; // Change this if the back of the pad needs to be visible.
      //大网格间距
      groundMaterial.majorUnitFrequency = 16;
      //小网格间距
      groundMaterial.minorUnitVisibility = 0;
      const gridOffset = 0; // This makes the grid cells to be aligned with the pad's borders.
      groundMaterial.gridOffset = new BABYLON.Vector3(gridOffset, 0, gridOffset);
      ground.material = groundMaterial
      ground.freezeWorldMatrix()
    
    • TIPS:很多同学会发现,将两个平面叠加时,移动相机视角,会出现虫影现象
    两平面叠加虫影
    • 优化方案
    //1、冻结材质和模型
    //绿地
    //设置为静态网格,freezeWorldMatrix之后,改变postion、rotation是无效的
    plane.freezeWorldMatrix()
    //将材质冻结
    plane.material.freeze()
    //网格
    ground.freezeWorldMatrix()
    ground.material.freeze()
    //2、增大两个物体的y轴间距
    plane.position.y = -0.1
    
    6、正方体物体初始化
     //添加物体
    var blueBox = BABYLON.Mesh.CreateBox("blue", 10, scene);
    var blueMat = new BABYLON.StandardMaterial("ground", scene);
    blueMat.diffuseColor = new BABYLON.Color3(0.4, 0.4, 0.4);
    blueMat.specularColor = new BABYLON.Color3(0.4, 0.4, 0.4);
    blueMat.emissiveColor = BABYLON.Color3.Blue();
    // blueMat.wireframe = true;//网格状
    blueBox.material = blueMat;
    //起始位置坐标
    blueBox.position.x = 0;
    blueBox.position.y = 5;
    blueBox.position.z = 0;
    
    7、天空盒初始化
    //天空盒初始化
    var skyMaterial = new BABYLON_MATERAIAL.SkyMaterial("skyMaterial", scene);
    skyMaterial.inclination = 0
    skyMaterial.backFaceCulling = false;
    var skybox = BABYLON.Mesh.CreateBox("skyBox", 5000.0, scene);
    skybox.material = skyMaterial;
    

    后续计划

    Babylon.js

    • 加载网络模型
    • 点击移动物体
    • 自定义启动界面
    • 初始化摄像机动画
    • 物体重力效果
    • babylonjs-gui 按钮实现
    • babylonjs+ammojs 碰撞体实现
    • 将3d界面放入可拖动窗口中

    Blockly

    • 入门使用blockly
    • 自定义block块
    • blockly第三方组件使用
    • 接入js-interpreter,步骤运行block块
    • ......(想到啥写啥)

    开源项目GitHub链接

    https://github.com/Wenbile/Child-Programming-Web

    你的点赞是我继续编写的动力

    相关文章

      网友评论

          本文标题:Vue实现图形化积木式编程(一)

          本文链接:https://www.haomeiwen.com/subject/itwdbltx.html