How to animate a line scaling in Phaser 3 with a tween? - javascript

Imagine a very simple scene:
canvas of 800x640
red circle in the center
two blue lines in a 9:00 position
private create(): void {
const circ = this.add.circle(
400, 320, 200, 0xff0000
);
const l1 = this.add.line(
0, 0, 400, 320, 400, 100, 0x0000ff
).setOrigin(0);
const l2 = this.add.line(
0, 0, 400, 320, 200, 320, 0x0000ff
).setOrigin(0);
}
So far so good.
Now I want to scale this simple figure, same config 1.5x its current size:
private create(): void {
const circ = this.add.circle(
400, 320, 200, 0xff0000
);
const l1 = this.add.line(
0, 0, 400, 320, 400, 100, 0x0000ff
).setOrigin(0);
const l2 = this.add.line(
0, 0, 400, 320, 200, 320, 0x0000ff
).setOrigin(0);
this.tweens.add({
targets: [circ, l1, l2],
scale: 1.5,
yoyo: false,
duration: 2000,
ease: 'Sine.easeInOut'
});
}
Expected behavior:
the circle expands from the center
the lines expand as well, ideally where they meet
Actual behavior:
As things stand, only #1 fit my expectations. The lines, however, translate as opposed to merely scaling. And the translation seems affected by the scale parameter passed to tweens.add. What gives? What am I missing here?
Given the various configurations for "origin" wrt lines in Phaser 3, the worst I was expecting was that the lines would emanate/grow differently than the circle (which emanates from the center/origin). But I definitely expect the lines to stay still/keep their intersection at the circle's center.
Can you explain what exactly Phaser is doing here and what might I do to get my desired effect?

There are a bunch of things that might be unintuitive with the Line GameObject in Phaser 3, all contributing to this behavior. Let's break them down.
Lines and Origins
First of all, make the distinction between line as a geometric entity and line as a Phaser 3 Game Object. They are related but the confusion stems from the subtleties.
For the Phaser 3 line Game Object, there are three ordered pairs p (x, y) you need to keep in mind:
P (X, Y), the origin of the line, relative to the game world (and we are going to define the term in a while); in the game world, (0, 0) is at the upper-left corner.
P1 (X1, Y1), the first point that defines a line, relative to P
P2 (X2, Y2), the second point that defines a line, relative to P
Another important thing is that the game world is basically a Cartesian coordinate system flipped around the x-axis. So negative x values still go to the left but negative y values go up and positive y values go down.
Now let's define the origin, which I will just paraphrase from the official docs:
An object's origin is a normalized value in the range [0, 1].
For the x-axis, 0 means the left of the Game Object and 1 means the right.
For the y-axis, 0 means the top of the Game Object and 1 means the bottom.
By default, the origin is set at 0.5, the center of the object.
Interpreting the example and the tween
Let's take the horizontal line l2 in the example and figure out how it's being drawn.
const l2 = this.add.line(
0, 0, 400, 320, 200, 320, 0x0000ff
).setOrigin(0);
The origin is to the left of the game object.
With respect to the game world, this origin lies at 0, 0 or the upper-left corner.
The line is defined by two points relative to the origin, (400, 320) and (200, 320).
The important observation here is that the origin is completely outside the geometric line defined. In effect, there is a considerable invisible space in the rectangular area between your origin (at (0, 0)), left of the game object and the right, lowermost corner (at (400, 320)).
Now, when the tween is invoked, it actually scales the game object rather than just the geometric line. And since most of the game object is "invisible" (the geometric line being the only part visible), the effect is as if the geometric line is translating!
Solution
To get the desired effect, constrain the game object to exactly the area taken up by the geometric line. One such way to do this would be:
since we want the lines to "radiate" in the same way as the circle, set P at (400, 320), the center of the circle. Additionally, set P1 to (0, 0)---remember this is relative to P, so in the game world, we just made P = P1.
let's leave the lines' origins at 0 but now we have to redefine their P2 values to (0, -100) and (0, -200) respectively.
Putting it all together,
private create(): void {
const circ = this.add.circle(
400, 320, 200, 0xff0000
);
const l1 = this.add.line(
400, 320, 0, 0, 0, -100, 0x0000ff
).setOrigin(0);
const l2 = this.add.line(
400, 320, 0, 0, -200, 0, 0x0000ff
).setOrigin(0);
this.tweens.add({
targets: [circ, l1, l2],
scale: 1.5,
yoyo: false,
duration: 2000,
ease: 'Sine.easeInOut'
});
}

Related

Phaser 3 - Check group collision with world bounds

In my scenario, i create a ship according to player's preferences. The ship consists of the flag and the hull.
An example view
PROBLEM-1
Ship is an Arcade.Group and i want to prevent this group from going outside the borders of the world.
create(){
// Create a group for ship
this.shipGroup = this.physics.add.group()
// Add hull to shipGroup
this.shipGroup.create(400, 500, "1021")
// Add flag to shipGroup
const mainFlag = this.shipGroup.create(400, 500, "FA1")
mainFlag.setOrigin(0.5, 0.8)
// Set collision property to true of every object in shipGroup
this.shipGroup.children.each((item: any) =>
item.setCollideWorldBounds(true)
)
}
update(t: number, dt: number){
if (this.cursor.up.isDown) {
this.shipGroup.setVelocity(...)
}
else {
this.shipGroup.setVelocity(0, 0)
}
}
With this approach every object in group calculated seperately. After hitting the world boundary, the position of the objects is distorted.
PROBLEM-2
To avoid this i tried another approach. I add bounding box to group. Instead of check collision for every object, i will only check collision for bounding box.
create(){
// Create a group for ship
this.shipGroup = this.physics.add.group()
// Add bounding box for shipGroup
this.shipBox = this.shipGroup.create(400, 500, "bbox")
this.shipBox.setCollideWorldBounds(true)
this.shipBox.body.onWorldBounds = true
// Add hull to shipGroup
this.shipGroup.create(400, 500, "1021")
// Add flag to shipGroup
const mainFlag = this.shipGroup.create(400, 500, "FA1")
mainFlag.setOrigin(0.5, 0.8)
/*this.shipGroup.children.each((item: any) =>
item.setCollideWorldBounds(true)
)*/
}
update(t: number, dt: number){
if (this.cursor.up.isDown && !this.shipBox.body.checkWorldBounds()) {
this.shipGroup.setVelocity(...)
}
else {
this.shipGroup.setVelocity(0, 0)
}
}
The problem is checkWorldBounds() returns false even if shipBox hits world boundaries. But collision for shipBox is work.
checkWorldBounds()
Description: Checks for collisions between this Body and the world
boundary and separates them.
Returns: True if this Body is colliding with the world boundary.
How can i implement collision for group and world boundary?
P.S. : phaser version is 3.55.2
There are a few ways to solve/work around this issue, I personally would just use only one image why one physics-body (hull and flag combined) and just move that single image/texture, and switch the image, when needed.
That said, if you need to use the separate images, the easy way is to use a phaser container. (link to the documentation)
create a container
add the images to the container
set the size for the container (default size is width=0 height=0)
create a physics body for the container
done
A short demo:
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 183,
physics: {
default: 'arcade',
arcade: {
gravity:{ y: 0 },
debug: true
}
},
scene: {
create
},
banner: false
};
function create () {
this.add.text(10,10, 'Ship with physics')
.setScale(1.5)
.setOrigin(0)
.setStyle({fontStyle: 'bold', fontFamily: 'Arial'});
let graphics = this.make.graphics();
graphics.fillStyle(0xffffff);
graphics.fillRect(0, 0, 10, 40);
graphics.generateTexture('ship', 10, 40);
graphics.fillStyle(0xff0000);
graphics.fillRect(0, 0, 30, 10);
graphics.generateTexture('flag', 30, 10);
graphics.generateTexture('flag2', 20, 6);
let hull = this.add.image(0, 0, 'ship')
let flag = this.add.image(0, -5, 'flag')
let flag2 = this.add.image(0, 10, 'flag2')
this.ship = this.add.container(100, 80, [ hull, flag, flag2]);
this.ship.setAngle(-90)
this.ship.setSize(40, 30)
this.physics.world.enable(this.ship);
this.ship.body.setVelocity(100, 0).setBounce(1, 1).setCollideWorldBounds(true);
}
new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
Info: this demo is based partly from this official example

How do I get the center of a circle in paper.js?

I am trying out paper.js and I've made a few circles:
var circle = new Shape.Circle({
center: [100,100],
radius: 50,
strokeColor: 'black',
strokeWidth: 2
});
This draws a circle with center [100,100] (which could also be a point: Point({x:100, y:100});) and radius 50. This is great, this is a circle.
If I want to get the radius of a circle, I can do this:
circle.radius; // returns 50
But I don't know how to get the center back again. Part of the reason I guess is that Shape.Circle returns a Shape object, which does not have a center parameter (documentation here), but surely I can get this point back somehow. Does anyone know how?
Since circles are centred on the position they're created at, you can get the position (as a Point) and the x and y values from that:
var circle = new Shape.Circle({
center: [100,100],
radius: 50,
strokeColor: 'black',
strokeWidth: 2
});
console.log(circle.position.x, circle.position.y); //100 100
From http://paperjs.org/reference/shape/#position
While the circle object does not have a center property, if you read the docs properly you will find that it does have a bounds property.
In graphics, a "bound" is the rectangle that fully contains your object. So for a circle, the bounds will be a rectangle that touches the circle on the left, right, top and bottom. Therefore the center of the bound is the center of the circle (note: this is not always true for all objects depending on your definition of "center").
Paper.js will give you:
circle.bounds.x
circle.bounds.width
circle.bounds.y
circle.bounds.height
Therefore the center of the circle is:
var centerX = circle.bounds.x + circle.bounds.width/2;
var centerY = circle.bounds.y + circle.bounds.height/2;
Note: You will have to try this out yourself as I have zero experience with paper.js. I just read the docs
Use :
circle.center[0] and circle.center[1]
cause its an array
<script>
var circle = {
center: [100,100],
radius: 50,
strokeColor: 'black',
strokeWidth: 2
};
console.log(circle.center[0]);
</script>
Update:
Sorry, I haven't read your question properly.
You can use circle.position to get the position of center.

Hairline/Empty Space when Drawing a Triangulated Rectangle on Canvas

I was exploring some drawing on the HTML canvas, and quickly landed at dissecting my shapes into triangles, as that seem to be the most common and sure way to ensure that one is handling only simple polygons and not self-intersection ones (which get drawn in a somewhat uncontrollable fashion to me).
But when I wanted to draw a simple rectangle as to triangles, I got the following result:
The approach I am taken is the following:
const rect = [{
x: 200,
y: 225
}, {
x: 490,
y: 225
}, {
x: 490,
y: 375
}, {
x: 200,
y: 375
}];
const triangle1 = [rect[0], rect[1], rect[2]];
const triangle2 = [rect[0], rect[2], rect[3]];
fillPolygon(ctx, triangle1, 'blue');
fillPolygon(ctx, triangle2, 'blue');
See the full example here:
https://jsfiddle.net/JLVva/85/
Since triangles seem to be the basis of graphics drawing, I think I am missing a common workaround applied in these scenarios. How is this normally handled?
If you also add a stroke to the shape as well as a fill it removes that gap. I assume the stroke defaults to transparent or something.
Add this to the end of your fillPolygon function.
ctx.strokeStyle = color;
ctx.stroke();

how to flip a cube in air using three.js

I am using three.js framework and using physi.js for forces and gravity. My problem is that i has given some position to a dice on a plane and want to lob it or flip it from its position with some angle by clicking on it such that it rotate in air and fall on plane due to gravity force of plane.
i have idea about onclickevent method but i don't know how to set the dice to jump with some height in 90 degree and rotate it with some angle only one time for one click.
if you have some suggestion please give me method to set up the dice
thank you
i have no experience with physi.js, for physics i use cannon.js, so i do have some experience with physics. i can tell you how it's done, but i cant provide an example just yet. the first thing you need to do is cast a ray from the camera to the dice. This tells you if the object is clicked on. from there you apply a force to where the object is clicked. i hope this helps.
EDIT/Follow up:
I've been looking over physi.js, and it seems pretty similar to cannon.js. Also, there are many aspects to what you're asking, but i'll try to be as comprehensive as possible.
The first thing to do is set the gravity. In the examples it is done by:
var scene = new Physijs.Scene({ fixedTimeStep: 1 / 120 });
scene.setGravity(new THREE.Vector3( 0, -30, 0 ));
the next thing to do is define the plane the dice is sitting on as a rigid body, and the dice as a rigid body.
var dice = new Physijs.BoxMesh(
new THREE.CubeGeometry(5, 5, 5), //the collision geometry
new THREE.MeshLambertMaterial({ map: THREE.ImageUtils.loadTexture('images/dicetexture.jpg' )});, // material of the dice
5, // mass, for the plane use 0, meaning it doesnt move.
{ restitution: .2, friction: .8 } //contact material
);
and finally, you need to apply a force, i cant find an example of it, but going over the source real quick, i found:
// Physijs.Mesh.applyForce
Physijs.Mesh.prototype.applyForce = function ( force, offset ) {
if ( this.world ) {
this.world.execute( 'applyForce', { id: this._physijs.id, force_x: force.x, force_y : force.y, force_z : force.z, x: offset.x, y: offset.y, z: offset.z } );
}
};
both force and offset should be vectors. force being the direction and magnitude, offset being the point of origin.
i'll experiment a bit more with physi.js it seems to have solved some problems i've been pondering with. if this still doesnt solve the problem. i'll post an example.
as for letting it spin for a set number of times in a preset height, i think that's a lot of experimenting with the values...
EDIT 2:
In the fiddle you posted the problem you explained was an undefined variable, effect.
if (intersects.length > 0) {
intersects[ 0 ].applyImpulse(effect, offset);
}
change this to:
if (intersects.length > 0) {
var effect = new THREE.Vector3( 0, 100, 0 );
var offset = new THREE.Vector3( 0, 0, 0 );
intersects[ 0 ].object.applyImpulse(effect, offset);
}
the values ARE defined, but you define them in a different function.
function apply_force(){
var effect = new THREE.Vector3( 0, 100, 0 );
var offset = new THREE.Vector3( 0, 0, 0 );
object.applyImpulse( effect, offset );
}
this function will not be used anymore, so you might as well remove it.
CLICK TIMER:
if(canClick == true){
//process click
canClick = false;
setTimeout(function(){canClick = true}, 5000)//5 seconds before the user can click again.
}

getBounds() returns the same as getTransformedBounds() on a Shape() which has been scaled

I'm having an issue with obj.getTransformedBounds() of a Shape().
The getBounds() method should return a rectangle (if it was set using setBounds()) which stores the x/y/w/h of the Shape() within its OWN coordinate space - it's real width & height with no transformations applied.
The getTransformedBounds() method should return a rectangle (again, if set by using setBounds()) which stores the x/y/w/y within its PARENT'S coordinate space - the shape's width & height with transformations applied.
Instead, I see getBounds() returning only the transformed bounds of a shape that has its scaleX and/or scaleY applied:
http://jsfiddle.net/W9Tb8/1/
var shape = new createjs.Shape();
shape.graphics = new createjs.Graphics().beginFill("#f00").drawRect(0,0,100,100);
shape.setBounds(0,0,100,100);
shape.scaleX = shape.scaleY = 0.5;
console.log(shape.getBounds(), shape.getTransformedBounds()); // returns [0, 0, 50, 50], [0, 0, 50, 50]
// shape.getBounds() should return [0, 0, 100, 100]
// shape.getTransformedBounds() should return [0, 0, 50, 50}
Am I missing something?
It looks like what's happen is EaselJS is reusing some rectangle instances to help reduce memory impact. Have a look at the documentation here for more about it:
http://createjs.com/Docs/EaselJS/classes/Shape.html#method_getBounds
If you want to force it to give you the originally set bounds you'll either need to clone or copy them. I've altered your example to show how to output the original bounds using cloning.
http://jsfiddle.net/W9Tb8/2/
console.log(shape.getBounds().clone(), shape.getTransformedBounds());

Categories