[SOLVED] Scaling footstep animation based on distance

Started by CShelton, Thu 04/03/2010 18:19:18

Previous topic - Next topic

CShelton

Facts:

1. 30 frame looping walk animation.
2. Game is set to run at 60hz.
3. AdjustSpeedWithScaling is true.
4. AnimationDelay on the walk is set to 1 (playing 30 frames as if it was 60)

The problem I'm having is that when the character arrives at his location, if he happens to be mid-animation with a full leg extension, it pops him back to stand. This looks pretty bad when you've got a high framerate animation, so I've made several attempts to compensate.

MovementLinkedToAnimation does not fix this issue, as it doesn't seem to complete or scale an animation, rather it only moves the player when the frame is changed. Even in this mode, I can get jarring animation pops.

I've already figured out how to hijack the walk animation before it can end abruptly, but that hijacking is based on the distance he has left to travel. What would be great would be if I could pre-calculate the number of steps he needs to take and then somehow scale animation speed over time so that he plays an even number of animation cycles to get there, but with scaled movement and other factors, this would be very tricky.

Any ideas out there?

=============== UPDATE ===============

SOLVED: Please see post below for demo and project files.

Ali

This has been suggested before in sllightly different terms: http://www.adventuregamestudio.co.uk/yabb/index.php?topic=33107.0

I made a demo of a hack-ish way of creating the effect of a character coming to a stop. Unfortunately, there is a barely-perceptible flicker just before the 'stopping' animation is played, and to be effective it depends on a slightly exaggerated movement. The old link is dead but I uploaded it again here:

http://www.filefront.com/15740953/Stopping.zip

I'd love to see a better module than mine, or a built-in solution too!

GarageGothic

Quote from: Ali on Thu 04/03/2010 20:55:08Unfortunately, there is a barely-perceptible flicker just before the 'stopping' animation is played

You could probably avoid this by making the player character invisible and using a dummy character that every loop is updated to have the same ViewFrame and coordinates as the player - UNLESS the player has just stopped moving, in which case you go straight to the stopping animation.

CShelton

Ali, that is an excellent demo! it will give me a great head start on how to hack through this. I don't think I'll end up with something module worthy as this is going to require some serious rigging to work around.

I've scoured the thread you sent me, and another thread contained within discussing transitions. It seems that there's no elegant solution even to this day (those were from 2007).

I'll try your method and see if I can't work around that standing frame flicker. I'm working with 3d rendered frames so making changes is a snap. I'll post my results as they come in.

Quote from: GarageGothic on Thu 04/03/2010 21:08:43
You could probably avoid this by making the player character invisible and using a dummy character that every loop is updated to have the same ViewFrame and coordinates as the player - UNLESS the player has just stopped moving, in which case you go straight to the stopping animation.

That is similar to what I have in mind, but I think I'll use an object since you can string together arbitrary animations using the .graphic property, similar to what Ali is doing in his test example.

GarageGothic

Quote from: CShelton on Thu 04/03/2010 23:01:09That is similar to what I have in mind, but I think I'll use an object since you can string together arbitrary animations using the .graphic property, similar to what Ali is doing in his test example.

You can do the same thing with characters, you just need to set its ViewFrame.Graphic.

Ali

Just a note to say that GarageGothics suggestion got my script working perfectly. However this solution is more effective with a cartoony stopping animation that wouldn't suit all styles of animation.

I've just started replaying Broken Sword and it got me thinking.

The solution in that game is that George never moves less than a step away from where he's standing. So if you click immediately next to his feet he does nothing.

Similarly, he doesn't walk exactly where the player clicks. He gets near to the spot you click and then his animation comes to a stop when the animation reaches the right frame. I may not have time to try such a thing in AGS, but I thought I'd mention it in case it was of any interest.

GarageGothic

Interesting research on Broken Sword, Ali. Precise walking is pretty much pointless in modern adventure games - no falling off cliffs, no "you have to get closer" interaction responses, cursor change on exit hotspots). Unless a puzzle requires you to stand in a very specific spot (e.g. hiding from the guard in Blackwell Legacy) you barely ever use the walk cursor unless it's a scrolling screen. So it should be perfectly acceptable to use approximate WalkTo coordinates if it helps with animation.

I was thinking it should be possible to use an invisible character walking at a faster speed (and relatively faster animation speed to keep frames/coordinates in sync) than the player character, and start polling his ViewFrames as he nears the destination (x,y). That would tell you at what coordinate the invisible character begins his final step, so you can use that info to trigger the visible characters stopping animation once he catches up.

CShelton

Excellent ideas, keep em coming!

I am totally okay with having the player get "close enough" to the position clicked, so some mix of the methods you guys have come up with will work for me.

I might give that ghostwalker idea a try GG, just to see if I can really get things working nicely. All of my floors have scaled ranges, and some have extensive travel in the Y, so trying to figure the math without walking the path first is pretty much quantum mechanics.

To maintain animatronic fidelity, I also plan to implement a deadzone around the character so you can't make him take teeny steps that look dumb.

CShelton

Thanks Ali and GarageGothic! I've managed to get exactly what I want with the "ghostwalker" method.  By ghostwalker, I'm referring to the idea that you can have a hidden 2nd character that walks to the clicked location before the player does.

There are several things needed to pull this off:

1. The ghostwalker must be able to move at double the player speed in X and Y.
2. The ghostwalker must able to animate at twice the player's animation speed.
3. The ghostwalker has to be moved to the player's current room.  I did this in the onEvent when the player enters a new room.
4. The ghostwalker has to be managed in a way that he resets to the players x,y whenever a click occurs.
5. The ghostwalker should be NOT solid, but otherwise should be an exact duplicate of your player character.

I'm tracking the last frame, and the total number of frames the ghost plays while walking a path. I'm also tracking the number of frames the player has played so I know when he's getting close to his destination (based off the ghost's frame count).

When the player is within two strides of his destination, I start checking to see if the next stride is his last.

My walk animation is a step left, step right at 32 frames, minus the first frame (standing frame). So my good stopping points are frame 1, and frame 16. With what I have set up, my character always stops on one of those frames. He's never further off than one step from his destination.

This method works with walks in all directions, and works with regions that scale. This totally isn't a module, and there's still some stuff to clean up,  but here's the code for those who might be interested.

Code: ags

//GLOBALS g_ClickX and g_ClickY are the X and Y of where the player just clicked.

int ghostCounter;
int playerCounter;
int ghostLastFrame;
bool ghostToldToMove = false;
bool playerStepAgain = false;

function resetPlayerMovement()
{
	cEgoGhost.StopMoving();
	ghostToldToMove = false;
	playerStepAgain = false;
	cEgoGhost.x = player.x;
	cEgoGhost.y = player.y;
	playerCounter = 0;
	ghostCounter = 0;	
}

function checkPlayerMovement()
{
	// player started moving, start moving the ghost to the same destination
	if (player.Moving && !cEgoGhost.Moving && !ghostToldToMove)
	{
		ghostToldToMove = true;
		cEgoGhost.Walk((GetViewportX() + g_ClickX), (GetViewportY() + g_ClickY));
	}
	
	// track the player's current frame
	if (player.Moving)
		playerCounter++;
	
	// track the ghost's current frame (has to be seperate from player, as the ghost will stop moving before player does).
	if (cEgoGhost.Moving)
	{
		// ghost moves at double the speed, double the animation rate
		ghostCounter += 2;
				
		// always save the last ghost frame so we know where the player animation would end up
		ghostLastFrame = cEgoGhost.Frame;
	}
	
	// the player has stopped, reset all counters and move ghost position to player's xy so we can start over
	if (!player.Moving)
		resetPlayerMovement();

	// ghost has already walked the path, but player hasn't arrived yet
	if (!cEgoGhost.Moving && cEgoGhost.x != player.x)
	{
		// don't bother stopping the player early if the ghost ends naturally on a good stopping frame
		if (ghostLastFrame != 1 && ghostLastFrame != 16)
		{
			// start checking for stopping the player, we're one ghost stride from the destination
			if (playerCounter >= (ghostCounter - 30))
			{
				// we want to stop on 16 if the ghost last frame is > 16, otherwise we're 2 steps short
				if (ghostLastFrame > 16)
				{
					// the player is more than one step from destination save a var 
					// to force us back in here again after we take another step.
					if (playerCounter > ghostLastFrame || playerStepAgain)
					{
						playerStepAgain = true;
						
						if (player.Frame == 16)
							player.StopMoving();
					}
				}
				// the ghostLastFrame is < 16, we can't take another step, stop at frame 1
				else if (player.Frame == 1)
					player.StopMoving();
			}
		}
	}
}





Ali

I was literally just about to post announcing my total failure to get this working and asking for help. Well done!

It'd be great if you could post a demo game, though I'll understand if you want to keep your graphics under wraps while your game is in progress. Thanks for posting the code, I'll see if I can get it working!

CShelton

#10
Quote from: Ali on Wed 17/03/2010 00:43:16
It'd be great if you could post a demo game, though I'll understand if you want to keep your graphics under wraps while your game is in progress. Thanks for posting the code, I'll see if I can get it working!

Ok Ali, I whipped up a project file and my first demo. Also included, is my mangled, but documented version of your scrolling/parallax module. The one where I was working on smooth scrolling with directional changes and a deadzone. I've yet to finish that up, and I think you figured it out in the meantime, but I just left it in because smooth scrolling rocks.

GhostWalker Compiled Demo (10mb)
GhostWalker Project (10mb)

AGS 3.2 Latest Beta is probably required.

Ali

Thanks a lot, that looks great!

I had trouble implementing your script with a faster walking character with fewer frames, but I'll give it another try now I've seen it working.

The only thing this demo needs to be perfect (a la Broken Sword) is an animated starting and stopping movement. As it is the character's standing position seems a little crouched, perhaps so as not to jar with his walking poise.

Animated starting is easy to do, I just created a walkcycle which started from stopped and moved up to full speed, then switched to the regular walkcycle after one step. With this module, I guess it would be possible to use the Counter to switch them over to one of two 'stopping' walkcycles (left foot and right foot) at the appropriate time.

Thanks again for the great work, I look forward to experimenting.

GarageGothic

I'm pleased to see that it worked out!

I agree with Ali though, that for graphics this hi-res/high fps that making a stopping animation would be worthwhile. When everything else is so smooth (and the walking speed slowish, sorry, this is a pet peeve of mine), it does seem a bit abrupt to halt at the standing frame. I'm not sure if animating a full step is necessary, possibly some kind of "regaining balance" animation could work if run just as he finishes the last step.

I think I'll be using this method in some of my planned future games as well, especially as they will allow running, and then it looks REALLY weird to halt mid-step.

CShelton

Oh yeah I'm totally going to experiment with a stopping animation. Now that I have a handle on what he's doing, and when, I will expand upon this to give him starts, stops, turn arounds. The animations and the model are temp, when I'm done testing and have a real character, I'll get a real animator to do me a decent walk cycle.

And yeah I intend to take advantage of doing a stopping left/right since I know which foot fall will be the last. Right now his stopping frames are really just between stride frames, so it looks pretty bad, nontheless it stops where I want it to!

Misj'

Great work!

Quote from: GarageGothic on Thu 11/03/2010 02:06:25
Interesting research on Broken Sword, Ali. Precise walking is pretty much pointless in modern adventure games - no falling off cliffs, no "you have to get closer" interaction responses, cursor change on exit hotspots). Unless a puzzle requires you to stand in a very specific spot (e.g. hiding from the guard in Blackwell Legacy) you barely ever use the walk cursor unless it's a scrolling screen. So it should be perfectly acceptable to use approximate WalkTo coordinates if it helps with animation.

Well...it might be a problem if an animation requires the character to stand at a specific location and interact with the environment. Examples of this are picking up items, turning levers, etc. In an ideal animation, the character would walk to the correct position, reach up and perform the interaction without either the character 'shifting' a few pixels, or the object of interest moving (when it becomes part of the character-interaction-sprite). Even a shift of a single pixel can be noticed by the player (who happens to focus on the animation when it's playing), and would reduce the overall illusion.

It should be noted, that in many games there is a generic 'pick up' animation, where only the sprite makes a movement, but the object which is picked up isn't involved and is simply hidden from view. But to me this too makes the illusion less believable (actually, I really hate them; but understand the design-choice).


Ali

Good point Misj', but as you say this is still a great start.

Broken Sword does uses a walking effect like CShelton's, and somehow also achieves pixel-perfect positioning allowing characters to pass objects from one to another.

I guess the Virtual Theatre engine splits the screen into 'stepping points' with characters moving to the nearest point to your click. At the moment I can't think of a way of doing this in AGS that would take walkable areas and scaling into account, but I look forward to seeing this module develop!

CShelton

Quote from: Misj' on Wed 17/03/2010 19:55:07
Well...it might be a problem if an animation requires the character to stand at a specific location and interact with the environment. Examples of this are picking up items, turning levers, etc. In an ideal animation, the character would walk to the correct position, reach up and perform the interaction without either the character 'shifting' a few pixels, or the object of interest moving (when it becomes part of the character-interaction-sprite). Even a shift of a single pixel can be noticed by the player (who happens to focus on the animation when it's playing), and would reduce the overall illusion.

I've been thinking about this issue for a while now. What I've been thinking is that I'd have adjustment regions around object pick up spots,  or something that lets me detect "nearby" items. When the player clicks near something of interest, I could use a special transition animation that puts them in place to pick that object up. This would need to be subtle though, as I don't want transitions to clue the player in to every item of interest.

Since I'm working with 3D, I probably will do custom animations for each pickup, because it's fairly trivial, and adds a lot to see the character put his hands on something and interact with it.

I won't accept letting the player get close enough, then reach out with a bad animation that misses the mark, so when an object is nearby, I'll have to assume the player is probably going to pick it up, or interact with it.

I've thought about sending the ghost a step closer to the item than the player clicked, that way, if I need to force the player to take an extra step they didn't intend, I'll know if it's a valid location. I'm sure I can design around this, but I don't want it to be a major hassle to do so.

I'm open to suggestions, so please, continue to discuss! It's really been super helpful, and I'm continuing work on this system daily.

Misj'

Quote from: CShelton on Wed 17/03/2010 22:00:30I've been thinking about this issue for a while now. What I've been thinking is that I'd have adjustment regions around object pick up spots,  or something that lets me detect "nearby" items. When the player clicks near something of interest, I could use a special transition animation that puts them in place to pick that object up. This would need to be subtle though, as I don't want transitions to clue the player in to every item of interest.

I'm just thinking out loud here (so please bear with me)...

transition animations would probably only work if A. the distance to cover would either be known, or B. if the transition animation itself would be 'scalable'. A would only be the case if the character-movement always ends on an intersection of a (invisible) grid. This should be possible, since the movement is directly related to the step-distance, which is a constant (albeit perspective/scale related).

This would also imply that - if the position of the player character on the grid is known - it should be relatively simple to create an animation that is specifically tailored to that situation, since the location of both the interaction-object and the character are known (and thus their distance).

Such a 'grid' might only be possible though - if at all - if the character has a constant step-distance for both legs (so no limping)...

Option B has it's own set of problems. But let's first look at the idea: imagine a 'small' step to go to the right position. If this step takes 5 frames, and each frame moves a single pixel, at the end of the animation the character has moved 5 pixels. But...if the distance to overcome is 7 pixels, the dynamic animation would decide that between frame 1 and 2 and 3 and 4 the distance should be 2 rather than the usual 1 pixels. Similarly, if the distance to overcome is 3 pixels, it would decide that between certain frames the movement should be 0 rather than 1. Of course this could create a non-homogenous movement-speed (in a homogeneous animation), which could only be countered by sub-pixel translations of the sprite (I smell Fourier space).

Also...for a jump/run animation this might work fine (since both feet leave the ground at the same time). However, walking animations would suffer from 'sliding feet', where the foot is not firmly on the ground but slides a little. If you look at broken sword this sliding almost doesn't occur...which is a good thing. In AGS, however, due to the way the pathfinder is implemented (with a constant movement for each frame), sliding can be quite evident, particularly at high-res. This is of course - at least somewhat - countered by adding more frames because then the slide-per-frame is lower...however, the slide-per-movement will still be the same (this could be reduced by having smaller steps; but that results in the slow/sluggish feel of the animation that Garage pointed out earlier).

Ok...I'm done thinking for today.

SMF spam blocked by CleanTalk