I have some animations running at the same time with different time length so awaiting is not my best option ...
How can I wait for the animation to be completed?
something in these lines:
accepted.TranslateTo(
newX, newY,
1500, Easing.CubicIn,
(o,e) => {
accepted.MakeGlow();
}
rejected.TranslateTo(
HazardsDeckDiscard.X,
HazardsDeckDiscard.Y,
300,
Easing.CubicIn,
(o,e) => {
RemoveFromScreen(rejected);
}
);
Related
I am making a platformer type game and need help on knowing how to delay the time before going downwards on the jump function i have tried the thread function and it didn't work I do have a question related to that but if there are any alternatives please let me know that would be very much appreciate it.
Here's a way to delay something without stopping the program. It's a class I wrote a long time ago, when I was still a student, so I'm not proud of it but I think that it's a good way to introduce someone to millis().
Long story short, this class will let you enter a number of milliseconds it will wait for and you can, in your loop, test to see if the delay is expired or not. Here's the class:
class Delay {
int limit;
Delay (int l) {
limit = millis() + l;
}
boolean expired () {
return millis() > limit;
}
}
And here's an example of a program waiting 5000 millis (5 seconds) after a mouse click before letting you click again. It's pretty self-explanatory, but don't hesitate to ask questions about it:
int count;
Delay delay;
void setup() {
size(400, 400);
delay = new Delay(0);
}
void draw() {
background(255);
//
if (!delay.expired()) {
fill(color(255, 0, 0));
rect(100, 100, 100, 100);
fill(0);
text("Time before click is allowed: " + (delay.limit - millis()), 20, 20);
}
text("Time since last click: " + (millis() - count), 20, 40);
}
void mouseClicked() {
// only lets you start a new delay if the last one is expired
if (delay.expired()) {
delay = new Delay(5000);
count = millis();
}
}
class Delay {
int limit;
Delay (int l) {
limit = millis() + l;
}
boolean expired () {
return millis() > limit;
}
}
Knowing how to time stuff is important, but timing stuff like jumps in a platformer often include other considerations, like touching the ground or not. Still, I hope that this will help you.
Have fun!
I'm making a stopwatch and when I wanna reset the clock for the second time, it is not changed.
On click at the first time, it sets h: 0, m: 0, s: 0. But when click again, it doesn't set h: 0, m: 0, s: 0 and stopwatch goes ahead.
const events$ = merge(
fromEvent(startBtn, 'click').pipe(mapTo({count: true})),
click$.pipe(mapTo({count: false})),
fromEvent(resetBtn, 'click').pipe(mapTo({time: {h: 0, m: 0, s: 0}})) // there is reseting
)
const stopWatch$ = events$.pipe(
startWith({count: false, time: {h: 0, m: 0, s: 0}}),
scan((state, curr) => (Object.assign(Object.assign({}, state), curr)), {}),
switchMap((state) => state.count
? interval(1000)
.pipe(
tap(_ => {
if (state.time.s > 59) {
state.time.s = 0
state.time.m++
}
if (state.time.s > 59) {
state.time.s = 0
state.time.h++
}
const {h, m, s} = state.time
secondsField.innerHTML = s + 1
minuitesField.innerHTML = m
hours.innerHTML = h
state.time.s++
}),
)
: EMPTY)
stopWatch$.subscribe()
The Problem
You're using mutable state and updating it as a side-effect of events being emitted by observable (That's what tap does).
In general, it's a bad idea to create side effects that indirectly alter the stream they're created in. So creating a log or displaying a value are unlikely to cause issues, but mutating an object and then injecting it back the stream is difficult to maintain/scale.
A sort-of-fix:
Create a new object.
// fromEvent(resetBtn, 'click').pipe(mapTo({time: {h: 0, m: 0, s: 0}}))
fromEvent(resetBtn, 'click').pipe(map(_ => ({time: {h: 0, m: 0, s: 0}})))
That should work, though it's admittedly a band-aid solution.
A Pre-fab Solution
Here's a stopwatch I made a while ago. Here's how it works. You create a stopwatch by giving it a control$ observable (I use a Subject called controller in this example).
When control$ emits "START", the stopWatch starts, when it emits "STOP", the stopwatch stops, and when it emits "RESET" the stopwatch sets the counter back to zero. When control$ errors, completes, or emits "END", the stopwatch errors or completes.
function createStopwatch(control$: Observable<string>, interval = 1000): Observable<number>{
return defer(() => {
let toggle: boolean = false;
let count: number = 0;
const ticker = () => {
return timer(0, interval).pipe(
map(x => count++)
)
}
return control$.pipe(
catchError(_ => of("END")),
s => concat(s, of("END")),
filter(control =>
control === "START" ||
control === "STOP" ||
control === "RESET" ||
control === "END"
),
switchMap(control => {
if(control === "START" && !toggle){
toggle = true;
return ticker();
}else if(control === "STOP" && toggle){
toggle = false;
return EMPTY;
}else if(control === "RESET"){
count = 0;
if(toggle){
return ticker();
}
}
return EMPTY;
})
);
});
}
// Adapted to your code :)
const controller = new Subject<string>();
const seconds$ = createStopwatch(controller);
fromEvent(startBtn, 'click').pipe(mapTo("START")).subscribe(controller);
fromEvent(resetBtn, 'click').pipe(mapTo("RESET")).subscribe(controller);
seconds$.subscribe(seconds => {
secondsField.innerHTML = seconds % 60;
minuitesField.innerHTML = Math.floor(seconds / 60) % 60;
hours.innerHTML = Math.floor(seconds / 3600);
});
As a bonus, you can probably see how you might make a button that Stops this timer without resetting it.
Without a Subject
Here's an even more idiomatically reactive way to do this. It makes a control$ for the stopwatch by merging DOM events directly (No Subject in the middle).
This does take away your ability to write something like controller.next("RESET"); to inject your own value into the stream at will. OR controller.complete(); when your app is done with the stopwatch (Though you might do that automatically through some other event instead).
...
// Adapted to your code :)
createStopwatch(merge(
fromEvent(startBtn, 'click').pipe(mapTo("START")),
fromEvent(resetBtn, 'click').pipe(mapTo("RESET"))
)).subscribe(seconds => {
secondsField.innerHTML = seconds % 60;
minuitesField.innerHTML = Math.floor(seconds / 60) % 60;
hours.innerHTML = Math.floor(seconds / 3600);
});
I'm trying to create a program that does something, waits for a set amount of time does another thing, then waits again. However, what actually happens is the program waits at the beginning then does both things without any delay between them.
var start, current
function setup() {
createCanvas(500, 550);
}
function draw() {
background(220);
print('a');
wait(500);
print('b');
wait(500);
}
function wait(time)
{
start = millis()
do
{
current = millis();
}
while(current < start + time)
}
The draw() function is executed several times per seconds (around 60 times per second, see framerate in the doc). This is also what we call a "draw loop".
Your logic seems to be very sequential (do this, then wait and to that, then wait and do another thing...), and maybe you should consider an other flow for your program than the draw loop.
If you want animation, the easy answer would be to stick to the answer provided by Rabbid76.
(read and compare elapsed time millis every time the draw loop executes).
If you want one-time events (things that happen only once when a wanted duration is reached), you should look into Promises (or async-await functions), also known as asynchronicity.
This subject can be confusing for beginners, but is very important in javascript.
Here is an example:
(link with p5 editor)
// notice we are NOT using the draw() loop here
function setup()
{
createCanvas(400, 400);
background('tomato')
// try commenting-out one of these:
doScheduleThings();
doAsyncAwaitThings();
}
// you can wait for a Promise to return with the javascript 'then' keyword
// the function execution's is not stopped but each '.then(...)' schedules a function for when the Promise 'sleep(...)' is resolved
function doScheduleThings()
{
sleep(2000).then(function() {
fill('orange')
ellipse(30,30, 50, 50)
})
sleep(1000).then(function() {
fill('yellow')
ellipse(130,30, 50, 50)
})
}
// you can also wait for a Promise to return with an async-await function
// the function's execution is stopped while waiting for each Promise to resolve
async function doAsyncAwaitThings()
{
await sleep(4000)
fill('blue')
rect(200,200, 50, 50)
await sleep(1000)
fill('cyan')
rect(300,200, 50, 50)
}
// a custom 'sleep' or wait' function, that returns a Promise that resolves only after a timeout
function sleep(millisecondsDuration)
{
return new Promise((resolve) => {
setTimeout(resolve, millisecondsDuration);
})
}
You cannot wait in the draw callback. The canvas is just updated when after draw was executed. You must evaluate the time in draw:
function draw() {
background(220);
let ms = millis()
if (ms < 500) {
// [...]
}
else if (ms < 1000) {
// [...]
}
else {
// [...]
}
}
As mentioned, the problem in your attempt is that you're waiting inside the draw() loop. This doesn't work, because draw() is going to be called continually.
A simple way to do it is the following:
function setup() {
//...
}
let task_done = false;
let last_done = 0;
function draw() {
const delay = 1000 //ms
if(!task_done) {
/* do something */
doSomething();
task_done = true;
last_done = millis();
}
else {
if(millis() - last_done > delay) {
task_done = false;
}
}
}
function doSomething() {
//...
}
It only executes something every delay ms.
I made a function to randomly pick a color for each element in the animejs but it doesn't work as expected. the random colors are picked only for the initiation of animation and on loop the colors don't change, is there any workaround to this?
here's what i have:
anime({
targets: '.jumperTexts',
scale: [
{value: [0.5 , 1], easing: 'easeInOutBounce', duration: 800}
],
backgroundColor:function(){
let letters = "3456789ABCDEF";
let color = '#';
for (let i = 0; i < 6; i++)
color += letters[(Math.floor(Math.random() * 13))];
return color;},
width:['80px','80px'],
height:['80px','80px'],
borderRadius: ['50%','50%'],
duration: 1200,
endDelay: 1000,
delay:anime.stagger(50),
direction: 'alternate',
loop: true
});
Most elegant way to do this using callback .complete:
function animate() {
anime({
targets: '.some-target',
opacity: function () {
// Generates random opacity each time animation is triggered
return anime.random(0.25,1);
},
complete: function (anim) {
// Re-triggers animation by callback.
animate();
}
});
}
This also works with anime.timeline().
I ran into the same problem some time ago. Function based values can be random, but that randomness is generated only once and the values stay the same, they're not generated each time the animation runs. That wasn't obvious to me, but checking the documentation for Function Generated Values you can confirm this. Clicking the example restarts the animation and changes the random values.
To solve this, I created a separate function to generate my random values, and global variables to receive them. Then I used loopComplete to call that function and update the random values. Here's an example of my code.
let myRandom;
function randomN() {
myRandom = Math.random();
console.log(myRandom);
}
function animateMe() {
let myAnim = anime({
targets: 'body',
//use myRandom value on any animatable property you like
duration: 1000,
easing: 'linear',
direction: 'alternate',
loop: true,
loopBegin: function (anim) {
randomN();
}
});
}
You can also call the random function after the animation finishes by using loopComplete. If you're not using loop, then you can use begin and complete.
I want switch between Run and Jump animations, but i have some problems :
If the Player run and i tap on the screen, the Player start Jumping (one time), and the Jumpanimation starts but donĀ“t end , so the player is running with the Jumpanimation.
Do you know where my fault is?
My code :
// Runanimation + Player Run
public void setRunning()
{
canRun = true;
final long[] PLAYER_ANIMATE = new long[] { 100, 100, 100,};
animate(PLAYER_ANIMATE, 0, 2, true);
}
// Jumpanimation + Player Jump
public void jump()
{
if (footContacts < 1)
{
return;
}
body.setLinearVelocity(new Vector2(body.getLinearVelocity().x, 10));
final long[] PLAYER_JUMP_ANIMATE = new long[] { 100, 100, 100, 100, 100, 100};
animate(PLAYER_JUMP_ANIMATE, 0, 5,true);
}
Thx Seref
You are animating with loop boolean set to true, which means its keep looping animation. You should have some kinds of flags (booleans) like jumping and running, so inside set running method you should check if jumping equals true, and if so, stopAnimation() and animate using different frames (in this case running)