Related
Drawn graphics in a PhaserJS WebGL setup show these pixelated "stairs like" outlines as shown in the picture. How to get them smooth?
Example:
https://phaser.io/examples/v3/view/game-objects/graphics/primitives/line-gradient-stroked
var config = {
width: 800,
height: 600,
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: {
create: create
}
};
var game = new Phaser.Game(config);
function create ()
{
var graphics = this.add.graphics();
graphics.lineGradientStyle(128, 0xff0000, 0xff0000, 0x0000ff, 0x0000ff, 1);
graphics.lineBetween(100, 100, 600, 500);
}
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>
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.
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
I'm new with jointjs and I try to constraint a rectangle with ports to a line.
I tried to reproduce tutorial, that works with a basic.Circle, with a basic.Rect but not with devs.Model
Could someone explian me why and how to solve this problem?
Many thanks in advance!
Here is my code :
var width=400, height=1000;
var ConstraintElementView = joint.dia.ElementView.extend({
pointermove: function(evt, x, y) {
joint.dia.ElementView.prototype.pointermove.apply(this, [evt, 100, y]);
}
});
var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({ el: $('#myholder'), width: width, height: height, gridSize: 1, model: graph, elementView: ConstraintElementView});
var m1 = new joint.shapes.devs.Model({
position: { x: 20, y: 20 },
size: { width: 90, height: 90 },
inPorts: [''],
outPorts: [''],
attrs: {
'.label': { text: 'Model', 'ref-x': .4, 'ref-y': .2 },
rect: { fill: '#2ECC71' },
'.inPorts circle': { fill: '#16A085' },
'.outPorts circle': { fill: '#E74C3C' }
}
});
var m2=m1.clone();
m2.translate(0,300);
var earth = new joint.shapes.basic.Circle({
position: { x: 100, y: 20 },
size: { width: 20, height: 20 },
attrs: { text: { text: 'earth' }, circle: { fill: '#2ECC71' } },
name: 'earth'
});
graph.addCell([m1, m2, earth]);
Why does it not work?
devs.Model is not rendered via ContraintElementView to the paper.
devs.Model uses devs.ModelView for rendering, basic.Circle and basic.Rect use ContraintElementView.
JointJS dia.Paper searches for a view defined in the same namespace as the model first. If found, it uses it. It uses one from the paper elementView option otherwise. i.e. joint.shapes.devs.ModelView found for devs.Model but no view found for basic.Circle (no joint.shapes.basic.RectView is defined)
How to make it work?
define elementView paper option as a function. In that case paper don't search the namespace and uses the result of the function first.
Note that in order to render ports devs.ModelView is still required.
i.e.
var paper = new joint.dia.Paper({
elementView: function(model) {
if (model instanceof joint.shapes.devs.Model) {
// extend the ModelView with the constraining method.
return joint.shapes.devs.ModelView.extend({
pointermove: function(evt, x, y) {
joint.dia.ElementView.prototype.pointermove.apply(this, [evt, 100, y]);
}
});
}
return ConstraintElementView;
}
});
http://jsfiddle.net/kumilingus/0bjqg4ow/
What is the recommended way to do that?
JointJS v0.9.7+
not to use custom views that restrict elements movement
use restrictTranslate paper option instead.
i.e.
var paper = new joint.dia.Paper({
restrictTranslate: function(elementView) {
// returns an area the elementView can move around.
return { x: 100, y: 0, width: 0, height: 1000 }
};
});
http://jsfiddle.net/kumilingus/atbcujxr/
I think this could help you :
http://jointjs.com/demos/devs