Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

9 years ago by rootbeerking

Hello, I looked once again for an answer in the documentation but the Animations Tutorial doesn't seem to cover this, and it searching on the forums turned up nothing so I thought I'd ask: how does one go about having it so that when a key is pressed an animation is played, and only after the animation has completely finished playing through once then it goes back to the idle animation?

I've got my animation set up like so:
this.addAnim( 'shot', 0.167, [1,2], true );

and I tried this:

if ( ig.input.pressed('shoot') ) {
		this.currentAnim = this.anims.shot;
		if ( this.currentAnim.loopCount ) {
			this.currentAnim = this.anims.idle;
		}
	}

But that didn't work.

If it helps to know I also have other animation stuff above that code that looks a little something like this:
	if (this.vel.y < 0) this.currentAnim = this.anims.jump;
	else if (this.vel.y > 0) this.currentAnim = this.anims.fall;
	else if (this.maxVel.x > 100 && this.accel.x > 0) this.currentAnim = this.anims.run;
	else if (this.maxVel.x > 100 && this.accel.x < 0) this.currentAnim = this.anims.run;
	else if (this.accel.x > 0) this.currentAnim = this.anims.walk;
	else if (this.accel.x < 0) this.currentAnim = this.anims.walk;
	else this.currentAnim = this.anims.idle;

9 years ago by Jesse

Looking at the Animation source, I don't see any way to do this either and I'm going to need it. It looks like the code will never know if it finishes an animation because it takes time into account and jumps around the sequence array based on time, not on an incrementing index.

I would fill the sequence in such a way that '0' is not a real frame, but a sentinal value and a corresponding blank tile in the sheet so it won't "draw" a finished animation, so a zero will simply go to blank space in the sheet.

So it would look like [1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,0,0,0]

I have no idea how many zeros you would need, but you need more than one or two because of frame skipping that will most likely occur across the different platforms.

Then you could just check if the animation's "frame" property is 0, or catch it early with "frame+1", but mind the sequence length.

If not, you must inject code into "Animation", that's the only way I can see fixing this.

9 years ago by rootbeerking

Oh dear, that doesn't sound pretty...Hopefully someone else has an easier solution.

9 years ago by Jesse

Test to see if the frame was "moved back"

		ig.Animation.inject({
			update: function() {
				var lastFrame = this.frame;
				this.parent();
				if(this.frame < lastFrame) {
					this.done = true;
                                // call a callback ?
				}
			}
		});

9 years ago by MikeL

@rootbeerking: I think part of the problem is that you've got this line:
        if ( this.currentAnim.loopCount ) {
            this.currentAnim = this.anims.idle;
        }

within your check for the shot input, so it only gets checked at the time the shot is pressed. It would need to be pulled out into the main update function. You would have to first check if the shot animation is the current animation, then check if the animation is complete.

9 years ago by rootbeerking

@MikeL I tried this:
if ( ig.input.pressed('shoot') ) {
		this.currentAnim = this.anims.shot;
	} else if ( this.currentAnim == this.anims.shot && this.currentAnim.loopCount ) {
			this.currentAnim = this.anims.idle;
		}

But that didn't work either. All it did was VERY quickly play the last frame of the animation, and I'm sure that isn't because the delay isn't long enough.

9 years ago by paularmstrong

Make sure you rewind your animation when you set it:

##
this.currentAnim = this.anims.shot.rewind();
##

This will set your animation to the first frame with zero loopCount.

edit: preformatted text doesn't seem to be working? weird.

9 years ago by rootbeerking

@paularmstrong Yeah, maybe I'm not using that right, but that doesn't work either... I mean doing that now I get the first frame but not the last and it's still only for the split second that the key is pressed.

9 years ago by MikeL

Hmmm. Try making your time per frame longer (like 2 secs), just to see what is happening:
this.addAnim( 'shot', 2, [1,2], true );

Will it play one or both frames? If not which one will it play and for how long?

9 years ago by MikeL

Looks like the last line in your if-else sequence above:
   else this.currentAnim = this.anims.idle;

is not taking into account if the entity is currently in the 'shot' phase, therefore it will immediately revert to the idle animation.

9 years ago by rootbeerking

@MikeL Okay so it's sort of working as it should...But I had to get rid of "else this.currentAnim = this.anims.idle;" in my other animation stuff, so that seems to be the real issue. So how do I get
if ( ig.input.pressed('shoot') ) {
        this.currentAnim = this.anims.shot.rewind();
    } else if ( this.currentAnim == this.anims.shot && this.currentAnim.loopCount ) {
            
	    this.currentAnim = this.anims.idle;
	    
        }

to play nicely with this?
    if (this.vel.y < 0) this.currentAnim = this.anims.jump;
    else if (this.vel.y > 0) this.currentAnim = this.anims.fall;
    else if (this.maxVel.x > 100 && this.accel.x > 0) this.currentAnim = this.anims.run;
    else if (this.maxVel.x > 100 && this.accel.x < 0) this.currentAnim = this.anims.run;
    else if (this.accel.x > 0) this.currentAnim = this.anims.walk;
    else if (this.accel.x < 0) this.currentAnim = this.anims.walk;
    else this.currentAnim = this.anims.idle;

9 years ago by MikeL

Depends exactly on how you want your animation to work. Do you want the shot animation to override every other animation? For example, what happens if the entity shoots and then falls immediately afterwards. Which sequence should take precedence? Or say the entity is walking when a shot is fired, should the shot sequence run its course and then go back to walking?

9 years ago by rootbeerking

Basically what I'm trying to do is just a standing still shooting animation, so it should play the animation and stop the player from doing anything else until the animation is done.

9 years ago by MikeL

Ok. Give this a try. I can't really test it other than in my head, so let me know if it works.

//If shoot is pressed then change the animation to 'shot' and rewind.
if ( ig.input.pressed('shoot') ) 
        this.currentAnim = this.anims.shot.rewind();

if ( this.currentAnim == this.anims.shot ){
    if ( this.currentAnim.loopCount ) {
        //Shot has played through all the way. Go to idle animation
        this.currentAnim = this.anims.idle;
    }
}else{
    //We are not in the shot animation (which overrides all other animations)
    //so check parameters to see which animation to play.
    if (this.vel.y < 0) this.currentAnim = this.anims.jump;
    else if (this.vel.y > 0) this.currentAnim = this.anims.fall;
    else if (this.maxVel.x > 100 && this.accel.x > 0) this.currentAnim = this.anims.run;
    else if (this.maxVel.x > 100 && this.accel.x < 0) this.currentAnim = this.anims.run;
    else if (this.accel.x > 0) this.currentAnim = this.anims.walk;
    else if (this.accel.x < 0) this.currentAnim = this.anims.walk;
    else this.currentAnim = this.anims.idle;
}

9 years ago by rootbeerking

Thank you very much MikeL, that worked perfectly. One last question regarding this: if I wanted to have it so, for example, if the character is walking while the shoot key is pressed a different animation is played, where would I insert the code for that? Would I do this:
if ( this.accel.x > 0 && ig.input.pressed('shoot') ) 
        this.currentAnim = this.anims.walkingshot.rewind();

if ( ig.input.pressed('shoot') ) 
        this.currentAnim = this.anims.shot.rewind();

if ( this.currentAnim == this.anims.walkingshot ){
    if ( this.currentAnim.loopCount ) {
        this.currentAnim = this.anims.walk;
    }
}

if ( this.currentAnim == this.anims.shot ){
    if ( this.currentAnim.loopCount ) {
        this.currentAnim = this.anims.idle;
    }
}else{
    //We are not in the shot animation (which overrides all other animations)
    //so check parameters to see which animation to play.
    if (this.vel.y < 0) this.currentAnim = this.anims.jump;
    else if (this.vel.y > 0) this.currentAnim = this.anims.fall;
    else if (this.maxVel.x > 100 && this.accel.x > 0) this.currentAnim = this.anims.run;
    else if (this.maxVel.x > 100 && this.accel.x < 0) this.currentAnim = this.anims.run;
    else if (this.accel.x > 0) this.currentAnim = this.anims.walk;
    else if (this.accel.x < 0) this.currentAnim = this.anims.walk;
    else this.currentAnim = this.anims.idle;
}

Or something else?

9 years ago by MikeL

Probaly not. But try this:

//If shoot is pressed then change the animation to 'shot' or 'walkingshot' and rewind.
if ( ig.input.pressed('shoot') ) { 
    if ( this.accel.x != 0 ){
        this.currentAnim = this.anims.walkingshot.rewind();
    }else{
        this.currentAnim = this.anims.shot.rewind();
    }
}

if ( this.currentAnim == this.anims.shot || this.currentAnim == this.anims.walkingshot ){
    if ( this.currentAnim.loopCount ) {
        //Shot has played through all the way. Go to idle animation
        this.currentAnim = this.anims.idle;
    }
}else{
    //We are not in a shot animation (which overrides all othe animations)
    //so check parameters to see which animation to play.
    if (this.vel.y < 0) this.currentAnim = this.anims.jump;
    else if (this.vel.y > 0) this.currentAnim = this.anims.fall;
    else if (this.maxVel.x > 100 && this.accel.x > 0) this.currentAnim = this.anims.run;
    else if (this.maxVel.x > 100 && this.accel.x < 0) this.currentAnim = this.anims.run;
    else if (this.accel.x > 0) this.currentAnim = this.anims.walk;
    else if (this.accel.x < 0) this.currentAnim = this.anims.walk;
    else this.currentAnim = this.anims.idle;
}

However, any more code beyond this is US $0.05 per line ;)

9 years ago by rootbeerking

Haha, thanks again, I really appreciate the help.

The only problem I see with that is that I wanted to have it so if the player is doing the walking shot it doesn't go back to the idle animation but instead it displays the walking animation. Cause I mean having it be "shot or walking shot" would mean if the character was walking, and then went into the walking shot animation, after that animation ended. and assuming the character is still walking, wouldn't there be a split second where the idle animation would be shown?

9 years ago by MikeL

Well, give it a try. I'm thinking it won't really be detectable. But if so, you could try this if I'm not mistaken on the syntax -

Change this line (the one directly after the loopcount check):
this.currentAnim = this.anims.idle;

to
this.currentAnim == this.anims.walkingshot ? this.currentAnim = this.anims.walking : this.currentAnim = this.anims.idle;

9 years ago by Jeremy

I would be nice to be able to pass a callback function so when the animation finishes that function will run.

Maybe something like http://api.jquery.com/animate/

9 years ago by daveb

I created a new events plugin that solves this problem. It does the following things:

Adds an Animation API allowing you to be notified when a particular animation reaches a particular frame or when a particular animation completes.

Adds an Entity API allowing you to add, fire and remove events on entities directly

Adds an event aggregator allowing you to add, fire and remove events on any Javascript object. This aggregator is the basis for the Entity and Animation APIs and effectively decouples publishers from listeners.

This plugin can be found here: http://www.pointofimpactjs.com/plugins/view/7/impact-events

8 years ago by ansimuz

I used this simple solution.

1. added a shoottimer to the entity

2. When i press shoot i reset the timer

3. if the timer is below 0.5 secs it displays a different anim (the shooting animation)

Heres an example:


// reset the shoot timer when player shoots
if(ig.input.pressed('shoot')){
                    this.shootTimer.reset();
...
}


// later on the update choose the correct animation
if(this.shootTimer.delta() < this.shootTimerDelay){
    this.currentAnim = this.anims.shoot;
}else{
    this.currentAnim = this.anims.idle;
}

Hope it helps

6 years ago by zedd45

Dave B's plugin is very solid; I've used it on two different games for two different features (animation callbacks and event aggregator). Both work great! Thanks for this awesome plugin!

6 years ago by Bum

This works exceptionally well:

if(this.currentAnim.frame == this.currentAnim.sequence.length - 1){
    // ... do stuff
}

5 years ago by Joncom

Quote from Bum
This works exceptionally well:

##
if(this.currentAnim.frame == this.currentAnim.sequence.length - 1){
// ... do stuff
}
##
Something to note however, depending how fast your animation is, it's possible for some frames to be skipped over (including the last one). This should work though if you tell the animation not to loop via the stop argument.

5 years ago by Bum

I've had no problems with it without telling the animation not to loop, but in the case the steps per frame are too fast, I can see that being the best method to ensure the algorithm works.
Page 1 of 1
« first « previous next › last »