I'm setting this loop up with three animations, the first one that runs on initial screen load (firstAnimation). Then the next two animations use callbacks to loop between themselves (slideAnimation -> rotateAnimation -> slideAnimation...)
How can I stop the animation whichever animation it's in using POINTERDOWN and resume it on PONTERUP..?
The way I have done it below works only during the firstAnimation, as soon as its in the loop it obviously no longer targets those two animations. So how would I target all three animations using a single .pause() .resume() command?
var slideAnimation = function(){
scene.beginDirectAnimation(box, [xSlide], 0, 2 * frameRate, false, 2, rotateAnimation);
}
var rotateAnimation = function(){
scene.beginDirectAnimation(box, [yRot], 0, 2 * frameRate, false, 2, slideAnimation);
}
var firsAnimation = scene.beginDirectAnimation(box, [xSlide], 0, 2 * frameRate, false, 2, slideAnimation);
scene.onPointerObservable.add((pointerInfo) => {
switch (pointerInfo.type) {
case BABYLON.PointerEventTypes.POINTERDOWN:
light2.intensity =0.95;
firsAnimation.pause();
box.scaling = new BABYLON.Vector3(4, 1, -1)
break;
case BABYLON.PointerEventTypes.POINTERUP:
light2.intensity =0.15;
firsAnimation.restart();
box.scaling = new BABYLON.Vector3(1, 1, 1)
break;
}
});
Or does this need a different approach all together?
Babylon Group Animations could be useful for you - https://doc.babylonjs.com/how_to/group . It is a way to group animations and animatables, normalize then, and control them together.
You can see an example here - https://www.babylonjs-playground.com/#CBGEQX#5
If you want to see 2 animations running at the same time on one animatable (mesh), change line 79 from
animationGroup.addTargetedAnimation(animation2, box2);
to
animationGroup.addTargetedAnimation(animation2, box1);
And press the run button on the top.
Then pressing the pay button will run both animations on the same mesh.
Related
I am working off the Slingshot demo. The problem is that after the rock is fired, it can still be clicked on and dragged around, which I want to disable.
I've added a filter to the rock:
var rockOptions = {
density: 0.004,
restitution: 0.75,
collisionFilter: { mask: SOLID, category: NEXTBALL }
};
And to the mouseconstraint:
var mouse = Mouse.create(render.canvas),
mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
collisionFilter: { category: NEXTBALL },
constraint: {
stiffness: 0.2,
render: {
visible: true
}
}
});
And then in the click event I attempt to change that filter, so it should no longer match the mouses category:
Events.on(engine, "afterUpdate", function () {
if (
mouseConstraint.mouse.button === -1 &&
(rock.position.x > shootPosition.x + 20 ||
rock.position.y < shootPosition.y - 20)
) {
Composite.remove(engine.world, elastic);
rock.collisionFilter = {category: SOLID, mask: SOLID};
}
});
But it still is draggable. I'm guessing the problem is how I'm changing the filter on the rock, but I don't see anything in the docs to suggest a way to change it.
I don't think it's because of the categories I've set up, but here they are just in case (the solid and image ones do work, the ball doesn't collide with image ones:
const SOLID = 0x0001;
const IMAGE = 0x0002;
const NEXTBALL = 0x0003;
Help me make the rock stop being clickable
Preventing the mouse from manipulating already-shot rocks can be achieved with two simple changes denoted by // <--- in the code snippet below.
rock.collisionFilter.category = 0b10; sets the category to 2 for any rock that was just launched, before creating a new one and overwriting the rock variable with the next rock to be launched.
collisionFilter: {mask: 0b1}, sets the mask on the mouse constraint to only interact with bodies in category 1. Since disabled rocks are in category 2 (0b10), the mouse will no longer interact with them.
Here's the code in context. I used this commit in case anything changes.
Events.on(engine, 'afterUpdate', function() {
if (mouseConstraint.mouse.button === -1 && (rock.position.x > 190 || rock.position.y < 430)) {
rock.collisionFilter.category = 0b10; // <---
rock = Bodies.polygon(170, 450, 7, 20, rockOptions);
Composite.add(engine.world, rock);
elastic.bodyB = rock;
}
});
// add mouse control
var mouse = Mouse.create(render.canvas),
mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
collisionFilter: {mask: 0b1}, // <---
constraint: {
stiffness: 0.2,
render: {
visible: false
}
}
});
The default mask value is 32 bits all set, or 4294967295/0xffffffff. You may wish to be more precise and disable only the second bit on the mouse constraint: 0xfffffffd. This lets the mouse interact with anything but category 2 rather than just category 1.
The existing answer is correct that categories should be powers of two so that only one bit is set per category.
The reason rock.collisionFilter = {category: SOLID, mask: SOLID}; doesn't work is because it destroys the group property.
See also:
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?
Finally figured it out after much starting over, tweaking and examining different demos.
First the bitmasks for the categories have to be powers of two, so NEXTBALL has to be 0x0004 rather than 0x0003.
Next, you can't set the whole collisionFilter object on an established body, or it breaks the collisions. Instead, you have to use rock.collisionFilter.category = NEXTBALL;
I'm running into a bit of an odd issue with Paper.js - I'm using the library to scale the "petals" of a randomly generated flower while audio plays.
The issue crops up if the flower is "growing" and the user navigates to a different tab in the browser. Even though it appears that the onFrame event is not firing when the window is out of view, whichever petal is currently scaling at the time will continue to scale indefinitely.
I even tried using a special js library to determine if the window is in view and still wasn't able to get the petals to stop scaling.
You can view a demo here, as I was not even able to replicate this in a Paper sketch: https://demos2.paperbeatsscissors.com/
Also including my onFrame code here in case the problem is obvious to someone:
view.onFrame = function(event) {
// See if something is playing
if (playing > -1) {
// Get the active flower
var activeFlower = garden[playing],
activeData = activeFlower.data;
// Active layer and petal
var activeLayer = getEl(activeFlower, activeData.lIndex),
activePetal = getEl(activeLayer, activeData.pIndex);
// Variables
var time = activeData.audio.seek(),
scaleAmount = (1 / (activeData.timing / event.delta.toFixed(3))) * 2;
// Petal progression
if (!activeData.completed) {
if (activePetal.scaling.x < 1 && activePetal.scaling.y < 1) {
activePetal.pivot = {x: 0, y: activePetal.height / 2};
activePetal.scaling.x = activePetal.scaling.x + scaleAmount;
activePetal.scaling.y = activePetal.scaling.y + scaleAmount;
} else {
if (activeData.pIndex < (activeLayer.children.length - 1)) {
// If the petal is scaled, jump to a new petal
activeData.pIndex += 1;
} else {
if (activeData.lIndex > 0) {
// When all petals are bloomed, jump to a new layer
activeData.pIndex = 0;
activeData.lIndex -= 1;
} else {
// Set the flower as completed
activeData.completed = true;
}
}
}
}
activeFlower.rotate(.125, activeData.center);
// Reset the playing variable if the audio clip is complete and the flower has completed
if (!activeData.audio.playing() && time === 0 && activeData.completed) {
playing = -1;
}
}
}
Really stumped on this one so any help is greatly appreciated!
I think that your problem is coming from the fact that you base your scaling calculation on event.delta which represents the time elapsed since the last event fired.
The thing is that, if I'm not mistaken, under the hood, Paper.js onFrame event relies on requestAnimationFrame which does not fire when the tab if inactive.
So when you switch tab, wait for a while and get back to your tab event.delta value is big and your scaling value too, hence the size of your petals. This basic sketch showcase this behavior.
So in my opinion, you should simply check event.delta value and limit it if it's too high.
I'm currently experimenting a bit with Famo.us and there is actually one thing I can't yet wrap my head around.
In a small example i tried to create a HeaderFooterLayout, where the header contains a simple icon left aligned. A click on it will bounce it to the right end of the header.
Now with a simple Transform.translate this works not as smooth as expected on my Nexus4 and Nexus 7, but hell changing it to a SpringTransition rocks. Here is the code example:
var Transitionable = require('famous/transitions/Transitionable');
var SpringTransition = require('famous/transitions/SpringTransition');
Transitionable.registerMethod('spring', SpringTransition);
var logoStateModifier = new StateModifier({});
var logo = new ImageSurface({
size: [186, 43],
content: 'images/my-logo.png'
});
var posX = 0;
var adjustment = 20;
// Click event on image
logo.on('click', function() {
if(posX === 0) {
posX = (window.innerWidth - logo.size[0] - adjustment);
} else {
posX = 0;
}
var spring = {
method: 'spring',
period: 10,
dampingRatio: 0.3,
};
// transform translate with Easing
logoStateModifier.setTransform(
Transform.translate(posX,0,0),
{ duration: 1000, curve: Easing.inOutBack}
);
// spring transition
logoStateModifier.setTransform(
Transform.translate(posX, 0, 0), spring
);
});
So what I don't understand here is why Easing is so "slow" compared to the Physics driven SpringTransition?
The spring transition your requesting has a period of 10ms while the easing transition is 1000ms or 100 times slower. I tried your code "as is" and with a modification that compares more apples to apples and the transitions can run at the same speed (both laptop and devices.) First you should note that the minimum spring period is 150ms so the 10ms your asking for is actually 150. Second you are stacking the transitions so that one follows the other. The easing will take 1 second and then the spring will oscillate. You may want to try something slightly different... set the transitions to the following:
// transform translate with Easing
logoStateModifier.setTransform(
Transform.translate(posX,0,0),
{ duration: 150, curve: Easing.inOutBack}
);
// spring transition
logoStateModifier.setTransform(
Transform.translate(0, 0, 0), spring
);
This will behave slightly differently. On click (every other click actually) the logo will cross the screen at high speed and then come back. I expect you'll find that these transitions run at comparable high speeds. Of course for a slower more viewable test you can set the spring period to 1000 and the easing duration to the same and again the speeds should be comparable.
I'm developing a Fez-based HTML5 Canvas game using EaselJS and I've found a problem, that I can't solve myself, while trying to implement SpriteSheets to the hero.
The problem is that I've defined three animations to my hero ("stand", "jump" and "run") and when I try to change from one animation to another using hero.gotoAndPlay("run") or hero.gotoAndStop("stand") the animations don't change, change but show the first frame only or change to the wrong animation.
Can someone help me? What I'm doing wrong and how can I fix it? Thanks.
Here's the relevant JavaScript code I'm using to create the hero Sprite:
var data = {
images: ["http://i.imgur.com/M0GmFnz.png"],
frames: {width:34, height:46},
animations: {
stand:0,
run:[0,12,true,0.25],
jump:13
}
};
var spriteSheet = new createjs.SpriteSheet(data);
var hero = new createjs.Sprite(spriteSheet, "stand");
hero.offset = 4 + 1; // "+1" is important, change the first number only.
hero.regX = hero.offset + 2;
hero.regY = hero.offset;
hero.width = 34 - (hero.offset*2) - 12;
hero.height = 46 - (hero.offset*2);
hero.x = 0;
hero.y = 0;
hero.jumping = true;
stage.addChild(hero);
And the code I'm using to change the animation:
if(!left && !right){ // User isn't pressing left arrow or right arrow
hero.gotoAndStop("stand");
}else{
hero.gotoAndPlay("run");
}
JSFiddle
Official Site
If you are calling gotoAndStop or gotoAndPlay in a tick (or similar) then it will constantly reset to the first frame. You have to ensure you only call these functions one time when the animation changes.
A good way to set this up is to store the current animation, store it, and only change it up if the animation changes. Something like:
var animation;
if(!left && !right){ // User isn't pressing left arrow or right arrow
animation = "stand";
}else{
animation = "run";
}
if (currentAnimation != animation) {
hero.gotoAndStop(animation);
}
This is just an example, but should give you an idea. You should only call these methods one time to kick off an animation.
I am developing a simple HTML5 game presently which you can play test at the following URL:
http://frankcaron.com/simplegame/game.html
The code is here:
https://github.com/frankcaron/Personal_HTML5_GamePrototype/blob/master/js/game.js
In order to animate elements, I'm using a few setIntervals. The main game loop has a setInterval which works fine. Some fading elements use them, as well, without issue. The one I'm having an issue with is one for an on-click animation.
The issue I'm having is that, in Safari and also to some extent in Chrome, the click event and animation is not responsive. It doesn't fire every time. I think there is some conflict with the concurrent intervals running. I'm not sure why.
I've done some research on the other method of HTML5 animation using requestAnimFrame but I don't think that'll solve my problem here.
Here is the code of interest:
//Track mousedown
addEventListener('mousedown', function (e) {
//Safari Fix
e.preventDefault();
//If you click while the game is going, do your special move
performSpecial();
}
//Perform a special move
var performSpecial = function () {
switch(classType)
{
case 1:
//Class 1
break;
default:
//No class
renderExplosion();
}
}
//Render explosion special move
var renderExplosion = function () {
//Temp vars
var alpha = 1.0; // full opacity
var circleMaxRadius = 32;
fadeCircle = setInterval(function () {
ctx.beginPath();
ctx.arc(hero.x + 16, hero.y + 16, circleMaxRadius, 0, 2 * Math.PI, false);
ctx.lineWidth = 5;
ctx.strokeStyle = "rgba(255, 0, 0, " + alpha + ")";
ctx.stroke();
circleMaxRadius = circleMaxRadius + 5;
alpha = alpha - 0.05 ; // decrease opacity (fade out)
if (alpha < 0) {
clearInterval(fadeCircle);
}
}, 1);
};
Welp, I rewrote the application using window.requestAnimationFrame and I no longer have this problem. The code is up on my Github if anyone wants to see the full solution.
In brief, I removed all of the intervals and replaced the entire game loop. When an animation effect is needed, I turn a flag on corresponding with the event. The renderer checks this conditional and renders the effect if the flag is on, turning the flag off again when it's done.