PDA

View Full Version : Keeping Rolling when you're Jumping!



Pleng
04-28-2009, 09:31 AM
Hi Guys. I'm back, and after a long period of being far too busy to play with Novashell, I finally have some more time on my hands and want to get busy creating a new game.

The game will be modernized dizzy-style game and I've come into some problems with handling the slightly bizzare way in which the classic Dizzy games handle jumping.

If you've not heard of Dizzy, or aren't familiar with his jumping style then let me fill you in. When Dizzy jumps he has a fixed distance he can jump. If he hits a platform whilst jumping, he will continue to roll underneath the platform until he has completed his maximum jump distance (or until the platform ends, whichever comes first).

Consider the example below:

http://www.strappymusic.co.uk/jumpexample.jpg


In this example dizzy can jump a distance of nine blocks.

White dizzy will have a normal, un-interrupted jump of nine squares, and end up in front of the signpost.

Blue Dizzy will hit the block above his head, but rather than bouncing straight back down as in a normal platform game, he will continute to roll underneath the blocks above his head until he has completed his jump cycle of 9 blocks, and he will land just to the left of the door.

Similarly, Red Dizzy will hit the blocks above his head and he will continue to roll underneath them until he has completed his jump cycle, thus granting him access to the next platform.

Green Dizzy will hit the blocks above his head and will start to roll underneath them, but his jump will terminate when he reaches the end of the blocks, as there is nothing more for him to roll underneath.

Is there any way to implement a similar jumping system using Novashell?

Seth
04-30-2009, 04:03 AM
Because the jumping is 100% handled in script, it IS possible to implement the jumping that you need.

But currently, in the examples, I use the physics engine to handle the jump, like this in the LowRes example for instance:


this:StopY(); //Kill any Y interia so each jump is exactly the same height, feels more consistent
this:AddForce(Vector2(0, m_JumpPower));

What you need would be .. difficult.. to do with real physics, so instead, I would suggest handling the jumping without physics, but more manually by moving him using SetPos() over a period of time.

So, something like this:

Disable player control
Setup a timer that triggers every 100 MS (ten times a second)
When the timer is trigger, move him 5 pixels up, 5 pixels to the right, until max up and max right is reached. (for some dizzys, max right might be block_size_in_pixels*9 or whatever)

If a collision is detected, abort/handle the jump accordingly, and don't forget to turn player control on at the end.

This time of method means you don't have play with physics/forces/etc, which is pretty hard to model complicated jumps with.

Pleng
05-01-2009, 04:38 PM
Hi Seth

Thanks for that.

I am fine with the syntax for actually moving the entity, but am not sure how to disable/enable player control, or set up a timer. Could you point me in the right direction?

thanks!

Seth
05-02-2009, 10:17 AM
I'm not sure which player script you started with, but most work basically work the same - using the "StandardBase" brain instead of doing things totally manually. (It applies movement based on the players direction for you, and is capable of working with pathfinding.. at least for top down movement games)

To disable the player's normal movement that the "StandardBase" brain does for you, you can directly communicate with its brain and turn off player control like this:


this:GetBrainManager():SendToBrainBase("control_filterx=0");

And when you want the player to move again:


this:GetBrainManager():SendToBrainBase("control_filterx=1");

Now, to apply the actual movement, you should use Schedule(), either to move the player directly, or to call a function that does the move. (probably a better idea, so you can do a lot of code easy)

So, to schedule the movement for 100 ms later, say, right after they hit the jump button:


Schedule(100, this:GetID(), "OnDoJumpStep();");

And in your player.lua, have:


function OnDoJumpStep()

local vPos = this:GetPos();
vPos.x = vPos.x + 20; //20 is how many pixels we move to the right

//if we wanted, we could also examine this new position using GetCollisionByRay() before moving to see what is here, for instance, to stop a jump in progress.
this:SetPos(vPos);

//Now, if our jump isn't finish, keep scheduling this to run..

Schedule(100, this:GetID(), "OnDoJumpStep();");
end

Note, this is just off the top of my head, there are probably a lot of ways to accomplish this, if I actually tried to do this I might end up with a different way depending on how it worked out.. :sweatdrop:

Pleng
05-02-2009, 12:02 PM
OK So here's the code I have come up with so far:



function OnDoJumpStep()

local vPos = this:GetPos();
local jumpdist = this:Data():GetNum("jumpdist");
local jumpsofar = this:Data():GetNum("jumpsofar");


if (jumpsofar < jumpdist) then


if (m_bLeft) then
vPos.x = vPos.x - 5; //is how many pixels we move to the right
else
if (m_bRight) then
vPos.x = vPos.x + 5; //is how many pixels we move to the right
end
end

vPos.y = vPos.y - 30;

//if we wanted, we could also examine this new position using GetCollisionByRay() before moving to see what is here, for instance, to stop a jump in progress.
this:SetPos(vPos);
this:Data():SetNum("jumpsofar", jumpsofar + 5);

//Now, if our jump isn't finish, keep scheduling this to run..

Schedule(100, this:GetID(), "OnDoJumpStep()");

//DEBUG
LogMsg("jsf " .. jumpsofar .. " jd " .. jumpdist);
//END DEBUG

else

jumpsofar = 0;
this:Data():SetNum("jumpsofar", 0);

end


I don't have any code for exiting when he hits the ground yet so he sort of bounces up and down. I don't think that will be too much of an issue to fix, but the big problem I'm having is that the physics engine is pushing Dizzy back down between calls to the OnDoJumpStep() routine, making him look like a bizzare drunkard!

Is there any way to disable the physics on Dizzy when he is jumping?

Seth
05-02-2009, 12:15 PM
Is there any way to disable the physics on Dizzy when he is jumping?

Hmm... Actually no... if we did a this:LoadCollisionInfo("") to remove the physics, I think the StandardBrain would freak and give errors about it.

Something that would accomplish about the same thing would be to kill the gravity.

So, at the start of the jump, do:


this:SetGravityOverride(0);
//also do a this:Stop() if you want to kill all current movement before the jump happens

//and after the jump is over, turn it back on:
this:SetGravityOverride(C_GRAVITY_OVERRIDE_DISABLE D);

If that is working, but he's bumping off of walls and things during flight, you could also kill collision if you need to with this:SetCollisionCategories(C_CATEGORIES_NONE)..

Pleng
05-02-2009, 01:54 PM
Thanks again for all your help Seth. I've got something working now. I'll need to play with the numbers to get it looking natural but at least I know what direction I'm going now! :D

Pleng
05-05-2009, 07:12 AM
Seth

I have come up with a routine based on your advice but I currently have 2 problems.

Firstly, even though the control filters are in the CheckForJump() routine, they don't seem to kick in until Dizzy is half way through his ascent. This means that the player can still change direction in the first few stages of his jump.

Secondly, occasionally (and for no apparent reason) Dizzy will jump twice as far as normal, and at a lower gradient. This is really confusing me!

Do you have any ideas as to why these phenomena are occurring?

cheers!



function ProcessMovement(step)

//check for movement
local facing = C_FACING_NONE;

if (m_bLeft) then facing = C_FACING_LEFT; end;
if (m_bRight) then facing = C_FACING_RIGHT; end;

if (this:GetOnGround() and m_jumpTimer < GetApp:GetGameTick() ) then
this:GetBrainManager():Remove("ForceVisual"); //all "ForceVisual" brains are removed
//this:SetVisualStateOverride(C_VISUAL_STATE_NONE); //same as above but faster

//RESTORE X and Y Filters if they have been disabled.
//And bring back gravity
this:GetBrainManager():SendToBrainBase("control_filterx=1");
this:GetBrainManager():SendToBrainBase("control_filterx=1");
g_jumpstate=0;
else
this:GetBrainManager():Add("ForceVisual", "force_set=" .. C_VISUAL_STATE_JUMP );
//this:SetVisualStateOverride(C_VISUAL_STATE_JUMP); //same as above but faster
end

if (facing != C_FACING_NONE) then

//they are pressing towards a direction
this:SetFacingTarget(facing); //turn the way we want
this:GetBrainManager():SetStateByName("Walk");
m_lastDirection = facing; //store this for later, like when we get off the ladder
else
this:GetBrainManager():SetStateByName("Idle");
end

end

function OnDoJumpStep()

if (g_jumping) then

local vPos = this:GetPos();
local jumpdist = this:Data():GetNum("jumpdist");
local jumpsofar = this:Data():GetNum("jumpsofar");
local xdist = 3;
local ydist = 3;
local scheduletime = 1;
local jumpstate = 0;
local movex;
local movey;

//SO, have we reached maximum distance?


if (jumpsofar < jumpdist) then
//no we haven't, so we're in ascent!
movey = 0 - ydist;

else
//we're in descent, bring him down!
movey = 0 + ydist;

//as we're in descent, it's safe to assume that
//we can now check for the ground
if (this:GetOnGround()) then
//we can end the jump
jumpstate = 1;
end


end

//now, are we going left, right, or up?

if (g_playerdirection == "left") then
movex = 0 - xdist;
elseif (g_playerdirection == "right") then
movex = 0 + xdist;
else
//just jump straight up
movex = 0;
end

if (jumpstate==1) then

StopJump();

else

this:SetPos(vPos + Vector2(movex, movey));
this:Data():SetNum("jumpsofar", jumpsofar + ydist);
Schedule(scheduletime, this:GetID(), "OnDoJumpStep()");

end

end

end

function StopJump()
jumpsofar = 0;
this:Data():SetNum("jumpsofar", 0);
this:StopX();
this:StopY();
this:SetGravityOverride(C_GRAVITY_OVERRIDE_DISABLE D);
g_jumping = false;
end

Seth
05-09-2009, 08:01 AM
Hmm, looking through your code snippet I didn't see where you start the Jump - (Like a StartJump(), like you have your StopJump() ) if you want each jump to move exactly the same amount, where you turn off gravity, you should also do a this:Stop(); (is that what it is?) to make sure no residual physics is around.

You may want to add LogMsg() statements in your jump cycles so you can see if they are being run too many times, or two jumps are happening in a row or such.

Pleng
05-09-2009, 10:38 AM
Hi Seth, thanks for your response.

I start the jump in the CheckForJump() function:



function CheckForJump()

//check for jumping

if (m_bUp and m_jumpTimer < GetApp:GetGameTick()) then


if (m_bLeft) then
g_playerdirection = "left";
this:GetBrainManager():Add("ForceVisual", "force_anim=jump_left");
elseif (m_bRight) then
g_playerdirection = "right";
this:GetBrainManager():Add("ForceVisual", "force_anim=jump_right");
else
g_playerdirection = "up";
this:GetBrainManager():Add("ForceVisual", "force_anim=jumpup");
end

this:GetBrainManager():SendToBrainBase("control_filterx=0");
this:GetBrainManager():SendToBrainBase("control_filtery=0");

//lets stop the player from dizzy from moving left or right

if (g_jumping == false) then

//DEBUG
LogMsg("Jump Started");
//DEBUG

g_jumping = true;


//ResetKeys();
this:Stop();
this:SetGravityOverride(0);

g_jumpstate = 0; //Player has not left the ground
g_jumpheadstate = 0; //Player has not hit his head on anything
//ResetKeys();
Schedule(1, this:GetID(), "OnDoJumpStep();");

//this:PlaySound(m_jumpSound);
end


end
end


I was using this:StopX(); this:StopY(); but have changed to this:Stop(); and both issues are still occuring.

I have also added a LogMessage to the StopJump() function. I checked the log immediately after Dizzy did one of his double-length jumps and there was nothing suspicious. All 'Jump Started' and 'JUMP STOPPED' messages appeared in pairs exactly how they should have done.

Seth
05-12-2009, 12:35 AM
Hmm.. well, I don't see anything that jumps out of me. Maybe add more LogMsg()'s to your OnDoJumpStep() to verify the same amount of steps are happening each time, as that's the only place that could move the player it looks like.

You could do something like: LogMsg("Doing Step, current pos is", this:GetPos()); to verify each step is moving by the correct amount of pixels.

Pleng
05-12-2009, 09:39 AM
Hmm

So the amount of jump steps appears to be consistent (unless, of course, Dizzy has hit his head on something or broken the jump by landing on a platform, which is to be expected).

Having experimented a little more it seems that the distance of the jump is being affected by the amount of time the directional button is being pressed for.

If the cursor key is lifted mid jump, for example, dizzy will jump half the distance in the same amount of jump steps. I can only assume that for some reason, commands such as this:Stop(); this:SetGravityOverride(0); aren't kicking in until mid-way through the jump. This would be consistent with the other problem I mentioned before:



Even though the control filters are in the CheckForJump() routine, they don't seem to kick in until Dizzy is half way through his ascent. This means that the player can still change direction in the first few stages of his jump.


Do you think they are linked? And can you think of any reasons that this could be happening?

Pleng
05-12-2009, 07:55 PM
Further to above post I have resolved the issue by placing this:Stop() inside the OnDoJumpStep() function, as well as to original CheckForJump(). Now all jumps are equal, so I assume that something was coming from outside the function and somehow affecting the positioning.

The next issue I had was that even though control filters x and y were both in place, it was for some reason possible to change the direction of the jump, by pressing [jump + direction] whilst mid-jump. Pressing [jump + left], for instance would change a right facing jump or a standing still jump into a left facing jump. The total distance of the jump was not affected.

To resolve this issue I added a ResetKeys to OnDoJumpStep. Now the player can not alter his direction mid-jump regardless of what he presses.

This, however, leaves me with one final problem. That is that after the jump, Dizzy will just stand still. Is the a function I can call from within my StopJump function that will re-check the keys so that Dizzy can continue in whichever direction the player is holding down at the end of the jump?

Seth
05-12-2009, 11:36 PM
Ah ok, I see what you mean.. I forgot that there are separate controls for when an entity is "in the air". Oops! :wheelchair: See here (file:///O:/projects/clanlibstuff/novashell/scripts/api/files/BrainTopBase-cpp.html).

Add:


this:GetBrainManager():SendToBrainBase("air_control_filterx=0; ir_control_filtery=0");

when the jump starts, so the player movement won't actually power the player.

Another idea would be to do a this:SetDesiredSpeed(0), which would sort of do the same thing.


The next issue I had was that even though control filters x and y were both in place, it was for some reason possible to change the direction of the jump

This is all being done in that script, in the process ProcessMovement function, it checks the last keys that were hit and sets the direction with a this:SetFacingTarget() command.

Just set it up to not call ProcessMovement while the jump is taking place.

Your update function probably has this in it now:


if (this:GetBrainManager():InState("Attack") == false ) then
ProcessMovement(step);

Change to:


if (this:GetBrainManager():InState("Attack") == false and g_jumping == false) then
ProcessMovement(step);

Or something like that. When the jump is over and the ProcessMovement(step) is run, the last hit direction will be set.

But.. I notice that it also checks the jumpstate and sets the visual override so it looks like the player is jumping in ProcessMovement() , so you might need to move that part into your jump start/end or something.

( the this:SetVisualStateOverride(C_VISUAL_STATE_JUMP); )

Pleng
05-13-2009, 02:52 PM
Phew it took a while but we got there in the end!

Thanks once again Seth. Due to your constant support I now have an Engine worthy of creating a successor to the Dizzy games of the 80s. A happy boy I am indeed :D