playMaker

Author Topic: Let's go over the character controller jumping issue again!  (Read 16572 times)

kitheif

  • Playmaker Newbie
  • *
  • Posts: 5
    • Tumblr
Let's go over the character controller jumping issue again!
« on: April 09, 2014, 06:02:19 PM »
Hi there,
I've read plenty of threads on the issue of using the default move/simple move character controller with jumping, and I think it'd be nice to get a good final solution to this problem.
Currently I've read about 2 solutions, both of which feel janky to me.  :P

1: Import the character controller package's CharacterMotor.js script and connect it to your player. Edit the script to remove the double movement caused when having both this and the character controller's movement. Use the set property action to trigger the property InputJump within the CharacterMotor.
This is currently the best option, but the double movement issue sucks and there's plenty of bugs to have fun with..

2:
Manually change the game objects local y position, which leads to checking the space above you to not get your player glitched and stuck.

Are there any options that are less finicky that the two above? Since the character controller is tied to the move/simple move action I believe it's important to make jumping as simple as everything else within Playmaker.

« Last Edit: April 09, 2014, 06:26:32 PM by kitheif »

Breadman

  • Full Member
  • ***
  • Posts: 185
  • Derp
Re: Let's go over the character controller jumping issue again!
« Reply #1 on: April 09, 2014, 07:35:37 PM »
Hey KiTheif, welcome to the forums! I've seen you around on Tumblr :P

Sometimes I feel like my game is customized to the point where my solutions can't really benefit anyone else, unless their game is super similar to mine. But maybe this one area where I can share what I'm doing to counter this issue. I spent quite a deal of time trying to find a solution that wasn't janky like you said:

1) Character has a simple FSM on it with two actions: "get axis vector" and "smooth look at direction".


2) Character has a standard "character controller" component

Character also has a modified version of the "FPSWalker Enhanced" script - a really versatile script. I've modified mine to prevent some double movement and rotation errors, but keep the jumping and sliding aspects. As a Playmaker user, editing this script and getting the behavior I wanted out of it took weeks - I'd be glad to share it with you to save you the trouble!


This system works well with a moveable camera, but will work just fine if your camera is stationary as well. Keep in mind you may need to change certain variables based on your game, but the overall setup should work!

Let me know if you have any questions, getting a good character controller that's right for your game can be quite the battle!

kitheif

  • Playmaker Newbie
  • *
  • Posts: 5
    • Tumblr
Re: Let's go over the character controller jumping issue again!
« Reply #2 on: April 10, 2014, 12:54:05 AM »
Oh hey thanks man (I've been following your king game on tumblr ;D)
I actually found solution one because you talked about it somewhere on the forums,
and sure if you're okay with sharing your edit then that'd be really helpful!

Breadman

  • Full Member
  • ***
  • Posts: 185
  • Derp
Re: Let's go over the character controller jumping issue again!
« Reply #3 on: April 10, 2014, 01:01:38 AM »
Yeah, I had tried charactermotor.js at one point, but eventually scrapped it. It wasn't quite working for what I needed. (lots of people have great success with it, so it's probably me that's the problem haha)

Here's the script, keep in mind you may need to change variable names, etc. I can't promise plug-and-play, but hopefully it gets you on the right track without too much frustration.

If you hit any snags, let me know!

Code: [Select]
import HutongGames.PlayMaker;

var moveSpeed = 16.0;

 
// If true, diagonal speed (when strafing + moving forward or back) can't exceed normal move speed; otherwise it's about 1.4 times faster
var limitDiagonalSpeed = true;
 
// If checked, the run key toggles between running and walking. Otherwise player runs if the key is held down and walks otherwise
// There must be a button set up in the Input Manager called "Run"
var toggleRun = false;
 
var jumpSpeed = 8.0;
var gravity = 20.0;


 
// Units that player can fall before a falling damage function is run. To disable, type "infinity" in the inspector
var fallingDamageThreshold = 10.0;
 
// If the player ends up on a slope which is at least the Slope Limit as set on the character controller, then he will slide down
var slideWhenOverSlopeLimit = true;
 
// If checked and the player is on an object tagged "Slide", he will slide down it regardless of the slope limit
var slideOnTaggedObjects = true;
 
var slideSpeed = 12.0;
 
// If checked, then the player can change direction while in the air
var airControl = true;
 
// Small amounts of this results in bumping when walking down slopes, but large amounts results in falling too fast
var antiBumpFactor = .75;
 
// Player must be grounded for at least this many physics frames before being able to jump again; set to 0 to allow bunny hopping
var antiBunnyHopFactor = 1;
 
private var moveDirection = Vector3.zero;
private var grounded = false;
private var controller : CharacterController;
private var myTransform : Transform;
private var hit : RaycastHit;
private var fallStartLevel : float;
private var falling = false;
private var slideLimit : float;
private var rayDistance : float;
private var contactPoint : Vector3;
private var playerControl = false;
private var jumpTimer : int;
 
function Start () {
controller = GetComponent(CharacterController);
myTransform = transform;
rayDistance = controller.height * .5 + controller.radius;
slideLimit = controller.slopeLimit - .1;
jumpTimer = antiBunnyHopFactor;
oldPos = transform.position;


}

 
function FixedUpdate() {
var inputX = Input.GetAxis("Horizontal");
var inputY = Input.GetAxis("Vertical");

var pmove = FsmVariables.GlobalVariables.FindFsmString("Player Movement");

var cameraTransform = Camera.main.transform;
var forward = cameraTransform.TransformDirection(Vector3.forward);
forward.y = 0;
forward = forward.normalized;

// Get the part of camera forward that is on the XZ plane as a normalized vector
var camForward = Camera.main.transform.forward;
camForward.y = 0.0;
camForward.Normalize();

// Get the part of camera right that is on the XZ plane as a normalized vector
var camRight = Camera.main.transform.right;
camRight.y = 0.0;
camRight.Normalize();

// Save 'y' direction for jumping but reset X and Z
moveDirection.x = 0;
moveDirection.z = 0;



// Right vector relative to the camera
// Always orthogonal to the forward vector
var right = Vector3(forward.z, 0, forward.x);

var v = Input.GetAxisRaw("Vertical");
var h = Input.GetAxisRaw("Horizontal");

var targetDirection = h * right + v * forward;
var horizontaldir = h * right;
var verticaldir = v * forward;

   




// If both horizontal and vertical are used simultaneously, limit speed (if allowed), so the total doesn't exceed normal move speed
var inputModifyFactor = (inputX != 0.0 && inputY != 0.0 && limitDiagonalSpeed)? .7071 : 1.0;
var inputfix = (Camera.main.transform.right.x != 0.0 && Camera.main.transform.forward.z != 0.0 && limitDiagonalSpeed)? .7071 : 1.0;
 
if (grounded) {
var sliding = false;
// See if surface immediately below should be slid down. We use this normally rather than a ControllerColliderHit point,
// because that interferes with step climbing amongst other annoyances
if (Physics.Raycast(myTransform.position, -Vector3.up, hit, rayDistance)) {
if (Vector3.Angle(hit.normal, Vector3.up) > slideLimit)
sliding = true;
}
// However, just raycasting straight down from the center can fail when on steep slopes
// So if the above raycast didn't catch anything, raycast down from the stored ControllerColliderHit point instead
else {
Physics.Raycast(contactPoint + Vector3.up, -Vector3.up, hit);
if (Vector3.Angle(hit.normal, Vector3.up) > slideLimit)
sliding = true;
}
 
// If we were falling, and we fell a vertical distance greater than the threshold, run a falling damage routine
if (falling) {
falling = false;
if (myTransform.position.y < fallStartLevel - fallingDamageThreshold)
FallingDamageAlert (fallStartLevel - myTransform.position.y);
}
 
 
// If sliding (and it's allowed), or if we're on an object tagged "Slide", get a vector pointing down the slope we're on
if ( (sliding && slideWhenOverSlopeLimit) || (slideOnTaggedObjects && hit.collider.tag == "Slide") ) {
var hitNormal = hit.normal;
moveDirection = Vector3(hitNormal.x, -hitNormal.y, hitNormal.z);
Vector3.OrthoNormalize (hitNormal, moveDirection);
moveDirection *= slideSpeed;
playerControl = false;
}
// Otherwise recalculate moveDirection directly from axes, adding a bit of -y to avoid bumping down inclines
else {
moveDirection += inputY * moveSpeed * camForward;
moveDirection += inputX * moveSpeed * camRight;
playerControl = true;
moveDirection.y = antiBumpFactor * -1;
playerControl = true;


}
 
// Jump! But only if the jump button has been released and player has been grounded for a given number of frames
if (!Input.GetButton("Jump"))
jumpTimer++;
else if (jumpTimer >= antiBunnyHopFactor) {
playerControl = true;
moveDirection.y = jumpSpeed;
jumpTimer = 0;
}
}

else {
// If we stepped over a cliff or something, set the height at which we started falling
if (!falling) {
falling = true;
fallStartLevel = myTransform.position.y;
}
 
// If air control is allowed, check movement but don't touch the y component
if (airControl && playerControl) {
moveDirection += inputY * moveSpeed * camForward;
moveDirection += inputX * moveSpeed * camRight;
playerControl = true;
}
}



// Apply gravity
moveDirection.y -= gravity * Time.deltaTime;
 
// Move the controller, and set grounded true or false depending on whether we're standing on something
grounded = (controller.Move(moveDirection * Time.deltaTime) & CollisionFlags.Below) != 0;
}
 
 
// Store point that we're in contact with for use in FixedUpdate if needed
function OnControllerColliderHit (hit : ControllerColliderHit) {
contactPoint = hit.point;
}
 
// If falling damage occured, this is the place to do something about it. You can make the player
// have hitpoints and remove some of them based on the distance fallen, add sound effects, etc.
function FallingDamageAlert (fallDistance : float) {
Debug.Log ("Ouch! Fell " + fallDistance + " units!");
}
 
@script RequireComponent(CharacterController)

kitheif

  • Playmaker Newbie
  • *
  • Posts: 5
    • Tumblr
Re: Let's go over the character controller jumping issue again!
« Reply #4 on: April 10, 2014, 01:04:31 AM »
Thanks man, I'll plug it into my little fps setup and have a go at it.
« Last Edit: April 10, 2014, 04:40:03 AM by kitheif »

zorranco

  • Junior Playmaker
  • **
  • Posts: 50
Re: Let's go over the character controller jumping issue again!
« Reply #5 on: August 10, 2014, 02:22:58 PM »
¿Is there a way to implement it via only playmaker, not using charactermotor or FPS walker?

I perform tht jump through itween move add and adding a certain amount to Y, but character controller is unresponsive mid air.
« Last Edit: August 10, 2014, 02:58:10 PM by zorranco »

Red

  • Hero Member
  • *****
  • Posts: 563
Re: Let's go over the character controller jumping issue again!
« Reply #6 on: August 10, 2014, 03:05:05 PM »
Are there any options that are less finicky that the two above? Since the character controller is tied to the move/simple move action I believe it's important to make jumping as simple as everything else within Playmaker.

I'm not sure if this is what you'd be looking for but for the system that I've been working on recently I've had to re-roll my own physics system in a sense (mainly because when jumping I felt that the physics was a little too floaty and didn't give me the level of responsiveness I really wanted.)

I'm not sure how easy this would be to apply to your own system but what I did was as follows. That said, keep in mind that this is for a side-scroller so I only needed to input one axis for the horizontal movement and limited vertical movement (such as when the character is on a ladder.)

First off, I scrapped the character controller entirely and used only a rigidbody. Not set to kinematic or anything but I froze the rotations in all the axes other than the one that would let me rotate the character.

Secondly, I took the inputs for both vertical and horizontal and fed those into the appropriate variables that I'd use to control the character. Right as things are now since I have both the horizontal and vertical movement controlled in separate FSMs I stashed them in two floats. In the "input monitor" system I have it always getting those two with the "Get Axis" action and feeding them to the appropriate other FSMs with the "Set FSM Float" actions set to every frame (since the "Input monitor" system is always in the idle state... because, well, it's always getting that data from the player inputs.)

In the horizontal movement I've got a basic float compare taking that horizontal axis information being fed to it from the "Input Monitor" system and always testing... If it's in the positive, I tell it to move the character to the right... if negative, to the left. In there I use the "set velocity" to the object. That gives me the movement.

To get to the jumping, I have a basic calculations for gravity... Since it has a rigidbody and I'm making my own gravity solution I tell the rigidbody not to use it's own gravity. And in each active state in the vertical where I want it to have gravity affect it I have a three actions (in order...)

Get Velocity. Getting the vertical (Y) and storing it in a float variable.

Float Operator. Taking the vertical velocity variable and adding the gravity value. The gravity value is a negative float value... And this does require a little tinkering to be sure you have the value you want to get the responsiveness you want... I do that and then put it in the same vertical velocity value (I don't know if this is the best way to do it though as this system that I'm working on is still relatively new-ish and I'm learning a fair amount as I go.)

Set Velocity. Simply take the vertical velocity that was extracted and run through the operator and re-applying it to the character.

This requires a collider, of course... because when you remove the character controller component it takes the controller itself off.

And to make it jump... When I enter into the "Jump" state I simply put in a new value for the vertical velocity and use "Set Velocity" to do it... And yes, I also still use the three gravity functions listed previously... You could have it exit that state ASAP once you've added the velocity to make it jump and of course this does require some fiddly work to get it to the level you'd want of it... But since you have far more control over things like how strong the jump will be, how strong the gravity will be and all that, you can get a lot more responsiveness.

This does, however, run on the "Every frame" mechanic so if you have a LOT of systems that are working and pulling the framerate down to an abysmal level this might not be the most ideal way of doing it... If that is the case you might want to make each state that uses the gravity to instead of using the "Every frame" option you could then instead put in a "Wait" action with real time and set it to the iterations you want... So, maybe a time of "0.01" or something... That's pretty fast though and it kinda requires even more finicky bits to get it to work just right (and I'm sure there's a far more elegant way of doing it) but that's been working somewhat for me.

Given all that, however, it does require some additional work... So, if you want to confirm that the character is grounded you'll probably want to create a set of raycasts that go only a small bit down from the characters feet to ensure that they're connecting. But that's not too hard to do, just make sure that you set the viewline distance to be far enough to only extend a little bit past the character's feet.

And if it has a collider and everything that it's going to interact with has a collider, that might be a way to prevent it from passing through things as long as it's velocity isn't blazingly fast (I've tested this and if the object is moving fast enough the collision systems might not be able to work fast enough to catch it... Since the collision system seems to work like a force field as opposed to a mass... The way I view it is like it's not a mass at all but more like a paper thin shell that only pushes outwards... So, if it breaks through that shell, it doesn't know what to do with it while it's within the system.

Does any of this make sense? I know it's kinda detailed on paper but in practice it's actually fairly simple. (I'll get a web-player build started to show you the system in action... It's for a side-scroller though but I'm sure the concept can be extended to incorporate a full XY planar style movement.)

Red

  • Hero Member
  • *****
  • Posts: 563
Re: Let's go over the character controller jumping issue again!
« Reply #7 on: August 10, 2014, 03:24:23 PM »
here the web build I was mentioning.

http://www.badseedgames.com/SideScrollingGameTest/SideScrollingTest.html

It's not finalized but does have a more responsive and snappier jump... That said this is made with the "Every frame" option so if you're able to have it run at a blazing-fast framerate the jump might be too snappy... And conversely if you have a computer that struggles it might be really slow and laggy... But it's a WIP so i've yet to work out all the kinks here.

zorranco

  • Junior Playmaker
  • **
  • Posts: 50
Re: Let's go over the character controller jumping issue again!
« Reply #8 on: August 10, 2014, 05:16:12 PM »
Thanks for sharing, red.

I am trying to figure out why character controller does not move while in-air, because if I trick it to think its grounded during jump (wich it isn't), still does not respond to direction change.

Red

  • Hero Member
  • *****
  • Posts: 563
Re: Let's go over the character controller jumping issue again!
« Reply #9 on: August 10, 2014, 09:52:22 PM »
Yeah, I think there's something inherent in the character controller itself that doesn't like having movement added to it when it's in the air... I mean, it is possible... I've had systems that I've tested where I have added force to it while it's in the air but it's never resulted in a very nice looking or nicely behaving thing. Maybe it has something to do with the physics... dunno...

Still, depending on what you want creating your own "gravity" might be what you're looking for... Or maybe it might be way too fiddly and tricksey for your personal tastes. All I can really suggest is to just try stuff out and see where that gets you. Experimentation is a bit of the fun of this stuff so have fun now while you can.

blackant

  • Hero Member
  • *****
  • Posts: 521
  • http://blackantmaster.com
    • blackantmaster.com
Re: Let's go over the character controller jumping issue again!
« Reply #10 on: August 11, 2014, 08:40:54 AM »
the setup i'm using simple and works perfectly without major bug.

not using character controller.

Capsuller collider + rigidbody
get axis Vector and us ite to translate everyframe pers econd on Late Update

for jumping
push Space or A for joystick (or anything you want ) and send it to another Sequence
--> Add Force with Vector direction used diring the move + Override Y Axis  by Jump Power

wait 0.1 before sending to another Sequence

--> In Air, Test each frame to know if you are grounded back or still to be in air
if you are Grounded go to first sequence

you can also add Get axis vector and Translate to modify direction in air
and add many desired features to your needs.

zorranco

  • Junior Playmaker
  • **
  • Posts: 50
Re: Let's go over the character controller jumping issue again!
« Reply #11 on: August 11, 2014, 11:54:00 AM »
I'm gonna study FPS wlaker enhanced and see if it's possible to make a clone with playmaker, the easy style :P
« Last Edit: August 11, 2014, 01:24:20 PM by zorranco »