How to Build a Physics‑Powered Basketball Mini‑Game with LayaAir and Matter.js
This tutorial walks through creating a web‑based basketball shooting game by initializing LayaAir, integrating the Matter.js physics engine, handling user gestures to draw aim lines, applying forces to a ball, and adding realistic basket components, while sharing practical code snippets and performance tips.
Preparation
The project is a short‑term activity for a Tencent mobile‑recharge promotion, where users earn extra shooting attempts by topping up or sharing. The game uses a draggable guide line to set shot power and angle, and relies on a physics engine for realistic ball behavior.
The chosen engine is LayaAir , which offers extensive documentation, supports 2D/3D/VR, multiple languages (AS, TS, JS), responsive community support, and built‑in tools for packaging, animation conversion, and asset management.
For physics, Matter.js is used because it is lightweight, well‑documented, easy to use, and has a higher GitHub star count than alternatives.
Initialization
1. Initialize LayaAir
Set up a 1334×750 canvas in WebGL mode, enable anti‑aliasing with Config.isAntialias = true, and configure screen adaptation via screenMode.
Config.isAntialias = true;
Laya.init(1334, 750, Laya.WebGL);
Laya.stage.alignV = 'top';
Laya.stage.alignH = 'middle';
Laya.stage.screenMode = this.Stage.SCREEN_HORIZONTAL;
Laya.stage.scaleMode = "fixedwidth"; // other modes: noscale, exactfit, etc.2. Initialize Matter.js and add the scene
Create the physics engine, world, and renderer, disable sleeping and wireframe mode, then load simplified background assets.
this.engine = Matter.Engine.create({ enableSleeping: true });
var world = this.engine.world;
Matter.Engine.run(this.engine);
var render = LayaRender.create({ engine: this.engine, options: { wireframes: false, background: "#000" } });
LayaRender.run(render);Load background image:
var bg = new this.Sprite();
Laya.stage.addChild(bg);
bg.pos(0, 0);
bg.loadImage('images/bg.jpg');Gameplay Mechanics
3. Draw the guide line and compute angle & distance
Listen for MOUSE_DOWN, MOUSE_MOVE, and MOUSE_UP events to draw a line. Use the helper functions getRad and getDistance to calculate the shot angle and power from the line’s start and end coordinates.
var line = new this.Sprite();
Laya.stage.addChild(line);
Laya.stage.on(this.Event.MOUSE_DOWN, this, function(e){ /* start line */ });
Laya.stage.on(this.Event.MOUSE_MOVE, this, function(e){ /* update line */ });
Laya.stage.on(this.Event.MOUSE_UP, this, function(e){ /* finalize line */ }); function getRad(x1, y1, x2, y2){
var x = x2 - x1;
var y = y2 - y1;
var hyp = Math.sqrt(Math.pow(x,2) + Math.pow(y,2));
var angle = x / hyp;
var rad = Math.acos(angle);
if (y2 < y1) rad = -rad;
return rad;
}
function getDistance(x1, y1, x2, y2){
return Math.sqrt(Math.pow(x1 - x2,2) + Math.pow(y1 - y2,2));
}4. Generate the basketball and apply force
On MOUSE_UP, create a dynamic circular body with appropriate density, restitution, and sprite texture, then apply a force derived from the calculated distance and angle.
addBall: function(x, y){
var ball = Matter.Bodies.circle(500, 254, 28, {
isStatic: false,
density: 0.68,
restitution: 0.8,
render: { visible: true, sprite: { texture: 'images/ball.png', xOffset: 28, yOffset: 28 } }
});
Matter.Body.applyForce(ball, ball.position, {x: x, y: y});
Matter.World.add(this.engine.world, [ball]);
}5. Add basket, net, and walls
Use Matter.js bodies to create static rectangles for the backboard and walls, and a soft body for the net. Set friction, air friction, visibility, and collision filters as needed.
addBody: function(){
var group = Matter.Body.nextGroup(true);
var netBody = Matter.Composites.softBody(1067,164,6,4,0,0,false,8.5,{ firction:1, frictionAir:0.08, restitution:0, render:{visible:false}, collisionFilter:{group:group} },{render:{lineWidth:2,strokeStyle:"#fff"}});
netBody.bodies[0].isStatic = netBody.bodies[5].isStatic = true;
var backboard = Matter.Bodies.rectangle(1208,120,50,136,{isStatic:true,render:{visible:true}});
var backboardBlock = Matter.Bodies.rectangle(1069,173,5,5,{isStatic:true,render:{visible:true}});
Matter.World.add(this.engine.world, [/* walls */]);
Matter.World.add(this.engine.world, [netBody, backboard, backboardBlock]);
}6. Detect scoring and manage sleeping balls
Listen to the engine’s tick event to check the ball’s position; if it falls within the basket’s bounds for more than two frames, log a successful shot. Use the sleepStart event to remove balls that become idle, preventing performance degradation.
Matter.Events.on(this.engine, 'tick', function(){
countDown++;
if (ball.position.x > 1054 && ball.position.x < 1175 && ball.position.y > 170 && ball.position.y < 180 && countDown > 2){
countDown = 0;
console.log('Ball scored!');
}
});
Matter.Events.on(ball, 'sleepStart', function(){
Matter.World.remove(this.engine.world, ball);
});With these steps, a functional basketball shooting mini‑game is completed using LayaAir for rendering and Matter.js for physics.
References
Matter.js official documentation
LayaAir demo repository
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Aotu Lab
Aotu Lab, founded in October 2015, is a front-end engineering team serving multi-platform products. The articles in this public account are intended to share and discuss technology, reflecting only the personal views of Aotu Lab members and not the official stance of JD.com Technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
