今天!!是令人兴奋的实作!!不再是单纯呼叫API文件的范例码,而是具备意义/情境的程序。之後只要出现这个标题,表示我们要用学到的知识分阶段来完成我们的弹珠台。
记得我们 Day3 的时候曾经开过一系列的需求吗?没关系,我相信快一个礼拜的现在,大家都忘了差不多了,我们这边帮大家 Recap 一下需求:
没错,粗体的就是我们今天要来完成的项目!好的,我们话不多说,直接开始吧!
今天会专注在世界的基础建造,也就是初始化。
一开始的共用模组跟一些常数我会统一宣告在外层全域。
// Global Settings / Variables
var Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
Bodies = Matter.Bodies,
Composite = Matter.Composite,
Events = Matter.Events,
BodyM = Matter.Body,
Vertices = Matter.Vertices;
var engine;
var render;
var runner;
const canvasWidth = 400;
const canvasHeigh = 500;
const blockSize = 20;
const mainBallRadius = 15;
const minimumBlockGenerateX = 50;
const minimumBlockGenerateY = 100;
const blockSeparateX = canvasWidth - minimumBlockGenerateX - 50;
const blockSeparateY = canvasHeigh - minimumBlockGenerateY - 100;
const minimumDistanceBetweenBlocks = 60;
再来过往我们直接做的初始化世界,我们用一个 init 的 function 来包住,这边笔者多设想了一个重置世界的 case,所以才会选择包起来。
function init()
{
engine = Engine.create();
render = Render.create({
element: document.body,
engine: engine,
options:{
wireframes:false,
showIds:true,
background: '#bfe9f5', //"#bfe9f5"
width:canvasWidth,
height:canvasHeigh
}
});
formHiddenWall();
formMainBall();
formRandomBlocks(15);
Render.run(render);
runner = Runner.create();
}
~~好的!我们今天完工了!~~我们今天本来就是初始化世界就差不多完成需求了,所以其实所有要做的事情都写在 init 里了,我们带大家看一下我们怎麽初始世界的,在 init 的流程如果用白话来说会是:
创建引擎与渲染相关物件 → [[加入引擎中的世界] 加入左右两侧的墙防止球跑出去 → 加入主要的球体 → 加入随机产生的柱子] → 让渲染跑起来 → 预先宣告runner但不让它跑
完成以上後应该会看到如同我们今天一开始贴的图,会是静止的球在空中,当你按下 Run the runner 的按钮,球就会掉下来了!
我们从墙的创建开始看 - formHiddenWall()
function formHiddenWall()
{
var wallLeft = Bodies.rectangle(-21, canvasHeigh/2, 40, canvasHeigh, { isStatic: true });
var wallRight = Bodies.rectangle(canvasWidth+21, canvasHeigh/2, 40, canvasHeigh, { isStatic: true });
Composite.add(engine.world, [wallLeft,wallRight]);
}
墙的创建算是最单纯的,基本上就是参考 canvas 的属性来决定墙的位置与长宽,位置要注意因为给的座标是墙的中心,所以会用这种取半的写法来让墙不要显示在画面内。
另外因为是一道不能移动的墙,我们在 options 中会设定 isStatic: true ,来表示它是一个静止的物体。
最後再加入 world 中就大功告成了!
第二个是我们的主角,球 - formMainBall()
function formMainBall()
{
const mainBallInitX = canvasWidth/2;
const mainBallInitY = 50;
var mainBall = Bodies.circle(mainBallInitX, mainBallInitY, mainBallRadius, options = {
restitution: 1,
render:{
fillStyle:"#FFFFFF"
}
}, 100);
Composite.add(engine.world, [mainBall]);
}
球的话要注意的是球的初始位置,我们会放在画面的中上方,render 因为是会被看到的,我们设成白色的,另外,因为我们预期球是会发生弹跳的,这边我们给 options 中的 restitution 值,可以依据个人喜好决定弹性碰撞的程度,范例中我们用 1 来做完全弹性碰撞。
最後是比较麻烦的乱数生成方块 - formRandomBlocks(blockCount)
function formRandomBlocks(blockCount)
{
var blockOptions ={
render : {
fillStyle : "#569cd8",
},
isStatic : true,
angle : getRadiusByDegree(45)
};
var blockCoordinateList = [];
for(var i=0; i<blockCount; i++)
{
var blockCoordinate = getRandomCoordinateForBlocks(blockCoordinateList);
var block = Bodies.rectangle(blockCoordinate.x, blockCoordinate.y, blockSize, blockSize, blockOptions);
blockCoordinateList.push(blockCoordinate);
Composite.add(engine.world, [block]);
}
}
方块本身的建立是相对单纯的,麻烦的会是考量弹珠台的实作,球不能被卡在方块中间,所以我们会定义一个方块最小相距常数,在乱数产生的函式中( getRandomCoordinateForBlocks )会让方块彼此间的距离不要太近。另外在 options 比较多的时候,我们可以像这里一样把 options 抽成单独一个变数,会比较乾净也能和其他物体共用(如果有需要的话啦),我们这边设定的 options依序是蓝色填充、静止物体、旋转角度45度。
流程上来说会是
设定方块创建选项 → 宣告一个具有所有方块座标的阵列(用於检查距离) → [[回圈产生指定数量的方块] → 随机产生符合距离规范的方块座标 → 用 Bodies 中的方法以及参数创建方块 → 将方块加入世界中]
细部的函示笔者就不走进去细节了,大家可以先看看理解一下,或是尝试自己实作。
完成你的弹珠台後,可以按下 Run the runner 的按键,检视你今天的杰作!
嘿,球动起来了!它和那些方块碰碰撞撞!
我们最後加码一个,如果我们要 reset 的话会怎麽做?我们看到 reInit 这个函式:
function reInit()
{
event.preventDefault();
Engine.clear(engine);
Render.stop(render);
Runner.stop(runner);
render.canvas.remove();
render.canvas = null;
render.context = null;
render.textures = {};
init();
}
这边依序是终止了发生中事件、清除引擎、停止渲染、停止跑动回圈,移除对应的canvas与纹理 ─ 最後,我们在执行一次我们一开始抽出来的初始化函式!
嘿!按下 Re Init的按钮,世界就这样又重新来过了!你可以一次次的让球重新掉落、重新生成碰撞方块了!
我们的弹珠台看起来已经有模有样了,不是吗?明天,让我们来熟悉其他的模组,一起为完成弹珠台继续努力!
<<: [Day_10]资料储存容器(3) - 字典(dict)
>>: 【Day 15】CodePipeline x 老实的人别去大阪 x 老菜卜玩东京
缺乏计画的目标,只能叫做愿望。 A goal without a plan is just a wi...
-VLAN组(来源:Cisco Press) VLAN是一种创建其广播域的网络分段和隔离机制。路由...
Colab连结 今天大家介绍 Gradient Exploding (梯度爆炸) 与 Gradien...
本篇重点 Order & Deal Event 委托单失败OrderState内容 官方说明...
本次要来介绍如何建立Android Studio上的模拟器,以及有哪些优缺点。 首先我认为最大的优点...