How to prevent sprites to be moved by mouse in Matter.js? - javascript

I am using Matter.js to build games. Starting from one of their examples: Pyramid, I started playing around. The code fro this specific example is as follows:
var Example = Example || {};
Example.pyramid = function() {
var Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies;
// create engine
var engine = Engine.create(),
world = engine.world;
// create renderer
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 600,
showAngleIndicator: true
}
});
Render.run(render);
// create runner
var runner = Runner.create();
Runner.run(runner, engine);
// add bodies
var stack = Composites.pyramid(100, 258, 15, 10, 0, 0, function(x, y) {
return Bodies.rectangle(x, y, 40, 40);
});
World.add(world, [
stack,
// walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(800, 300, 50, 600, { isStatic: true }),
Bodies.rectangle(0, 300, 50, 600, { isStatic: true }),
Bodies.rectangle(400, 605, 800, 50, { isStatic: true })
]);
// add mouse control
var mouse = Mouse.create(render.canvas),
mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
stiffness: 0.2,
render: {
visible: false
}
}
});
World.add(world, mouseConstraint);
// keep the mouse in sync with rendering
render.mouse = mouse;
// fit the render viewport to the scene
Render.lookAt(render, {
min: { x: 0, y: 0 },
max: { x: 800, y: 600 }
});
// context for MatterTools.Demo
return {
engine: engine,
runner: runner,
render: render,
canvas: render.canvas,
stop: function() {
Matter.Render.stop(render);
Matter.Runner.stop(runner);
}
};
};
I am trying to modify the code in order to disallow the sprites in the pyramid to be moved by mouse. The part of code responsible for creating those objects is this:
// add bodies
var stack = Composites.pyramid(100, 258, 15, 10, 0, 0,
function(x, y) {
return Bodies.rectangle(x, y, 40, 40);
});
I have tried searching among the options that can be passed to Bodies.rectangle, but found nothing.
How to achieve this?

You can use the collisionFilter property to do this.
Collision filters use groups, masks and categories to determine which bodies collide. I'll ignore groups in this example. By default, bodies belong to category 1 and have all masked enabled:
const objValsToBase = (o, base) =>
Object.fromEntries(Object.entries(o)
.map(([k, v]) => [k, v.toString(base)]));
const circle = Matter.Bodies.circle(200, 100, 50);
// show decimal (base 10)
console.log(circle.collisionFilter);
// show bits (base 2)
console.log(objValsToBase(circle.collisionFilter, 2));
// show hex (base 16)
console.log(objValsToBase(circle.collisionFilter, 16));
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
To get two bodies to stop colliding, you can set the category on one group to 0b10, which is 0x2 in hex and 2 in decimal, and set the mask on the other to turn off the 2 bit: 0b01. 0b01 also disables collisions you might have with other categories which are unused by default but might be used in your code sooner or later, so you might prefer 0xfffffd since (0xd).toString(2) => '1101'.
Below is the pyramid example code modified to disable mouse-pyramid collision. I also ran it through prettier and added a circle that collides with everything to show that the mouse still works on that:
var Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies;
// create engine
var engine = Engine.create(),
world = engine.world;
// create renderer
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 600,
showAngleIndicator: true,
},
});
Render.run(render);
// create runner
var runner = Runner.create();
Runner.run(runner, engine);
// add bodies
var stack = Composites.pyramid(
100,
258,
15,
10,
0,
0,
function (x, y) {
return Bodies.rectangle(x, y, 40, 40, {
collisionFilter: {mask: 0xfffffd}, // <--
});
}
);
World.add(world, [
stack,
// walls
Bodies.circle(200, 100, 50),
Bodies.rectangle(400, 0, 800, 50, {isStatic: true}),
Bodies.rectangle(800, 300, 50, 600, {isStatic: true}),
Bodies.rectangle(0, 300, 50, 600, {isStatic: true}),
Bodies.rectangle(400, 605, 800, 50, {isStatic: true}),
]);
// add mouse control
var mouse = Mouse.create(render.canvas),
mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
collisionFilter: {category: 0b10}, // <--
constraint: {
stiffness: 0.2,
render: {
visible: false,
},
},
});
World.add(world, mouseConstraint);
// keep the mouse in sync with rendering
render.mouse = mouse;
// fit the render viewport to the scene
Render.lookAt(render, {
min: {x: 0, y: 0},
max: {x: 800, y: 600},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
See also:
How can I change the collisionFilter of an object so it can no longer interact with MouseConstraint (MatterJS)
How to get only a single body to move using Matter-js Mouse
How to prevent sprites to be moved by mouse in Matter.js? #681

Related

Implementing bullet collisions in Matter.js for a shooting game

I am trying to make a shooting game in matter.js but can't find a way to shoot bullets from the player's exact location and how to count the collision between player and bullet but not with the walls.
I want to fire a bullet from player1 and then on pressing D again it should fire another bullet from the player1's last position.
My Codepen of this game
let p1= Matter.Bodies.polygon(200, 200, 3, 40, {
chamfer: {
radius: [15,10,15]
},
isStatic: false,
inertia: Infinity,
friction: 0.9,
render: {
fillStyle: '#F9ED69'
},
mass:1
});
let p2 = Matter.Bodies.polygon(1100, 200, 3, 40, {
chamfer: {
radius: [15,10,15]
},
isStatic: false,
inertia: Infinity,
friction: 0.9,
render: {
fillStyle: '#11999E'
},
mass:1
});
let bullet1 = Matter.Bodies.polygon(400, 300, 3, 7, {
chamfer: {
radius: [4,2,4]
},
isStatic: false,
inertia: Infinity,
friction: 0.9,
render: {
fillStyle: '#F9ED69'
},
mass:0
});
const keyHandlers = {
KeyS: () => {
Matter.Body.applyForce(p1, {
x: p1.position.x,
y: p1.position.y
}, {x: 0.0, y: 0.001})
},
KeyW: () => {
Matter.Body.applyForce(p1, {
x: p1.position.x,
y: p1.position.y
}, {x: 0.0, y: -0.002})
},
KeyD:()=>{
Matter.Body.applyForce(bullet1, {
x: p1.position.x,
y: p1.position.y
}, {x: 0.001, y: 0.0})
},
};
const keysDown = new Set();
document.addEventListener("keydown", event => {
keysDown.add(event.code);
});
document.addEventListener("keyup", event => {
keysDown.delete(event.code);
});
Matter.Events.on(engine, "beforeUpdate", event => {
[...keysDown].forEach(k => {
keyHandlers[k]?.();
});
});
// on collision of a bullet with wall and other bodies remove the bullet from the world after some delay and add the score
let score1 = 0;
let score2 = 0;
let health
Matter.Events.on(engine, "collisionStart", event => {
for (let i = 0; i < event.pairs.length; i++) {
const pair = event.pairs[i];
if (pair.bodyA === bullet1 || pair.bodyB === bullet1) {
Matter.World.remove(engine.world, bullet1);
alert('1');
}
if (pair.bodyA === bullet2 || pair.bodyB === bullet2) {
Matter.World.remove(engine.world, bullet2);
alert('2');
}
}
score1++;
alert(`SCore1 is ${score1}`); // these alerts are just to confirm the collision
});
You're on the right track, but if you hardcode bullet1 and bullet2 you're stuck with just those two bullets. Even with a fixed number of bullets and re-using the bodies (good for performance but maybe premature optimization), I'd probably use an array to store these bullets, which is almost always the correct move after you catch yourself doing thing1, thing2...
Here's a proof of concept. I'm creating and destroying bullets here to keep the coding easier, but it'd be more performant to keep a pool of objects and re-use/re-position them.
I'm also using sets to keep track of the types of the bodies, but you might want to use labels. Most of the code here could go in many different directions, specific to your use case.
const engine = Matter.Engine.create();
engine.gravity.y = 0; // enable top-down
const map = {width: 300, height: 300};
const render = Matter.Render.create({
element: document.body,
engine,
options: {...map, wireframes: false},
});
const player = {
score: 0,
body: Matter.Bodies.polygon(
map.width / 2, map.height / 2, 3, 15, {
frictionAir: 0.06,
density: 0.9,
render: {fillStyle: "red"},
},
),
lastShot: Date.now(),
cooldown: 150,
fireForce: 0.1,
rotationAngVel: 0.03,
rotationAmt: 0.03,
rotateLeft() {
Matter.Body.rotate(this.body, -this.rotationAmt);
Matter.Body.setAngularVelocity(
this.body, -this.rotationAngVel
);
},
rotateRight() {
Matter.Body.rotate(this.body, this.rotationAmt);
Matter.Body.setAngularVelocity(
this.body, this.rotationAngVel
);
},
fire() {
if (Date.now() - this.lastShot < this.cooldown) {
return;
}
// move the bullet away from the player a bit
const {x: bx, y: by} = this.body.position;
const x = bx + (Math.cos(this.body.angle) * 10);
const y = by + (Math.sin(this.body.angle) * 10);
const bullet = Matter.Bodies.circle(
x, y, 4, {
frictionAir: 0.006,
density: 0.1,
render: {fillStyle: "yellow"},
},
);
bullets.add(bullet);
Matter.Composite.add(engine.world, bullet);
Matter.Body.applyForce(
bullet, this.body.position, {
x: Math.cos(this.body.angle) * this.fireForce,
y: Math.sin(this.body.angle) * this.fireForce,
},
);
this.lastShot = Date.now();
},
};
const bullets = new Set();
const makeEnemy = () => Matter.Bodies.polygon(
(Math.random() * (map.width - 40)) + 20,
(Math.random() * (map.height - 40)) + 20,
5, 6, {
render: {
fillStyle: "transparent",
strokeStyle: "white",
lineWidth: 1,
},
},
);
const enemies = new Set([...Array(100)].map(makeEnemy));
const walls = new Set([
Matter.Bodies.rectangle(
0, map.height / 2, 20, map.height, {isStatic: true}
),
Matter.Bodies.rectangle(
map.width / 2, 0, map.width, 20, {isStatic: true}
),
Matter.Bodies.rectangle(
map.width, map.height / 2, 20, map.height, {isStatic: true}
),
Matter.Bodies.rectangle(
map.width / 2, map.height, map.width, 20, {isStatic: true}
),
]);
Matter.Composite.add(engine.world, [
player.body, ...walls, ...enemies
]);
const keyHandlers = {
ArrowLeft: () => player.rotateLeft(),
ArrowRight: () => player.rotateRight(),
Space: () => player.fire(),
};
const validKeys = new Set(Object.keys(keyHandlers));
const keysDown = new Set();
document.addEventListener("keydown", e => {
if (validKeys.has(e.code)) {
e.preventDefault();
keysDown.add(e.code);
}
});
document.addEventListener("keyup", e => {
if (validKeys.has(e.code)) {
e.preventDefault();
keysDown.delete(e.code);
}
});
Matter.Events.on(engine, "beforeUpdate", event => {
[...keysDown].forEach(k => {
keyHandlers[k]?.();
});
if (enemies.size < 100 && Math.random() > 0.95) {
const enemy = makeEnemy();
enemies.add(enemy);
Matter.Composite.add(engine.world, enemy);
}
});
Matter.Events.on(engine, "collisionStart", event => {
for (const {bodyA, bodyB} of event.pairs) {
const [a, b] = [bodyA, bodyB].sort((a, b) =>
bullets.has(a) ? -1 : 1
);
if (bullets.has(a) && walls.has(b)) {
Matter.Composite.remove(engine.world, a);
bullets.delete(a);
}
else if (bullets.has(a) && enemies.has(b)) {
Matter.Composite.remove(engine.world, a);
Matter.Composite.remove(engine.world, b);
bullets.delete(a);
enemies.delete(b);
document.querySelector("span").textContent = ++player.score;
}
}
});
Matter.Render.run(render);
Matter.Runner.run(Matter.Runner.create(), engine);
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
<div>press left/right arrow keys to rotate and space to shoot</div>
<div>score: <span>0</span></div>

Matter.js — How to prevent mouseConstraint to capture scroll events?

I am trying to enable scrolling into a page that contains a Matter.js canvas. I looked everywhere and the recommendation is to add removeEventListener so the mouse constraint won't hijack the scrolling event. Unfortunately, I am having no success. How can I enable a scroll?
Thank you in advance!
CodeSandbox
Code
import "./styles.css";
import Matter from "matter-js";
//Fetch our canvas
var canvas = document.getElementById("world");
//Setup Matter JS
var engine = Matter.Engine.create();
var world = engine.world;
var render = Matter.Render.create({
canvas: canvas,
engine: engine,
options: {
width: 500,
height: 500,
background: "transparent",
wireframes: false,
showAngleIndicator: false
}
});
//Add a ball
var ball = Matter.Bodies.circle(250, 250, 50, {
density: 0.04,
friction: 0.01,
frictionAir: 0.00001,
restitution: 0.8,
render: {
fillStyle: "#F35e66",
strokeStyle: "black",
lineWidth: 1
}
});
Matter.World.add(world, ball);
//Add a floor
var floor = Matter.Bodies.rectangle(250, 520, 500, 40, {
isStatic: true, //An immovable object
render: {
visible: false
}
});
Matter.World.add(world, floor);
//Make interactive
var mouseConstraint = Matter.MouseConstraint.create(engine, {
//Create Constraint
element: canvas,
constraint: {
render: {
visible: false
},
stiffness: 0.8
}
});
Matter.World.add(world, mouseConstraint);
// Why is this not working?
mouseConstraint.mouse.element.removeEventListener(
"mousewheel",
mouseConstraint.mouse.mousewheel
);
mouseConstraint.mouse.element.removeEventListener(
"DOMMouseScroll",
mouseConstraint.mouse.mousewheel
);
//Start the engine
Matter.Engine.run(engine);
Matter.Render.run(render);
It's a HTML issue. in index.html set the overflow to scroll

Matter.js — How to get the dimension of an image to set Bodies sizes?

I am trying to programmatically set the width and heights of the chained bodies in matter.js. Unfortunately, I am only getting 0 as values and I am unsure why. My guess is that the images are not being loaded fast enough to provide those values. How can I load those dimensions before the images are loaded?
Pseudo-code
Several bodies from Array
Get the width and height of each image in the Array
Use this value to set the Bodies dimensions
Code
var playA = Composites.stack(
percentX(25) - assetSize / 2,
percentY(25),
1,
6,
5,
5,
function (x, y) {
iA++;
var imgWidth;
var imgHeight;
var img = new Image();
img.src = String(design[iA]);
var imgWidth = 0;
var imgHeight = 0;
img.onload = function a() {
imgWidth = img.naturalWidth;
imgHeight = img.naturalHeight;
console.log(String(design[iA]), imgWidth, imgHeight);
};
console.log(String(design[iA]), imgHeight, imgWidth); // I can't access the values here.
return Bodies.rectangle(x, y, imgWidth, imgHeight, {
// collisionFilter: { group: group },
friction: 1,
render: {
sprite: {
texture: design[iA],
xScale: (assetSize / 100) * 0.46,
yScale: (assetSize / 100) * 0.46
}
}
});
}
);
Composites.chain(playA, 0.3, 0, -0.5, 0, {
stiffness: 1,
length: 10,
render: { type: "line", visible: false }
});
If you know the dimensions and can populate an array beforehand, the solution is potentially straightforward since Matter.js loads images given a URL string, with the caveat that the engine doesn't wait for the loads before running.
Here's a minimal example of iterating over width/height pairs in an array and passing these properties into the rectangle calls which I'll use as a stepping stone to the example that matches your use case.
const engine = Matter.Engine.create();
const render = Matter.Render.create({
element: document.body,
engine: engine,
options: {
width: 450,
height: 250,
wireframes: false, // required for images
}
});
Matter.Render.run(render);
const runner = Matter.Runner.create();
Matter.Runner.run(runner, engine);
const imgSizes = [[56, 48], [45, 50], [35, 50], [60, 63]];
const stack = Matter.Composites.stack(
// xx, yy, columns, rows, columnGap, rowGap, cb
150, 50, 4, 1, 0, 0,
(x, y, i) => {
const [w, h] = imgSizes[i];
return Matter.Bodies.rectangle(x, y, w, h, {
render: {
sprite: {
texture: `http://placekitten.com/${w}/${h}`
}
}
});
}
);
Matter.Composites.chain(stack, 0.5, 0, -0.5, 0, {
stiffness: 0.75,
length: 10,
render: {type: "line", visible: true}
});
Matter.Composite.add(engine.world, [
stack,
Matter.Bodies.rectangle(225, 0, 450, 25, {
isStatic: true
}),
Matter.Bodies.rectangle(450, 150, 25, 300, {
isStatic: true
}),
Matter.Bodies.rectangle(0, 150, 25, 300, {
isStatic: true
}),
Matter.Bodies.rectangle(225, 250, 450, 25, {
isStatic: true
})
]);
const mouse = Matter.Mouse.create(render.canvas);
const mouseConstraint = Matter.MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
stiffness: 0.2,
render: {visible: true}
}
});
Matter.Composite.add(engine.world, mouseConstraint);
render.mouse = mouse;
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
Now, if you need to load images using onload and use their dimensions, you'll need to use promises or put all code dependent on these images into the sequence of onload callback(s) as described in the canonical How do I return the response from an asynchronous call?.
The failing pattern is:
const getSomethingAsync = cb => setTimeout(() => cb("something"), 0);
let data = null;
getSomethingAsync(result => {
data = result;
console.log("this runs last");
});
console.log(data); // guaranteed to be null, not "something"
// more logic that is supposed to depend on data
The fix is:
const getSomethingAsync = cb => setTimeout(() => cb("something"), 0);
getSomethingAsync(data => {
console.log(data);
// logic that depends on the data from `getSomethingAsync`
});
console.log("this will run first");
// logic that doesn't depend on data from `getSomethingAsync`
Since you're juggling multiple onloads, you can promisify the onloads to make them easier to work with. I have a couple examples of doing this here and here agnostic of matter.js.
Here's an example of using promises to load images applied to your general problem. Again, I'll use my own code so that it's runnable and reproducible, but the pattern should be easy to extrapolate to your project.
The idea is to first load the images using a series of promises which are resolved when onload handlers fire, then use Promise.all to chain a then which runs the MJS initializer callback only when all images are loaded. The widths and heights are then accessible to your matter.js code within the callback.
As a side benefit, this ensures images are loaded by the time MJS runs.
const initializeMJS = images => {
const engine = Matter.Engine.create();
const render = Matter.Render.create({
element: document.body,
engine: engine,
options: {
width: 450,
height: 250,
wireframes: false, // required for images
}
});
Matter.Render.run(render);
const runner = Matter.Runner.create();
Matter.Runner.run(runner, engine);
const stack = Matter.Composites.stack(
// xx, yy, columns, rows, columnGap, rowGap, cb
150, 50, 4, 1, 0, 0,
(x, y, i) => {
const {width: w, height: h} = images[i];
return Matter.Bodies.rectangle(x, y, w, h, {
render: {
sprite: {
texture: images[i].src
}
}
});
}
);
Matter.Composites.chain(stack, 0.5, 0, -0.5, 0, {
stiffness: 0.75,
length: 10,
render: {type: "line", visible: true}
});
Matter.Composite.add(engine.world, [
stack,
Matter.Bodies.rectangle(225, 0, 450, 25, {
isStatic: true
}),
Matter.Bodies.rectangle(450, 150, 25, 300, {
isStatic: true
}),
Matter.Bodies.rectangle(0, 150, 25, 300, {
isStatic: true
}),
Matter.Bodies.rectangle(225, 250, 450, 25, {
isStatic: true
})
]);
const mouse = Matter.Mouse.create(render.canvas);
const mouseConstraint = Matter.MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
stiffness: 0.2,
render: {visible: true}
}
});
Matter.Composite.add(engine.world, mouseConstraint);
render.mouse = mouse;
};
const imageSizes = [[56, 48], [45, 50], [35, 50], [60, 63]];
const imageURLs = imageSizes.map(([w, h]) =>
`http://placekitten.com/${w}/${h}`
);
Promise.all(imageURLs.map(e =>
new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = e;
})
))
.then(initializeMJS)
;
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>

Running animation in Phaser 3 does not work

We have to create a game with Phaser 3 for a university project. We opted for a classic jump and round game.
I chose individual scenes to achieve the 15 levels. Now I have encountered the problem that the run animation does not work in the scene for some reason. However, I don't know exactly why so at least it doesn't make sense to me that it doesn't work. Maybe someone has an idea or knows what I did wrong.
// Level 1
var player;
var stars;
var bombs;
var platforms;
var cursors;
var score = 0;
var gameOver = false;
var scoreText;
class GameScene extends Phaser.Scene {
constructor (config) {
super(config);
//To use the scene in the game
Phaser.Scene.call(this, { key: 'gamescene'});
}
preload () {
this.load.image('sky', 'img/sky.png');
this.load.image('ground', 'img/platform.png');
this.load.image('star', 'img/star.png');
this.load.image('bomb', 'img/bomb.png');
this.load.spritesheet('dude', 'img/dude.png', { frameWidth: 32, frameHeight: 48 });
}
create () {
// A simple background for our game
this.add.image(400, 300, 'sky');
// The platforms group contains the ground and the 2 ledges we can jump on
platforms = this.physics.add.staticGroup();
// Here we create the ground.
// Scale it to fit the width of the game (the original sprite is 400x32 in size)
platforms.create(400, 568, 'ground').setScale(2).refreshBody();
// Now let's create some ledges
platforms.create(600, 400, 'ground');
platforms.create(50, 250, 'ground');
platforms.create(750, 220, 'ground');
// The player and its settings
player = this.physics.add.sprite(100, 450, 'dude');
// Player physics properties. Give the little guy a slight bounce.
player.setBounce(0.2);
player.setCollideWorldBounds(true);
// Our player animations, turning, walking left and walking right.
this.anims.create({
key: 'left',
frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'turn',
frames: [ { key: 'dude', frame: 4 } ],
frameRate: 20
});
this.anims.create({
key: 'right',
frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 8 }),
frameRate: 10,
repeat: -1
});
// Input Events
cursors = this.input.keyboard.createCursorKeys();
// Some stars to collect, 12 in total, evenly spaced 70 pixels apart along the x axis
stars = this.physics.add.group({
key: 'star',
repeat: 11,
setXY: { x: 12, y: 0, stepX: 70 }
});
stars.children.iterate(function (child) {
// Give each star a slightly different bounce
child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
});
bombs = this.physics.add.group();
// The score
scoreText = this.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' });
// Collide the player and the stars with the platforms
this.physics.add.collider(player, platforms);
this.physics.add.collider(stars, platforms);
this.physics.add.collider(bombs, platforms);
// Checks to see if the player overlaps with any of the stars, if he does call the collectStar function
this.physics.add.overlap(player, stars, collectStar, null, this);
this.physics.add.collider(player, bombs, hitBomb, null, this);
}
update() {
if (gameOver)
{
return;
}
if (cursors.left.isDown)
{
player.setVelocityX(-160);
player.anims.play('left', true);
}
else if (cursors.right.isDown)
{
player.setVelocityX(160);
player.anims.play('right', true);
}
else
{
player.setVelocityX(0);
player.anims.play('turn');
}
if (cursors.up.isDown && player.body.touching.down)
{
player.setVelocityY(-330);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

How to change the hitbox's shape of a image in Phaser 3?

I am creating a skateboarding game in JavaScript using the Phaser 3 framework. I have loaded the ramp image onto the screen, and I am currently using the "arcade" physics engine in my file. I know I have to use the matter physics engine to have a non-rectangular hitbox. How do I implement it with the triangular hitbox?
I have the .png image file of the ramp, along with the .json file for its hitbox.
I tried following a tutorial off of a website on how to create new hitboxes for the matter physics engine. Everything ended up falling off the screen and I couldn't figure out how to use the .json file for the ramp.
//Configurations for the physics engine
var physicsConfig = {
default: 'arcade',
arcade : {
debug : true //CHANGE THIS TO TRUE TO SEE LINES
}
}
//Game configurations
var config = {
type: Phaser.AUTO,
width: 1200 ,
height: 600,
physics: physicsConfig,
scene: {
preload: preload,
create: create,
update: update
}
}
//Start the game
var game = new Phaser.Game(config);
function preload() {
//Images
this.load.image('sky', 'archery_assets/images/sky.png');
this.load.image('ground', 'skate_assets/images/ground.png');
this.load.image('up_ramp', 'skate_assets/images/up_ramp.png')
//Spritesheets
this.load.spritesheet('player', 'skate_assets/spritesheets/skater.png', {frameWidth: 160, frameHeight: 160});
}
function create() {
//Background
skyImg = this.add.image(600, 300, 'sky');
//Scale the images
skyImg.setDisplaySize(1200, 600);
groundImg = this.add.image(600, 600, 'ground');
groundImg.setDisplaySize(1200, 250);
//Create the player
this.player = this.physics.add.sprite(100, 410, 'player');
this.player.setCollideWorldBounds(true);
//Rolling animation
this.anims.create({
key: 'move',
frames: this.anims.generateFrameNumbers('player', {start: 0, end: 3}),
frameRate: 16,
repeat: -1 // <-- keeps the rolling animation going
});
//Pushing animation
this.anims.create({
key: 'push',
frames: this.anims.generateFrameNumbers('player', {start: 4, end: 8}),
frameRate: 16
});
//Start and keep the rolling animation going
this.player.anims.play('move', true);
//Up ramp (1st ramp)
this.upRamp = this.physics.add.sprite(700, 330, 'up_ramp');
this.upRamp.setSize(320, 150).setOffset(0, 175);
this.upRamp.enableBody = true;
this.upRamp.setImmovable();
this.upRamp.body.angle = 150;
//Input
this.cursors = this.input.keyboard.createCursorKeys();
//Spacebar
this.spacebar = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
this.physics.add.collider(this.player, this.upRamp);
}
function update() {
//Set variable for push speed
var playerPushSpeed = 0;
//If the spacebar is pressed
if (this.spacebar.isDown) {
//Play the push animation
this.player.anims.play('push', true);
//Push speed
playerPushSpeed += 175;
//Move player
this.player.setVelocityX(playerPushSpeed);
}
if (this.upRamp.body.touching.left) {
this.player.setVelocityY(-200);
}
}
I need to know how to implement the .png image of the ramp along with its .json hitbox file, so that the player can properly "ride" up the ramp.
You'll have to use the physics: { default: 'matter' } in order to change the hitbox's shape. Use this code snippet for reference:
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
physics: {
default: 'matter',
matter: {
gravity: {
y: 0
},
debug: true
}
},
scene: {
create: create
}
};
var game = new Phaser.Game(config);
function create ()
{
this.matter.world.setBounds();
this.matter.add.rectangle(200, 200, 200, 200, {
chamfer: { radius: [230, 0, 200, 0 ] }
});
this.matter.add.mouseSpring();
}
You could also use a PhysicsEditor, you can check this tutorial on how to implement different shapes.
EDIT:
You can use console.log(this.matter.bodies) to check other available shapes to implement.

Categories

Resources