I am trying to implement a game where there are moving objects(Bitmaps) and I need to detect collision. I used the following code to make objects move(transform) and check hitTest with mouse hover.
However, the alpha is not changed with the correct mouse position, instead, it detects the left upper corner of the canvas.
rock = new createjs.Bitmap(loader.getResult("rock"));
rock.setTransform(800, 270, 0.5, 0.5);
stage.addChild(rock);
// .....
createjs.Ticker.timingMode = createjs.Ticker.RAF;
createjs.Ticker.addEventListener("tick", tick);
// .....
function tick(event) {
rock.alpha = 0.7;
if (rock.hitTest(stage.mouseX, stage.mouseY)) {//if hit, change alpha
rock.alpha = 1;
}
var deltaS = event.delta / 1000;
rock.x = (rock.x - deltaS * groundSpeed);//to gradually shift the rock
if (rock.x + rock.image.width * rock.scaleX <= 0) {//to re-position the rock
rock.x = w + Math.random() * 1000;
}
stage.update(event);
}
I found the answer when reading the answer to this question.
Rather than directly getting stage.mouseX or stage.mouseY, the correct position can be obtained using globalToLocal as on this.
var p = rock.globalToLocal(stage.mouseX, stage.mouseY);
if (rock.hitTest(p.x, p.y)) {
rock.alpha = 1;
}
Related
I have started working with PixiJs to develop a simple game. I am trying to rotate a sprite based on a click of the button, and then allowing the user top stop the rotation with another button click.
What I am not able to achieve is determine how many "cycles" the rotation would have done, for example if the image did a full rotation 3, 4 times, and its stopping location determining how many remaining rotations are needed for another full cycle. Is there something in place to easily retrieve this?
The code I have so far is quite basic and simple:
initGameLayout() {
const top = new PIXI.Graphics();
top.beginFill(0x2185c7);
top.drawRect(0, 0, this.app.screen.width, this.margin);
const headerStyle = new PIXI.TextStyle({
fontSize: 24,
fontStyle: 'italic',
fontWeight: 'bold',
});
const headerText = new PIXI.Text('', headerStyle);
headerText.x = Math.round((top.width - headerText.width) / 2);
headerText.y = Math.round((this.margin - headerText.height) / 2);
top.addChild(headerText);
const spinButton = new PIXI.Graphics();
spinButton.beginFill(0x2185c7);
spinButton.drawRect(0, 0, this.app.screen.width, this.margin);
spinButton.width = 150;
spinButton.height = 100;
spinButton.x = 620
spinButton.y = 500
spinButton.buttonMode = true;
spinButton.interactive = true;
spinButton.on('pointerdown', this.spinWheel);
const spinButton2 = new PIXI.Graphics();
spinButton2.beginFill(0x2185c3);
spinButton2.drawRect(0, 0, this.app.screen.width, this.margin);
spinButton2.width = 150;
spinButton2.height = 100;
spinButton2.x = 420
spinButton2.y = 500
spinButton2.buttonMode = true;
spinButton2.interactive = true;
spinButton2.on('pointerdown', this.stopWheel);
this.bunny = new PIXI.Sprite.from('https://pixijs.io/examples-v4/examples/assets/bunny.png');
this.bunny.width = 50;
this.bunny.height = 50;
this.bunny.anchor.set(0.5);
this.bunny.x = this.app.screen.width / 2;
this.bunny.y = this.app.screen.height / 2;
this.bunny.rotate += 0.1;
this.app.stage.addChild(top);
this.app.stage.addChild(spinButton);
this.app.stage.addChild(spinButton2);
this.app.stage.addChild(this.bunny);
}
spinWheel() {
if (!this.running)
{
this.running = true;
this.app.ticker.add((delta: any) => {
this.bunny.rotation += 0.1;
});
} else {
this.running = false;
this.bunny.rotation -= -0.1;
}
}
stopWheel() {
this.bunny.rotation -= -0.1;
this.running = false;
}
Appreciate any help anyone could give on the above issue
-Jes
The rotation member of a sprite is the measure of radians it is rotated. There are 2*Math.PI radians in a full circle. You can use this information to calculate the desired values:
When the sprite is first clicked, store originalRotation = bunny.rotation;
When the sprite is clicked again, calculate angleRotated = Math.abs(bunny.rotation - originalRotation);
Then numCycles = Math.floor(angleRotated / (2*Math.PI));
And radiansUntilNextCycle = 2*Math.PI - (angleRotated % (2*Math.PI));
If you are more familiar with degrees, you can use those instead. Swap:
bunny.rotation with bunny.angle
2*Math.PI with 360
I'm assuming by "cycle" you mean a single rotation of 360 degrees. However, your question is difficult to understand because each time you use the word "rotation" it seems to have a different meaning. So it doesn't quite make sense.
It may also help to explain why you want these values; what will you do with them?
And pixiplayground.com is a great place to share live, functional code.
I've been working on a specific animation in which I need to convert(with animation) a Rounded Rectangle Shape to Circle. I've checked the documentation of paper.js and haven't found any predefined function to achieve this.
-->
The animation needs to be smooth. As the number of rectangles I'm working with is very high, I can't use the "remove current rounded rect and redraw one more rounded version" method. It reduces the performace and the animation gets laggy.
This is the code I'm using to generate rounded rectangle.
// Had to paste something to post the question
// Though the whole code can be seen on codepen link
var rect = new Rectangle();
var radius = 100, origin = {x: 100, y: 100};
rect.size = new Size(radius, radius);
rect.center = new Point(origin.x, origin.y);
var cornerSize = radius / 4;
var shape = new Path.Rectangle(rect, cornerSize);
Prepared this Codepen example to show the progress.
If we can work out the whole animation using any other object types, that will be fine too. For now I can't find any any property which can transform the rounded rectangle to circle.
I'm also animating color of the object and position. I've gone through many documents to find out color animation.
PS: If there is any other(better) technique to animate colors of object, please share that too.
You will first have to create a path as a rounded rectangle. Then with each step in your animation you have to modify the eight segments of the path. This will only work with Path objects, not if your rectangle is a Shape.
The segment points and the handles have to be set like this:
κ (kappa) is defined in paper.js as Numerical.KAPPA (more on Kappa here).
The code to change the radius could look like this (Click here for the Sketch):
var rect = new Path.Rectangle(new Point(100, 100), new Size(100, 100), 30);
rect.fullySelected = true;
var step = 1;
var percentage = 0;
function onFrame(event) {
percentage += step;
setCornerRadius(rect, percentage)
if (percentage > 50 || percentage < 0) {
step *= -1;
}
}
function setCornerRadius(rectPath, roundingPercent) {
roundingPercent = Math.min(50, Math.max(0, roundingPercent));
var rectBounds = rectPath.bounds;
var radius = roundingPercent/100 * Math.min(rectBounds.width, rectBounds.height);
var handleLength = radius * Numerical.KAPPA;
l = rectBounds.getLeft(),
t = rectBounds.getTop(),
r = rectBounds.getRight(),
b = rectBounds.getBottom();
var segs = rectPath.segments;
segs[0].point.x = segs[3].point.x = l + radius;
segs[0].handleOut.x = segs[3].handleIn.x = -handleLength;
segs[4].point.x = segs[7].point.x = r - radius;
segs[4].handleOut.x = segs[7].handleIn.x = handleLength;
segs[1].point.y = segs[6].point.y = b - radius;
segs[1].handleIn.y = segs[6].handleOut.y = handleLength;
segs[2].point.y = segs[5].point.y = t + radius;
segs[2].handleOut.y = segs[5].handleIn.y = -handleLength;
}
Edit: I just found a much easier way using a shape. Not sure which approach performs faster.
Here is the implementation using a Shape (Click here for the Sketch).
var size = 100;
var rect = new Shape.Rectangle(new Rectangle(new Point(100, 100), new Size(size, size)), 30);
rect.strokeColor = "red";
var step = 1;
var percentage = 0;
function onFrame(event) {
percentage = Math.min(50, Math.max(0, percentage + step));
rect.radius = size * percentage / 100;
if (percentage >= 50 || percentage <= 0) {
step *= -1;
}
}
Change the corner size to the following
var cornerSize = circle.radius / 1;
I'm trying to make a game with top-down perspective where the player sprite is fixed in the center of screen and the background is moving (scrolling) in the direction opposite to the player's direction, so it results in effect of the player's movement.
I started with these examples: Invaders (for background scrolling) and Asteroids Movement (for sprite movement). The former uses TileSprite and its tilePosition property to make vertical scrolling of the background. This works good for linear scrolling in fixed direction (vertical or horizontal). But in my case I need implement scrolling (i.e. movement) in any direction. Furthermore I need such physics features as acceleration and drag, like in the Asteroids example. So I would like to apply Arcade Physics to TileSprite. But it seems that Arcade Physics doesn't work with TileSprite. Or, to be more precise, it doesn't work as I expected.
I've tried to enable Arcade Physics for TileSprite as it used with Sprites. Here is the code:
function preload() {
game.load.baseURL = 'http://examples.phaser.io/assets/';
game.load.crossOrigin = 'anonymous';
game.load.image('ship', 'games/invaders/player.png');
game.load.image('starfield', 'games/invaders/starfield.png');
}
var player;
var cursors;
var starfield;
var playerAngle = 0; // angle of the player's movement, in degrees
var playerSpeed = 5; // the player's speed, px per frame
function create() {
game.physics.startSystem(Phaser.Physics.ARCADE);
// The scrolling starfield background
starfield = game.add.tileSprite(0, 0, 800, 600, 'starfield');
game.physics.enable(starfield, Phaser.Physics.ARCADE);
starfield.body.velocity.set(200);
// The player sprite
player = game.add.sprite(400, 300, 'ship');
player.anchor.setTo(0.5, 0.5);
// Gameplay controls
cursors = game.input.keyboard.createCursorKeys();
}
function update() {
// Scroll the background
var delta = 0;
if (cursors.left.isDown)
delta = -1;
else if (cursors.right.isDown)
delta = 1;
if (delta)
player.angle = playerAngle = (playerAngle + delta + 360) % 360;
var a = (playerAngle + 90) / 360 * 2 * Math.PI; // angle of the background movement/scrolling, in radians
// move the background by playerSpeed along the a angle
game.physics.arcade.velocityFromRotation(a, 200, starfield.body.velocity);
}
Try at Phaser sandbox
Unfortunately this code doesn't work as I expected.
As far as I understood and as written in the docs, Arcade Physics is only intended for work with Sprite, not TileSprite nor any other game object. If so, this means that I have to manually reproduce all the Physics stuff such as acceleration and drag myself with respect to TileSprite. I've written simple TileSprite movement with constant velocity (no acceleration, drag and other stuff). Here is the code:
function preload() {
game.load.baseURL = 'http://examples.phaser.io/assets/';
game.load.crossOrigin = 'anonymous';
game.load.image('ship', 'games/invaders/player.png');
game.load.image('starfield', 'games/invaders/starfield.png');
}
var player;
var cursors;
var starfield;
var playerAngle = 0; // angle of the player's movement, in degrees
var playerSpeed = 5; // the player's speed, px per frame
function create() {
game.physics.startSystem(Phaser.Physics.ARCADE);
// The scrolling starfield background
starfield = game.add.tileSprite(0, 0, 800, 600, 'starfield');
// The player sprite
player = game.add.sprite(400, 300, 'ship');
player.anchor.setTo(0.5, 0.5);
// Gameplay controls
cursors = game.input.keyboard.createCursorKeys();
}
function update() {
// Scroll the background
var delta = 0;
if (cursors.left.isDown)
delta = -1;
else if (cursors.right.isDown)
delta = 1;
if (delta)
player.angle = playerAngle = (playerAngle + delta + 360) % 360;
var a = (playerAngle + 90) / 360 * 2 * Math.PI; // angle of the background movement/scrolling, in radians
// move the background by playerSpeed along the a angle
starfield.tilePosition.x += playerSpeed * Math.cos(a);
starfield.tilePosition.y += playerSpeed * Math.sin(a);
}
Try at Phaser sandbox
Sure it works, however even such simple thing as linear motion require some math knowledge. Other things will require much more knowledge of math and physics. I would not like to implement such things manually.
So is there any way to use Phaser Physics in conjunction with tilePosition?
My suggestion is to rethink the whole thing. Main idea is to not make the player sprite fixed, but to make it "dynamic" with physics enabled. To have your desired effect, just make the camera follow the player sprite (with no deadzone). That way, the player remains in the center, and manage the tilesprite classically.
Working on a project and cannot seem to get my animation right. I will not be showing the code because it simply doesn't work but it would be cool if someone were to give me a few pointers on how to animate a cloud of smoke moving upwards while slowly fading and increasing in size.
This effect should technically repeat once the y value reaches 0 i.o.w. the cloud reaches the top of the canvas.
What I need to know is how do I animate this, and which methods do I use. This is a kind of a self learning assignment.
Thanks in advance.
Here is a Plunker example of sprites growing in size and fading in transparency.
It is done using Pixi.js which actually renders in webgl with a canvas fallback. It should be possible to take the algorithm and apply it to raw canvas (although it would take some work).
var insertAfter = function(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
var range = function(aCount) {
return new Array(aCount)
}
function main() {
var el_main = document.getElementById("animation_main");
var el_div = document.createElement('div');
el_div.setAttribute('id', 'main_stage');
insertAfter(el_div, el_main);
renderer = PIXI.autoDetectRenderer(300, 300, {
transparent: true,
antialias: true
});
el_div.appendChild(renderer.view);
window.stage = new PIXI.Container();
window.stage.x = 0;
window.stage.y = 0;
renderer.render(window.stage);
var s = [];
for (x of range(400)) {
tCircle = new PIXI.Graphics();
tCircle.beginFill(0x000000, 1);
tCircle.s = (Math.random() * 2) + 1;
tCircle.drawCircle(0, 0, 5 - tCircle.s);
tCircle.x = Math.random() * 300
tCircle.y = (Math.random() * 50) + 20
tCircle.endFill();
s.push(tCircle);
window.stage.addChild(tCircle)
}
window.t = 0
animate = function(t) {
d = t - window.t
window.t = t
//Animation Start
for (n in s){
s[n].x += ((s[n].s / 25) * d)
s[n].alpha = 1 - s[n].x / 300
s[n].scale.x = 1 - s[n].alpha
s[n].scale.y = 1 - s[n].alpha
if (s[n].x > 300) {
s[n].x = 0
s[n].y = (Math.random() * 50) + 20
}
}
renderer.render(window.stage)
//Animation End
requestAnimationFrame(animate);
}
requestAnimationFrame(animate)
}
document.addEventListener("DOMContentLoaded", function(e){
main();
});
At the moment all of the tweening is linear ... it might look more realistic with a logarithmic or exponential tween ... but for simplicity i just left it as linear.
Jakob Jenkov has done a really nice on-line book about canvas here:
http://tutorials.jenkov.com/html5-canvas/index.html
Since yours is a learning experience, I would just point you towards:
The basic workflow of html5 Canvas: Anything drawn on the canvas cannot be altered, so all canvas animation requires repeatedly doing these things in an animation loop: (1) clearing the canvas, (2) calculating a new position for your objects, and (3) redrawing the objects in their new positions.
Animations: requestAnimationFrame as a timing loop: http://blog.teamtreehouse.com/efficient-animations-with-requestanimationframe
Transformations: Canvas gives you the ability to scale, rotate and move the origin of its drawing surface.
Styling: Canvas provides all the essential styling tools for drawing--including globalAlpha which sets opacity.
I'm trying to replicate a game called haxball, which is a really simple and basic 2d football game. However I am having trouble on the collision detection and I didn't want to use a engine like Box2d because it's a bit of overkill for what I want and I'm making the game just to practice, since I'm a beginner.
I can check if the collision happens, but I can't resolve it properly. I loop through all objects and check if they are colliding with the ball and then, if they are, I put the ball at the "border" of the object so that it stops being "inside" the other.
The problem comes here, because if the ball collides with a circle and a edge at the same time it will stay inside the edge or inside the circle.
This is the code of collision resolving for the circle and detection and resolving of the edge:
this.resolveCollisionCircle = function(circle) {
var nv = circle.pos.sub(this.pos);
nv = nv.setMag(circle.radius + this.radius).add(circle.pos);
this.pos = nv;
// I'll try to add later the bounce effect
}
this.edgeCollision = function() {
if(this.pos.x-this.radius < 0) {
this.pos.x = this.radius;
this.velocity = this.velocity.mult(new Vector(-1, 1));
}
else if(this.pos.x+this.radius > Width) {
this.velocity = this.velocity.mult(new Vector(-1, 1));
}
if(this.pos.y-this.radius < 0) {
this.pos.y = this.radius;
this.velocity = this.velocity.mult(new Vector(1, -1));
}
else if(this.pos.y+this.radius > Height) {
console.log('baixo')
this.velocity = this.velocity.mult(new Vector(1, -1));
}
}
The ball moves accordingly to a velocity vector, in this case it starts as (-4,0)
Demo of the bug: http://antoniomc.0fees.us/
Also! If you could point me to a good canvas tutorial that could teach me this things, I would appreciate it! I only seem to find for another languages, which helped me anyway, but it would be nice to see a canvas collision detection and resolution tutorial once in a while...
In .resolveCollisionCircle(), store the old position, change the position, and revert back to the old position and stop the ball completely if the new position is outside of the canvas.
this.resolveCollisionCircle = function(circle) {
//previous position
var prevPos = this.pos;
//set new position
var nv = circle.pos.sub(this.pos);
nv = nv.setMag(circle.radius + this.radius).add(circle.pos);
this.pos = nv;
//change back if out of canvas
if ((this.pos.x-this.radius < 0) || (this.pos.x+this.radius > Width) || (this.pos.y-this.radius < 0) || (this.pos.y+this.radius > Height)) {
this.pos = prevPos;
this.velocity = this.acceleration = new Vector(0, 0);
}
}