Results 1 to 15 of 15

Thread: Keeping Rolling when you're Jumping!

  1. #1
    Lesser Wizard
    Join Date
    Mar 2008
    Posts
    124

    Default Keeping Rolling when you're Jumping!

    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:




    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?

  2. #2
    Administrator Seth's Avatar
    Join Date
    Jul 2002
    Location
    Japan
    Posts
    5,340

    Default

    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:

    Code:
    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.
    Seth A. Robinson
    Robinson Technologies

  3. #3
    Lesser Wizard
    Join Date
    Mar 2008
    Posts
    124

    Default

    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!

  4. #4
    Administrator Seth's Avatar
    Join Date
    Jul 2002
    Location
    Japan
    Posts
    5,340

    Post

    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:

    Code:
    this:GetBrainManager():SendToBrainBase("control_filterx=0");
    And when you want the player to move again:

    Code:
    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:

    Code:
    Schedule(100, this:GetID(), "OnDoJumpStep();");
    And in your player.lua, have:

    Code:
    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..
    Seth A. Robinson
    Robinson Technologies

  5. #5
    Lesser Wizard
    Join Date
    Mar 2008
    Posts
    124

    Default

    OK So here's the code I have come up with so far:

    Code:
    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?

  6. #6
    Administrator Seth's Avatar
    Join Date
    Jul 2002
    Location
    Japan
    Posts
    5,340

    Default

    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:

    Code:
    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_DISABLED);
    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)..
    Seth A. Robinson
    Robinson Technologies

  7. #7
    Lesser Wizard
    Join Date
    Mar 2008
    Posts
    124

    Default

    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!

  8. #8
    Lesser Wizard
    Join Date
    Mar 2008
    Posts
    124

    Default

    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!

    Code:
    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_DISABLED);
    	g_jumping = false;
    end

  9. #9
    Administrator Seth's Avatar
    Join Date
    Jul 2002
    Location
    Japan
    Posts
    5,340

    Default

    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.
    Seth A. Robinson
    Robinson Technologies

  10. #10
    Lesser Wizard
    Join Date
    Mar 2008
    Posts
    124

    Default

    Hi Seth, thanks for your response.

    I start the jump in the CheckForJump() function:

    Code:
    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.

  11. #11
    Administrator Seth's Avatar
    Join Date
    Jul 2002
    Location
    Japan
    Posts
    5,340

    Default

    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.
    Seth A. Robinson
    Robinson Technologies

  12. #12
    Lesser Wizard
    Join Date
    Mar 2008
    Posts
    124

    Default

    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?

  13. #13
    Lesser Wizard
    Join Date
    Mar 2008
    Posts
    124

    Default Nearly There!!!! 1 small final issue...

    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?

  14. #14
    Administrator Seth's Avatar
    Join Date
    Jul 2002
    Location
    Japan
    Posts
    5,340

    Default

    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.

    Add:

    Code:
    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:

    Code:
    		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); )
    Seth A. Robinson
    Robinson Technologies

  15. #15
    Lesser Wizard
    Join Date
    Mar 2008
    Posts
    124

    Default

    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

Bookmarks

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •