使用matterjs库实现打砖块
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
之前我介绍过一个物理引擎库matterjs,文章在这,今天我们用它去实现一个小案例,做一个简易版的打砖块。
1. 初始化引擎
import Matter from "matter-js";
const Bodies = Matter.Bodies,
Body = Matter.Body,
Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
Events = Matter.Events,
Composite = Matter.Composite,
Composites = Matter.Composites
const engine = Engine.create()
const render = Render.create({
engine,
element: document.body,
options: {
wireframes: false,
showCollisions: true
}
})
engine.gravity.y = 0
engine.timing.timeScale = 0.5
Render.run(render)
const runner = Runner.create()
Runner.run(runner, engine)
这里要注意:重力设为0,不然小球会下落,时间缩放为0.5,防止小球移动的太快。
2. 创建地图图块
这里我们要注意要创建两种图块,一种是可以摧毁的,一种是不可摧毁的。
所用api:
- Composites.stack(x, y, columns, rows, columnGapNumber, rowGapNumber, callback) :创建一个起始点为(x,y),rows行,columns列的网格组合体 ,columnGapNumber和rowGapNumber分别是行列的间隔。
封装创建墙体的函数:
const createWalls = function(x:number, y:number, columns: number, rows: number) {
return Composites.stack(x, y, columns, rows, 0, 0, function(x: number, y: number) {
return Bodies.rectangle(x, y, 10, 10, {
isStatic: true,
label: 'wall',
friction: 0,
frictionStatic: 0,
render: {
fillStyle: '#aaa'
}
})
})
}
const createRect = function(x:number, y:number, columns: number, rows: number) {
return Composites.stack(x, y, columns, rows, 0, 0, function(x: number, y: number) {
return Bodies.rectangle(x, y, 10, 10, {
isStatic: true,
friction: 0,
frictionStatic: 0,
label: 'rect',
render: {
strokeStyle: 'green'
}
})
})
}
摩擦力最好设为0,不然小球在碰撞过程会损失动能。
3. 绘制地图、小球和平台
调用上面封装的方法,创建地图图块和初始化平台小球的位置。
const topWall = createWalls(0, 0, render.canvas.width / 10, 1)
const leftWall = createWalls(0, 10, 1, render.canvas.height / 10)
const rightWall = createWalls(render.canvas.width - 10, 0, 1, render.canvas.height / 10)
const bottomWall1 = createWalls(10, render.canvas.height/ 2, 30, 1)
const bottomWall2 = createWalls(500, render.canvas.height/ 2, render.canvas.width/ 6, 1)
const rects = createRect(10, 10, render.canvas.width / 10 - 2, render.canvas.height / 2 / 10 - 1)
const platform = Bodies.rectangle(400, render.canvas.height - 20, 100, 10, {
isStatic: true,
friction: 0.5,
frictionStatic: 1,
label: 'platform',
render: {
fillStyle: '#fff'
}
})
const ball = Bodies.circle(400, render.canvas.height - 30, 5, {
label: 'ball',
friction: 0,
frictionAir: 0,
frictionStatic: 0,
mass: 0,
restitution: 1,
render: {
fillStyle: 'red',
}
})
Composite.add(engine.world, [topWall, leftWall, rightWall, bottomWall1, bottomWall2, rects, platform, ball])
4. 处理小球发射和平台移动
所用api:
- Body.setPosition(object, position):设置物体位置。
- Body.applyForce(object, object.position, Vector vector):给物体施加一个vector方向的力。
document.body.addEventListener('keydown', function(e: KeyboardEvent) {
if(e.key === 'a') {
Body.setPosition(platform, {
x: platform.position.x - 30,
y: platform.position.y
})
} else if(e.key === 'd') {
Body.setPosition(platform, {
x: platform.position.x + 30,
y: platform.position.y
})
}
if(e.key === ' ') {
Body.applyForce(ball, ball.position, {
x: 0,
y: -0.01
})
}
})
5. 监听小球碰撞事件
监听小球碰撞,如果小球碰撞到砖块则砖块销毁。
- Composite.remove(parent, object):移除parent中的object。
- Events.on(engine, 'collisionStart', function(e) {}):监听碰撞事件,
e.pairs是一个碰撞组,里面包含所有的碰撞物体对。
Events.on(engine, 'collisionStart', function(e) {
const pairs = e.pairs;
pairs.forEach(pair => {
if(pair.bodyB.label === 'ball') {
if(pair.bodyA.label === 'rect') {
Composite.remove(rects, pair.bodyA)
}
}
})
})
6. 最后
这里只是实现了最简单的打砖块小游戏,大家可以扩展思路,制作更好玩的地图和关卡,也可以加一些道具,比如小球分裂,爆炸小球等等。最后本demo全程只用matterjs实现,如有错误请大佬指正。