ஒழுக்கின்மை (Paul Eres)
|
|
« on: May 11, 2009, 01:59:43 PM » |
|
This is again an overly simple/obvious question which I can't figure out after some amount of trial and experimentation, but how would you code a hopping motion from an overhead perspective?
As an example, the motion of the Pol's Voice or Tektites in Zelda 1. They don't move straight, but instead hop from location to location.
For those who haven't played Zelda 1, the movement of the enemies between 12 and 15 seconds in and between 25 and 28 seconds in this video is what I mean. I know it happens quick, but it's a speedrun.
|
|
|
Logged
|
|
|
|
BorisTheBrave
|
|
« Reply #1 on: May 11, 2009, 02:12:22 PM » |
|
1)Pick a nearby (valid) point. 2) Hop to it. 3)Repeat
|
|
|
Logged
|
|
|
|
ஒழுக்கின்மை (Paul Eres)
|
|
« Reply #2 on: May 11, 2009, 02:19:32 PM » |
|
I'm not sure how to apply that -- would x be one side of the equation and y the other side? How would the target location and starting location be involved so that you could be sure it'd land on that spot?
|
|
|
Logged
|
|
|
|
Matt Thorson
|
|
« Reply #3 on: May 11, 2009, 02:22:00 PM » |
|
You might be able to use a sine wave to add to the y position as the x/y move to where you're hopping to.
|
|
|
Logged
|
|
|
|
ஒழுக்கின்மை (Paul Eres)
|
|
« Reply #4 on: May 11, 2009, 02:25:37 PM » |
|
I tried correlating the vspeed with a sine wave, yes, but that didn't turn out that well (they just seemed to move up and down like a wave rather than hop). Perhaps the absolute value of a sine wave? cause right now all I get with it is like ~~~~~
|
|
|
Logged
|
|
|
|
Ivan
Owl Country
Level 10
alright, let's see what we can see
|
|
« Reply #5 on: May 11, 2009, 02:27:32 PM » |
|
your x would be an arbitrary value that you would advance in the direction vector and your y would be y. It would make the most sense to just use a normalized (0-1) value and multiply your distance by it when moving, so that you're always dealing with the same numbers.
You could also use an arc of the sine wave to do it, or just do a very simple gravity/velocity simulation and just apply a Y velocity and let gravity take care of the curve.
|
|
|
Logged
|
|
|
|
nihilocrat
|
|
« Reply #6 on: May 11, 2009, 04:40:08 PM » |
|
or just do a very simple gravity/velocity simulation and just apply a Y velocity and let gravity take care of the curve.
Many moons ago, I didn't really know that a quadratic equation ended up looking like this, but got the desired effect through doing it this way which is quite a bit simpler from a math standpoint.
|
|
|
Logged
|
|
|
|
pgil
Guest
|
|
« Reply #7 on: May 11, 2009, 04:41:25 PM » |
|
How about giving the character's position a z-component? 0 means the character is on the ground. Then give it a variable zspeed, and create some gravity (increasing zspeed until z >= 0).. Then when drawing the sprite, do something like: sprite_draw(x,y+z,.....) Of course, that's just the cosmetic part... As for making a hop to a specific location.. well I failed trigonometry... but if you're using Game Maker, it does have a couple of functions that could help you figure out the movement: something like var dist = point_distance(xfrom,yfrom,xto,yto) var dir = point_direction(xfrom,yfrom,xto,yto)
hspeed = .4 * lengthdir_x(dist,dir) vspeed = .4 * lengthdir_y(dist,dir)
What this does is give you the x and y speed that you need to hop to a particular position. Sorry if I'm totally incoherent...
|
|
« Last Edit: May 11, 2009, 04:54:31 PM by pgil »
|
Logged
|
|
|
|
ஒழுக்கின்மை (Paul Eres)
|
|
« Reply #8 on: May 11, 2009, 04:44:13 PM » |
|
That zspeed idea actually sounds the most common sense to me, and easiest to code, I'll try that first, thanks!
|
|
|
Logged
|
|
|
|
nihilocrat
|
|
« Reply #9 on: May 11, 2009, 04:52:26 PM » |
|
As for making a hop to a specific location.. well I failed trigonometry... but if you're using Game Maker, it does have a couple of functions that could help you figure out the movement: lengthdir_x(len,dir) Returns the horizontal x-component of the vector determined by the indicated length and direction. lengthdir_y(len,dir) Returns the vertical y-component of the vector determined by the indicated length and direction. I believe the incantation you want is this: x = sin(dir) * len y = cos(dir) * len For a 'normal' coordinate system where 10,10 is 10 pixels right, 10 pixels up. For a 'sinking' coordinate system, seen in many graphics libraries, that would mean 10 pixels right, 10 pixels down. You have to make this adjustment: x = cos(dir) * len y = sin(dir) * len This is the first bit of gaming-related math I ever learned, in order to move a triangle in an Asteroids-like game.
|
|
|
Logged
|
|
|
|
pgil
Guest
|
|
« Reply #10 on: May 11, 2009, 04:56:43 PM » |
|
Yeah.. I think that's pretty much what those two functions in Game Maker do. Just without all that scary math
|
|
|
Logged
|
|
|
|
Xion
|
|
« Reply #11 on: May 11, 2009, 05:00:08 PM » |
|
yar, I was gunna suggest the zspeed thing but you beat me to it. I've done this before and that was pretty much how I ended up going about it.
|
|
|
Logged
|
|
|
|
Sam
|
|
« Reply #12 on: May 11, 2009, 05:28:40 PM » |
|
I haven't written any code in a month! Hope it makes sense. //local variables: var x:number; var y:number; var targetX:number; var targetY:number; var hopYChange:number; var hopXChange:number; var hopCount:int; var hopStepNum:int; var hopping:boolean = false; const hopHeight:number = 200;
function hopTo(tarX:number, tarY:number, durationOfHop:int):void { if (!hopping) { targetX = tarX; targetY = tarY; hopStepNum = durationOfHop; //what net change in X and Y value needs to take place over this hop? hopXChange = targetX - x; hopYChange = targetY - y; hopping = true; hopCount = 0; } }
function update():void { if (hopping) { //check for having finished this hop if (hopCount >= hopStepNum) { //finish off this hop by making sure we're at the correct target position // hopefully we're there already, but good to be sure I guess. x = targetX; y = targetY; hopping = false; } else { //the horizontal part of the hop: // velX is constant throughout the hop, so could calculate it just once. var velX:number = hopXChange / hopStepNum; x = x + velX;
//the vertical part of the hop: // adding velY to y each frame should form a nice arc var velY:Number = (hopHeight - (hopHeight*2 * hopCount/hopStepNum) - hopYChange) / hopStepNum; y = y - velY;
//increment the hop's frame counter. hopCount = hopCount + 1; } }
Something like this would work (vaguely written in Actionscript.) Assuming this is in a class definition for something that's going to do some hopping. The update() function would be called every frame, and the hopTo() called whenever you want to start a new hopping action. The idea is it can cope with hopping to any position you desire, and take however many 'frames' (where one frame is one call to update()) you like. I'm too sleepy to explain the calculation of velY. But it works, which is nice. I tidied up the code a little to make it actually compile. Clicky! Click anywhere and the circle will 'hop' over to it, taking 30 frames (1 second) to do it, and jumping 'up' 150 pixels. I see I missed a million replies whilst typing this, so who knows if this fits in the thread any more!
|
|
|
Logged
|
|
|
|
Sam
|
|
« Reply #13 on: May 12, 2009, 01:47:38 AM » |
|
For fun, I implemented the z-movement method described above. I think it makes more sense than the way I used, and lets you do things like draw a shadow very easily. I'm also more confident that it's actually doing exactly as I wanted it to. Clicky!When a hop begins: velX = (targetX - x) / numberOfFramesToTake; velY = (targetY - y) / numberOfFramesToTake; velZ = -10;
Then each frame during the hop: x += velX; y += velY; //apply just enough "gravity" for z to return to 0 at the end of the hop velZ += (20 / (numberOfFramesToTake + 1)); z += velZ;
when I detect that the jump is over (when a frame counter is equal to or greater than numberOfFramesToTake) I force the x,y,z values to be as they should be. That corrects for any floating point errors, or my code being slightly off x = targetX; y = targetY; z = 0; Draw the shadow at ..and the object itself at Very neat! edit: Thinking about it, it would be better to apply the same gravity force for all hops (no matter how many frames they take) and instead vary the initial value of velZ so that the hop lasts the correct number of frames. That way if you have many things hopping with different hop durations, they'll seem to be being influenced by the same physics.
|
|
« Last Edit: May 12, 2009, 01:54:06 AM by Salt »
|
Logged
|
|
|
|
ChevyRay
Guest
|
|
« Reply #14 on: May 12, 2009, 03:44:07 AM » |
|
This is the formula you want, if you want a nice smooth sin curve: z = sin((dist_hopped / hop_length) * pi) * hop_height; Where dist_hopped is the distance from your start point and current position, and hop_length is the distance from your start point to the landing point, and hop_height is the peak z height of the current hop. I made an example in Game Maker, since I'm assuming this is for SD? Here it is: http://properundead.com/examples/hopexample.zip. This was my approach, and it worked just fine (though you'd have to tweak it to get the behaviour how you wanted it. eg: prevent object from hopping too many times in one general direction, etc.) creation event: z = 0; // height from ground move_spd = 2; // movement speed
hopping = false; // if the player is hopping hop_min = 20; // minimum distance that can be hopped hop_max = 80; // maximum distance that can be hopped hop_x = x; // x position of the landing point hop_y = y; // y position of the landing point hop_height = 0; // the peak z value of the current hop hop_timer = 60; // how long to wait between hops (frames) hop_at = 0; // how far we are in the hop hop_dist = 0; // how long the total hop is hop_dir = 0; // the direction from the start point to the landing point
alarm[0] = hop_timer; In the alarm[0] event... // CHOOSE A DIRECTION AND DISTANCE TO HOP
hop_dist = hop_min + random(hop_max); hop_dir = random(360);
// DETERMINE THE LANDING POINT
hop_x = x + lengthdir_x(hop_dist, hop_dir); hop_y = y + lengthdir_y(hop_dist, hop_dir);
// START HOPPING
hop_height = hop_dist; hopping = true; And in the step event... if (hopping){
// DETERMINE THE CURRENT HEIGHT
hop_at += move_spd; z = sin((hop_at / hop_dist) * pi) * hop_height;
// MOVE TOWARDS THE LANDING POINT
x += lengthdir_x(move_spd, hop_dir); y += lengthdir_y(move_spd, hop_dir);
// CHECK IF WE'VE LANDED AND RESET THE TIMER IF SO
if (hop_at >= hop_dist){ hopping = false; x = hop_x; y = hop_y; z = 0; alarm[0] = hop_timer; }
} And this is how you would draw it: var X, Y, Z; X = round(x); Y = round(Y); Z = round(Z);
draw_sprite(spr_shadow, -1, X, Y); draw_sprite(spr_character, -1, X, Y - Z); Here are some other tricks you can do with it: When (hop_at < hop_dist/2) it means you are moving upwards. When (hop_at > hop_dist/2) it means you are moving downwards. You could also set a hop_ratio, which would be a ratio of the hopping height to the distance to be hopped. So if it was set to 2, you would jump twice as high as the hop length. If it was .5 you'd jump half the hop length. A low ratio would end up in shallow, low jumps, while a high ratio would end up in steep arcing jumps. You'd apply it like this, in the last part of the alarm[0] event: // START HOPPING
hop_height = hop_dist * hop_ratio; hopping = true;
|
|
« Last Edit: May 12, 2009, 03:59:10 AM by ChevyRay »
|
Logged
|
|
|
|
ஒழுக்கின்மை (Paul Eres)
|
|
« Reply #15 on: May 12, 2009, 04:38:39 AM » |
|
yep, for SD. will look at the example, thanks for it and for the other code people have written.
|
|
|
Logged
|
|
|
|
ChevyRay
Guest
|
|
« Reply #16 on: May 12, 2009, 04:44:01 AM » |
|
Oh, and I forgot to mention. If you want to make every hop take the same amount of time, set the movement speed to a division of the hop_dist... so when you jump, setting move_spd to hop_dist / 60 will make every jump take 60 frames. Etc.
Also, you can use the sine ratio [sin((hop_at / hop_dist) * pi)] (which is equal to 0-1, 1 at the peak of your jump and 0 at either end) to scale your object as well. So you could set image_xscale and image_yscale to 1 + s / 2 (where s is the ratio), so your objects appear bigger the higher they are up.
I don't know if that'll look good or not, though.
|
|
|
Logged
|
|
|
|
ChevyRay
Guest
|
|
« Reply #17 on: May 12, 2009, 04:52:24 AM » |
|
Oh damn, I just realized if you're using a static speed, you don't have to calculate the lengthdir_x and lengthdir_y movement every step. Just calculate those values when the jump initiates in alarm[0], and save them into variables (like hop_h and hop_v), then just move by those values. Otherwise you're making extra calculations, because it's gonna be the same every time and I know you're trying to keep the FPS up with SD.
|
|
|
Logged
|
|
|
|
salade
|
|
« Reply #18 on: May 12, 2009, 10:53:21 AM » |
|
sin curve? wouldn't it be much more realistic(and easier) to set up a quadratic?
ah, it has already been suggested.
the only question now: which one is faster to calculate?
|
|
|
Logged
|
|
|
|
ஒழுக்கின்மை (Paul Eres)
|
|
« Reply #19 on: May 12, 2009, 11:20:04 AM » |
|
Nobody really explained what it means to "set up a quadratic" though.
|
|
|
Logged
|
|
|
|
|