Super Character Controller

Custom Character Controller built from scratch for Unity. Fulfills all common character controller functions such as collision detection and pushback, slope limiting and collider ignoring. New features include the ability to rotate the controller to any angle, sophisticated ground detection, ability to clamp the character to surfaces, and detailed collision response messages. All functions are fully exposed and editable. Also includes the SuperStateMachine, a finite state machine designed to interact with the controller.

GitHub repository of the project to download or contribute to

Note: The GitHub repo is brand new, and is the first open-source project I’ve maintained. For those more experienced than me at this, any suggestions on how to improve it are welcomed.

Example Unity Project containing demo scene (same project as on GitHub)

Unity package containing essential code (.unitypackage file)

Just a blue man in a technicolour world

Just a blue man in a technicolour world

Changelog

2.0.0: Added a BSPTree to replace RPGMesh, as well as new grounding techniques and recursive collision detection | Erik Ross
Added support for the CapsuleCollider primitive collider | Moodie
1.1.0: Added support for a fixed timestep and moving platforms | Erik Ross
1.0.2: Multiple optimizations | iivo_k
1.0.1: Fixed issue where ignoring colliders didn’t properly work | Erik Ross
1.0.0: Release | Erik Ross

For a more detailed changelog, open the README in the package.

References

Uses code from fholm, BitBarrelMedia, and UnityGems. Detailed referencing can be found within the README file of the package.

260 thoughts on “Super Character Controller

  1. Hey Drrossen, so I finally got around to looking at your project, here’s what I found. The issue is fairly simple overall: the SCC and the NetworkMovement script you have (which I assume is Gena’s CSP script) run on different Update loops. When the NetworkMovement sends back it’s data to the client, the client attempts to reconcile it’s position with the server. However, at the same time the SCC runs it’s own grounding and collision logic, resulting in a bit of a clash. In order to get this to work properly, you’ll need to sync up the SuperUpdate method with whatever movement prediction/reconciliation you’re doing, which is what I’ve done for my project.

    If you’re just looking to prototype out a project, you don’t really need prediction/reconciliation that much. By and large it’s a tool to prevent cheating (although it’s also used to keep players in sync), and typically people don’t cheat during the testing phase ;D. As well, it’s quite easy to add in later on in between current network logic, which is also what I did.

    • Hey Roystan, thanks a lot for the SSC!
      I’m also working on a networked project, would you mind to share your solution in more detail? I’m not quite sure how I should go about syncing the NetworkMovement with the SCC while still using a StateMachine. Do you sync the PlayerState from the server and then call the entire SimpleStateMachine-update for every call of the Move-method? (talking about the “Move” from Gena’s CSP)

      • Hey Leonardo, you’re my fav ninja turtle fyi.

        Anyways. Gena’s main technique is very similar to mine. He gathers all the inputs in the Update, and then runs his logic in FixedUpdate. Here’s what I do, and how it fits in with the SCC (and the state machine):

        I gather inputs in Update and push them to the server. That’s all that’s done in Update. On the server, each FixedUpdate tick I read the inputs and run them. Here’s where the SCC fits in. Because the SCC runs on it’s own Update loop (SuperUpdate, which itself is called from Update), this would cause problems with (most) networking solutions. So instead, I disable the main SuperUpdate (for the server characters) and instead run it manually, through a method called SuperCharacterController.ManualUpdate(float deltaTime). Running this runs the SingleUpdate method that’s in the current version of the SCC, except it sets the deltaTime to the provided value (this is necessary as you need to know how much time elapsed during the input the player provided). I am following Valve’s Source Multiplayer framework.

        I seem to get asked about this a fair amount these days, so I’ll push the SCC changes up to GitHub this week. It’s not an invasive change at all—there’s just one boolean that decides if the controller is updated normally or purely manually. Having the ability to manually run updates is also extremely useful for reconciliation, where the server sends back the authoritative position as it allows you to “re-run” a bunch of inputs all in one frame.

        I hope I answered all your questions, if not please comment back.

        Erik.

        • Haha, thanks for the fast and elaborate answer! I was experimenting yesterday and tried to make a ManualSuperStateMachine. I’m pretty sure the main thing I missed was the deltaTime, I could just calculate it from the timestamps of the inputs I guess. Looking forward to that Github push so I can see a well-tried solution.

          • Aight so I looked it up turns out Donatello is actually my fav ninja turtle. Sorry bud.
            So I pushed the change to the master branch on the repo. It’s not a large update, but it’s one that I’ve found useful and elegant. There aren’t any examples yet, but it shouldn’t be too hard to figure out. I’m thinking of putting up an example of my network controller, but as it is right now it’s specific to my project, so it’ll take a bit to make a generic one (and maybe would be more fitting as a separate repo).

  2. Hi, love your system but I have a few inquiries.
    One, how can I have the PlayerMachine cancel out velocity when colliding with geometry? Out of the box PlayerMachine keeps trying to move in whatever direction until natural velocity decay takes over and the player’s inputs are accepted again. This is most noticeable when jumping into a low ceiling. The SCC will “stick” to the ceiling until the y velocity becomes negative. I am making a game where the player will be pushed around a lot so this is a big issue!

    Two, occasionally when walking off of an edge the PlayerMachine will not correctly enter the falling state and will just “snap” to whatever floor is below. I can’t seem to find any particular pattern to this. I am using ProBuilder for my level geometry if that helps. (Its just mesh colliders)

    I stream my project on twitch so come check out what I’m doing if you’re bored. ^_^ http://www.twitch.tv/bamboy360

    • Hey Stephan,
      You can access the collision data of the SCC through the collisionData property, which will tell you what the controller collided with during it’s last update. You can use this to determine if you have contacted a wall or ceiling and adjust your moveDirection velocity accordingly.

      For the other problem, this is the second time I’ve heard of people having issues with the SCC and ProBuilder, but I thought I had fixed them. Do you have a video or gif showing the error?

      Erik

      • Its hard to reproduce as it isn’t consistent. I recently made a new level using simpler ProBuilder shapes but a lot more of them, rather than one or two very complicated shapes. I think this has helped, but every now and then it still happens.

        • I’ll look into it either way. From my last experience debugging a ProBuilder issue, it looks like at runtime ProBuilder objects function just like any other mesh collider. I’m assuming that it’s due to how lots of people use ProBuilder for prototyping out scenes quickly and end up with somewhat wonky geometry, with sharp edges and whatnot, that expose (literal!) edge cases. I have a fairly good idea what could be causing this, but could you screenshot your level in the area it happens?

          • Hey again. A few updates.

            While trying to make my velocity cancel out when colliding with something, I ended up going back to your demo project to test it on various surfaces in your scene, and low and behold, the snapping to the ground bug happened. It does not seem to be probuilder related. I’m using a slightly modified version of your player machine script. I’m at a loss. Again I don’t feel a screenshot would help because it does not happen consistently in any area or with speed. It seems to be random.

            While trying to make my velocity cancel out when I collide with something I ran into a problem in SCC.cs where you were calling ‘collisionData.Clear();’ at the start of your function RecursivePushback(). This was making it so that collisionData was empty 90% of the time and other scripts could not read from it. I fixed this by calling it in SingleUpdate() right before RecursivePushback() instead. I have not seen any issues from doing this. Consider making this change live?

            As far as canceling out the velocity variable itself within PlayerMachine, I did the below at the start of LateGlobalSuperUpdate(). It seems to work very well for my purposes but I have not tested extensively. Consider making this change live?

            //Stop moving in the same direction that was collided with
            if( controller.collisionData.Count > 0 )
            {
            foreach( SuperCollision c in controller.collisionData )
            {
            moveDirection = Vector3.ProjectOnPlane(moveDirection, c.normal);
            }
            }

            • Hey Stephan,

              I’ve pushed a change fixing the collisionData being cleared to the master branch. I’m not sure right now about adding the velocity cancelling, since ideally the PlayerMachine was intended to be as basic as possible to demonstrate the controller, but I don’t think that block of code would really push it over the edge. I’ll probably end up adding it in a future commit.

              For the clamping issue, I had another user run into the problem, and I wrote a large post about it here. You can look at the branch I pushed up for him and see if it solves the issue, but it’s not the most elegant fix, and I’ll be looking more into the overall problem further to find the best solution.

              Also, if you do find other bugs or make improvements to the controller, feel free to pushing your own branches to the repo, since I respond to the pull requests pretty quickly.

              As always, thanks for the feedback.

  3. So, I was moving forward with my project, and I have objects that the player can throw (using the Rigidbody component). However, I’m noticing that they’re not colliding properly anymore. Does implementing your system override the natural physics built into Unity? For example, my player throws a ball, but the ball just goes right through the ground instead of colliding with it as it should.

    • No. The SCC doesn’t modify the way the physics work at all, and interacts with them fine. The sample project on GitHub does have it’s own Project Settings, so if you imported your project into the sample, you would lose whatever current layers you have setup. In any case you can check Project Settings->Physics to make sure the proper layers are setup to collide. If not, build a new scene and test if something within the scene has broken.

  4. Hey Roystan,

    I’m in the process of transitioning from Unity’s CC to your SCC and have a few questions I hope you can answer.

    How am I expected to respond to collisions, with enemies that cause a recoil, for instance? With Unity’s CC I use OnControllerColliderHit() and build some lists of what was collided with this frame. From that I can ascertain what is in an “Enter”, “Stay” or “Exit” phase of a collision. I see that you expose “collisionData” in SCC. Is the idea that I iterate over this at the end (or start) of a frame and perform some similar logic? What point in the Update phase would be the best to do this?

    In your demo you have a CapsuleCollider on the Art node. I think I read somewhere that the spheres on the SCC are used for collisions from the SCC into other objects, and the capsule is used for collisions from other objects into the SCC, is this correct? Could you elaborate when I should be using “collisionData” in SCC versus the OnCollision methods that would fire from the CapsuleCollider? I imagine there are specific use cases you had in mind for each?

    Is it OK to have a kinematic RigidBody component on the same node as the SCC? This is how I had it setup with the Unity CC.

    Have you ever seen the SCC fall through a MeshCollider? I have added the BSPTree onto the same GameObejct as the MeshCollider. My character now runs on the flat part of the mesh OK, but as it starts to slope downwards, the SCC falls through the mesh. I have yet to debug this, but maybe you can think of a reason? Does it have problems with particularly sparse or dense MeshColliders? I’m using Unity 4.2.3f1, it could even be a physics bug I guess. Here is an image of the mesh collider. The collider itself is just a loop that goes round the entire terrain, it doesn’t have a front or back.

    http://postimg.org/image/zcs088b63/

    My character falls through roughly at the red X.

    I have read through the entire PDF that comes with the package and looked at the demo scenes, but couldn’t find any info on the above. If I’ve missed something though, please just point me towards it and I will read some more🙂

    Thanks a lot,

    Niall

    • One other question that has just popped into my head. Should I be aiming to have the sphere based capsule approximation (on SCC) and the actual capsule collider (on Art node) matching up exactly, or should one of them be slightly smaller and “inside” the other?

      Any tips for building the sphere based approximation for best results? My character is much slimmer than your test capsule, so I ended up using spheres with a radius of 0.18. I have used 5 spheres total and have tried to spread them out by the radius (your demo used 3 spheres and spread them out in a similar manner it seemed). The head sphere is intersecting a little bit more with the sphere below it so I could match the character height better. I have marked the bottom and top spheres as Feet and Head, and left both boxes unticked for the middle 3 spheres. Does this all sound right?

    • The original issue I had with falling through the terrain was actually caused by a missing BSP component. I had added the BSPTree for testing, but my terrain rebuilds whenever it is selected and I had inadvertently removed it.

      Now that I have that sorted, I as still seeing my character fall through the MeshCollider occasionally. Usually when he should be landing from a jump.

      I think this is due to the use of Physics.OverlapSphere in RecursivePushback().

      If the sphere radius is small enough and the character is moving at a large enough speed, the sphere will teleport from one side of the MeshCollider to the other. As the surface of the terrain has no thickness, when the sphere is “inside” the terrain, it doesn’t cause an overlap to occur and so we never do any pushing back to keep him on the surface of the terrain.

      I have reproduced this quite easily in your test scene by just changing the sphere radius to 0.05 and spacing them appropriately. When you jump off the starting block onto to the ground below, the character falls through. It isn’t 100%, but if you run around in the pit for a while, you’ll fall through as well pretty quickly.

      For my game I want to use a sphere radius of 0.18. My game scale is set up to reflect real world units already. The proportions of the character are fairly realistic and I want to have the capsule to just be slightly wider than the torso when viewed side on.

      In my game, the player can easily hit speeds of 25 m/s when falling from a platform. By my calculations, I would need to cap to about 10.8 m/s to avoid the sphere moving by more than it’s radius each frame.

      Even using the sphere radius of 0.5 in your demo, this means that a character can’t move at any more that 30 m/s before we start to see potential issues. I think there will be less issues with Sphere and Box colliders etc as they have an actual volume, mesh colliders on the other hand…

      Is there any reason that this call to OverlapSphere couldn’t be replaced with a cast from the previous known position of the sphere? We can assume that in the previous frame, none of the spheres were inside anything as we would have ensured this in the previous frame with RecursivePushback().

      I guess another object could have moved on top of the character actually… balls. Maybe we need to check both a SphereCast and an OverlapSphere to ensure we catch everything (even if we still might not catch MeshColliders moving on top of the character. People tend to sue them for static objects though, so we might get away with it.

      Any ideas on how best to work around this?🙂

      Thanks a lot for your time.

      • Hey Big N, several questions here, I’ll try to make sure I address all of them.

        The SCC doesn’t have anything directly analogous to the CC’s OnControllerColliderHit in the sense of a callback. I decided to avoid having a callback since the SCC already had a fairly strong structure in terms of the order that operations are performed in, and that an array of data would be more useful.

        Speaking of useful: you iterate over it whenever you need to know if you collided with something, or what kind of thing. It tells you which collision sphere (on the SCC) you hit, and a bunch of data where the collision occured and with what object. So to answer your question: you can iterate over it whenever you want, but I’d recommend only doing it during your SuperUpdate callbacks.

        If you’re attempting to build a system on top of the SCC that implements an Enter/Stay/Exit functionality, this is definitely possible. Again, I’d recommend either doing this at the beginning or end of your SuperUpdate callback.

        You are correct about the purpose of the Capsule Collider: it’s just there to give other objects something to collide with.

        You can definitely have a kinematic rigidbody on the SCC node (in my experience), but I’m not sure what purpose it would serve.

        I’ve always had the Capsule Collider match up exactly with the spheres, but there isn’t really any reason I can think of that they would be required to be.

        Your setup with the spheres sounds correct. In retrospect, I’m thinking I should just set the feet and head to be the lowest and uppermost spheres at runtime, since I’m struggling to come up with an application where you wouldn’t want this to be the case.

        Bear in mind when falling vertically towards a mesh collider, the distance you can travel without clipping through it would be equal to the height of the entire controller, not just a single sphere (since even if only the head is touching the mesh collider, the entire controller will be recursively pushed back onto the surface). But that doesn’t really address the core issue here.

        First off I’d make sure you have Fixed Time Step enabled and Fixed Updates Per Second set to something reasonable. That way, the SCC is frame rate independent. I typically set it to ~60, and with a speed of 25m/s you’d be looking at moving about 0.417 meters per frame…which probably would push the limit a bit for your case, given that your controller sounds very small (although the absolute size is irrelevant, it’s just the size compared to the velocity that matters).

        In terms of robust solutions, I briefly touch on this in one of my articles at the end. In general, the best way to implement this would be on the assumption that it would not be successful 100% of the time, since CapsuleCast and SphereCast both have the issue that they fail if there is an object intersection their origin. But by and large, that would be okay! The overwhelming majority of the time the recursive pushback works to free the controller from any colliders. Off the top of my head the best way to do this would probably be to throw it at the end of the pushback method, as a sort of final check to ensure we did not clip through anything.

        In any case, it’s something to work on when I get the time. As well, I’ll also update the documentation about the collisionData stuff, since it seems I only have one or two brief sentences about it that could stand to be a bit clearer. Hopefully I’ve answered all your questions here!

        • Haha, right back at ya, Big R!

          Thanks for addressing each question, really appreciate the support.

          I am not currently using the fixed time step mode.

          None of your demos seem to use the FixedTimeStep mode from what I can see, am I right in thinking that?

          Is it expected that I just run my entire PlayerUpdate using the fixed system, but instead of using Time.DeltaTime in the scripts, use SuperCharacterController.deltaTime?

          Hmmm, I see what you are saying about the RecursivePushback and the entire capsule height having to pass over in order to phase through. I guess the issue could be that it’s pushing back down instead of up? If we managed to catch the overlap on the head sphere (or any sphere in the top half of the capsule), the path of least resistance would be to move the controller down I’d imagine? Haven’t quite understood all the internals yet, so just guessing really at this stage. Is there any bias to push upwards given that is the way the normal of the MeshCollider would be facing.

          I also noticed that ProbeGround only uses the Feet sphere. In the case of ProbeGround, I imagine that it would only take twice the radius of the capsule to phase through. This could mean that the ground variables don’t get set correctly in this case. What happens if the SuperGround objects didn’t register the platform, but then the RecursivePushback() detects an overlap?

          I will continue to debug and see if I can figure out any of the above and the root causes of the falling through issue. Perhaps using the fixed timestep will sort it out…

          As an aside, any advice for treating the bottom of the capsule like a rectangle?

          • I leave Fixed Timestep disabled for the demos since you tend to want it disabled for any debugging. It can be difficult to narrow down an issue when multiple updates on the SCC are being run in a single frame. For a “finished” testing, it’s almost always a good idea to have it.

            And yes, you should always use controller.deltaTime in your SuperUpdate callback. Even if Fixed Timestep is not enabled, controller.deltaTime is just set to Time.deltaTime.

            The pushback happens in the direction of the normal of the triangle that the closest point lies upon, so it will never force itself further downward.

            If ProbeGround misses, it assumes that the SphereCast origin was clipping a wall (or something) and attempts a raycast downwards instead. If that fails, it debugs out an error and no action is taken. Here’s where things can get a little woolly. In theory ProbeGround should barely ever miss: since Pushback is run right after SuperUpdate, the controller should never be clipping an object when ProbeGround is run due to your character’s movement logic (unless you clipped through an object, like in your example). In practice, on rare occasions it looks like ProbeGround’s initial spherecast fails while on the edge of a cliff, the raycast kicks in and it clamps you to the ground at the bottom of the cliff (although I thought I had resolved this!).

            For your case it might just be clipping. Try Fixed Timestep, or reduce your character’s gravity to see what’s up. If neither of those work let me know!

            • Great. I think switching to fixed timestep has resolved a lot of my issues.

              I have also upped my sphere radius a little to 0.21, this should let me have my terminal velocity of 25 m/s at 60hz.

              Some of my remaining issues are caused by having platform objects that use multiple box colliders along the length of the platform (they are resizable in editor and made of pieces at present). Due to the way the pushback works. I will need to combine the colliders into one I think.

              Did you see my previous question about treating the bottom of the capsule more like a rectangle?

              The current issue is when I run into a crate, I zero out the horizontal velocity. When my character jumps, I give him a small horizontal velocity to force him forward (but stop him zooming over the top at running speed) and collide with the top edge of the crate. In the old system, this was enough to place him on top of the crate and he would then start running again. With SCC, the collision point is too far from the centre of the capsule, so this is treated as not steady ground and the character just falls to the ground below.

              I have tried to modify the variables used in OnSteadyGround() (groundingMinPercentFromcenter and groundingMaxPercentFromCenter), but this then causes clamping issues when dropping off of ledges:/

              Any ideas on how to proceed from here? Ideally, I don’t want to modify your code too much (if I can help it) to make future updates easier.

              My initial thought is to have OnSteadyGround always return true (this causes the clamping issues though). I would try and resolve this by adding in a ClampThresholdDistance, the distance to the ground would need to be less than this value in order to clamp. I would probably also add something so that it could only clamp to the last ground we actually came into contact with. This would prevent the player clamping to the ground below a platform instead of falling towards it.

              As always, really appreciate the help. Trying to fit your system into an almost complete game so we can ship it. Fixing the bugs with Unity’s CC was just not possible.

              • Yeah, gotcha. Since I primarily used the SCC for Mario, once I set those max/minPercentFromCenter vars I more or less forgot about them. Their purpose is to smooth out movement, since without them the capsule will “clamp” down the edge of a cliff until the dropoff finally occurs, in which it will switch to some sort of falling state. The problem is that typically when a character enters a falling state, their y velocity is set to zero and gravity will slowly accelerate them downwards over time. This creates a weird jittery effect, since the capsule’s vertical motion while clamping down the edge of the cliff can be quite fast in comparison to the slow decent of gravity.

                Either way it was always intended to be modifiable. The clamping issue you’ve encountered occurs because the SphereCast used to detect ground is slightly smaller than the radius of the controller (or tolerance reasons). So when you’re right on the edge there and you cast down, you completely miss the cliff and clamp to the ground. Although it’s not usually ideal to clamp down a large distance, the SCC is built to not care about that and instead let the PlayerMachine or whatever implementation deal with enabling/disabling clamping…unfortunately, in this case the ProbeGround that causes the issue occurs AFTER SuperUpdate but BEFORE the controller is clamped, giving no chance to the PlayerMachine to sort out which state it should be in.

                That’s probably a lot to take in. Anyways, I wrote up a quick fix for this and pushed it to a separate branch, which you can find here. It’s not the most brilliant solution ever, but for a one off thing it should work fine. Basically there’s just a new message sent out, OnBeforeClamp, that lets the PlayerMachine run some logic just before clamping happens. Sometime I’ll write something a bit more robust, but this should work fine for now, allowing the controller to live on the edge.

  5. Hey,

    I’m starting a new thread here, as the other is getting quite thin with all the replies.

    Your explanation makes perfect sense, thanks for the info. I am slowly building up a good working knowledge of how everything fits together (I think).

    I have integrated your SteadyGround branch changes, and as far as I can tell at this stage, it has fixed the issue at the edge of raised platforms, so thanks for that.

    So, one problem fixed, but a few more to go. I feel like there could be an end in sight fairly soon though🙂

    I have started hooking up the Enter, Stay and Exit logic we spoke about and noticed something curious.

    The collisionData list is cleared at the start of each call to RecursivePushback(). This is problematic for me as I am seeing this behaviour:

    1) RecursivePushback() is called from SingleUpdate()
    2) collisionData is cleared.
    3) Character feet sphere is overlapping with the ground
    4) The ground collision gets added to the collisionData.
    5) RecursivePushback() is called from within RecursivePushback().
    6) collisionData is cleared.
    7) Feet sphere is no longer overlapping

    The end result is that when my code checks the collisionData (in a custom LateSuperUpdate event, fired from the very end of SingleUpdate()) , it is empty.

    I have tried moving collisionData.clear() out of RecursivePushback and instead into SingleUpdate. This works a lot better, but I’m still seeing some issues with it.*

    collisionData is still sometimes empty, more often when moving down slopes, but it also happens on flat ground. I’m guessing on flat ground it’s due to floating point inaccuracy on the pushbacks so we might get lucky and still be overlapping the last time RecursivePushback is called. For the slopes, it could be that gravity isn’t large enough to move the character down enough to overlap. This makes sense and it’s why the clamping is needed.

    Perhaps collisionData could be modified in ClampToGround to add the collision data for the ground (if it’s not already present). I think this may solve the remaining issues I’m actually seeing (but not the potential issues I outlined above).

    What do you think of the problem and my potential (hacky?) solution? I think there must be a better way.

    I’m wondering if I should even be using collisionData at this stage? Perhaps I should only be looking at currentGround or some other data structure?

    CurrentGround doesn’t seem to expose enough information to do what I need though, the actual collider and also the collision point for instance. I can add these things, but just want to make sure I’m not missing anything.

    Apart from when the character has “escaped the level”, can I always rely on PrimaryGround being valid? Near, far, step and flush could potentially be null though?

    When should I use Normal() instead of PrimaryNormal()?

    HitDistance() and Distance() seem to be identical functions.

    As always, very grateful for any time you can dedicate to a response, I feel like I’m learning a lot!

    Cheers,

    Niall

    *There are also some potential issues I haven’t actually seen or looked into, but I imagine there would be issues with the same collider appearing multiple times in the list with potentially different collision points as the controller works to untangle the collisions (filtering on add could sort this, but do we want the old version or the new version for the collision data?).

    • Hey,
      The collisionData being cleared was a bug that I just fixed a couple days ago. You can view the fix in the latest push on the master branch. Right now I don’t have time to respond to the other comments, but I’ll definitely get to them later today, just figured the above was important to address quickly.

      And yeah, when I picked this styling for the site I didn’t anticipate how quickly the comment section would have the walls close in on it. Planning to change it in the near future to something a bit more flexible.

      • Thanks for the quick reply. Should have checked the GitHub for fixes before posting.

        I have got some info regarding the times when collisionData is empty.

        The updates where there is no CollisionData have very very small deltaTimes (the tail end to fill the 1/60th of a second when using fixed timestep). This means that the movement due to gravity is very tiny. I’m assuming that due to physics tolerances, this tiny movement doesn’t cause an overlap and thus no pushbacks are generated.

        Not sure how to fix/workaround yet.

        Quite easy to repro constantly though, just put:

        deltaTime = 0.005f;

        at the top of SingleUpdate().

        It will all be in slow motion, but it should show the issue.

        • Are you using collisionData to check what kind of object the player is standing on? If you are, in general it’s better to use the currentGround.transform or currentGround.superCollision type data. This way, it still works regardless of whether the physical controller actually intersected the ground in the previous frame (I tend to zero out my controller’s gravity when he is assumed to be standing on an object, which is done in the PlayerMachine).

  6. This is very good character controller! I have nothing bad to say about SCC. Very goodly optimized. This only needs little bit better documentation. It was little bit hard to understand how to use SCC at begin, but anyway very good work! And this should be included to unity by default:)

  7. Hey Roystan,
    I know someone asked awhile ago, and you mentioned having planned to give it a shot, but are you still planning to implement the ability to have the SCC compatible with Terrain Colliders?

  8. seems promising.I am implementing it on my project now, though I would like to know if it is mobile optimized? If object is really moving too fast on any one axis ,does it catch collision ? or it misses collision sometimes like Unity’s default controller?

    • I have not extensively tested it on mobile, but for most of my optimization tests I usually just drop in a ton of dummy controllers and have them walk into walls (usually a mid to large size mesh collider, as they are the slowest collider to do intersection tests with).

      It does not have anything built in to prevent “phasing” through walls. It does have an option to run on a fixed timestep to ensure that a certain number of updates are run per second, so that if the framerate drops he won’t just skip forward. Protection against phasing through walls is a feature I’d like to add, though.

  9. Hi!

    First off, fantastic work! I’m loving the SCC and the SSM! I’m currently having a minor issue regarding fixed timestep. When enabled, the camera will start micro-stuttering, but only when rotation is performed with the mouse (you can check this in the demo project by simply enabling fixed time step, set it to 60 and then move around the purple box on your left as you enter the testZone; remember to move the mouse on the x-axis as you rotate around it. Observe the corners of the box, they should now stutter).

    I believe the reason for this stutter is that the lookDirection variable that the camera accesses through the PlayerMachine is now only updated every 1/fixedUpDate. The peculiar thing is that I have vsync enabled and a steady fps at 60, so they should both really update every 1/60th second? If I disable vsync, the stutter disappears, however I am not sure this solution is the best way about it. I have moved the lookDirection variable to the playercam instead, and have the playermachine retrieve it from there. This has solved the stutter completely, and I believe this is the most clean solution. Alternatively I could simply have lookDirection be updated in an actual Update function in the PlayerMachine (instead of the EarlyGlobalSuperUpdate). I would love your thoughts on the matter.

    Thanks for being so awesome!

    • Hey Metamate, your name definitely sounds like a stuffed animal I could buy on Etsy or something.

      I was able to re-create the problem. Often, you tend to want to de-couple the camera from the physics (in this case, the PlayerMachine) like how you did it by placing it in the PlayerCam. However, because the SCC always updates AT least once, it shouldn’t be missing any inputs (as can happen if you poll for input in FixedUpdate). Gonna look into it a bit more, will post back here. Sorry for the slow reply, haven’t been around too much. As always, thanks for the bug report!

  10. “Fulfills all common character controller functions…”

    I don’t think that’s necessarily accurate. After looking through the example, it appears that you have to move your character manually:

    “transform.position += moveDirection * Time.deltaTime;”

    Whereas Unity’s character controller would take the desired movement via Move() or SimpleMove() and figure out the rest.

    So how does this work exactly? The more I read the code, the more confused I get…

    • Now my character is moving around at the speed of light with this component attached (even after I reduced the speed to 0.00001…) I can’t even fathom what’s causing that. lol

        • I did, that’s where I got the snippet of code from. I managed to fix the problem I was having with Unity’s controller though, so I think I’ll just stick with that for now as it’s more than enough for my current project. Thanks anyways though!

    • What do you think the methods Move() and SimpleMove() do actually? They’re simply methods that take in arguments, and execute things like “transform.position += … “

      • I know, but I assumed it hid that statement behind its collision checks and stuff. I just couldn’t wrap my head around how this script enforced slope checks or collision without controlling the movement itself…

    • This works differently than rigidbodies and AddForce. Instead of adding forces and impulses, you directly modify the transform of the object to move it. Check out the demo project to see an example.

  11. Hi, I’m having a fair amount of trouble with triggers in the scene. I’ve got a cube with a box collider set as a trigger and occasionally when jumping, the player will collide with the trigger, landing on top of it rather than passing through it.

    • Note: I’ve tested this on your example scene by just adding in a trigger cube to testZone, and it happens on there too.

      • Hey Jaeson, this is due to the OverlapSphere ignoring colliders, while the various ground-detection ray and spherecasts would detect them. To fix this we can make use of Unity 5.3 (maybe 5.2?)’s new QueryTriggerInteraction enum, which allows us to select whether we want to detect triggers or not.

        Anyways, I updated the SCC with the above. I didn’t yet push it to the repo since there’s several things I’d like to sit down and fix before making another update, but here it is. Just set the variable in the inspector, but bear in mind it cannot be modified at runtime (as the SuperGround constructor takes in the value from the SCC).

  12. Awesome controller, definitely an improvement over Unity’s built in offering!
    Any chance you plan to add a more complex playermachine to the repository with some of the more advanced functionality present in your Mario 64 HD demo such as…

    – ledge grabbing
    – sliding down slopes
    – rotating character to match incline normals
    – simple attacks

    anyway thanks for offering this tool to the community, it’s providing a nice starting place for me!

    • Probably not. While what you’ve listed would definitely be useful, I tried to only add the features that I felt were fairly universal, and primarily in the domain of collision detection and grounding. Since how ledge grabbing etc work depend greatly on what type of game you want to make, I don’t think I could make a particularly modular solution.

      • Fair enough! In that case if I manage to get my hands on some of the scripts from your Mario impelementation is it ok with you if I use them as a base for my own playermachine?

  13. Could you give an example of the code to query what game object we collided with? I’m comfortable with the standard Unity way of doing OnCollisionEnter method and the if(co.gameobject.tag == “bad guy”) etc

    With your product though, I’m not sure how or where I am supposed to be doing that query.

    Thank you for your help.

    • The SCC doesn’t use any collision messages like Unity’s rigidbodies. Instead, all collisions are stored in a List property named collisionData (updated every frame). So you can just retrieve the collisionData property and then iterate through each collided object one at a time.

    • I looked up a video of this game, and the controller looks like it would be very simple to code. I’d recommend looking into the Unity starting tutorials to get something like that up and running!

  14. Hey Roystan, great work on the controller, it works really well.

    However I’ve detected a case where it seems to fail. Perhaps it’s something simple and you can find a fast answer to it, but I’m at a bit of a loss working on your code…

    If you walk on the edge of a vertical cliff (let’s say the edge of a big cube, for instance, I was able to reproduce it on top of the purple cube of your test scene) if you try to walk almost parallel to that edge, getting closer and closer to falling, at a certain point, the character will just teleport to the bottom of the cliff instead of falling off…

    Also, for some reason I fail to understand, the narrower the cube you’re walking on is, the more often it seems to happen. I don’t think I’ll have this case in my game, but I thought you might want to know it in order to further improve your awesome system!

    • It’s an abstract class that you extend, and it allows you to write methods that correspond to Enum names and get called depending on what currentState is. PlayerMachine.cs is an example of how it is used.

  15. I’m running into a couple issues trying to use this, and also have a question if it’s possible to do something in this easily.

    One issue is with the spheres defined on the player. With the default spheres, I have no issues. When I try to define a few with a radius of 0.3, to match the size of my capsule collider, the player slides on the ground no matter the slope they are on.

    With some colliders the player can walk partially through them, and then get rubberbanded outside of the collider. Is this because I am moving the player outside of the SuperUpdate method? I haven’t tried to switch my codebase to that just yet.

    One of my characters is a spider-like creature that can walk on/under/up/down/etc any surface. Basically if it runs into a wall it needs to start walking up that wall. I’ve seen that there’s a lot of sphere casts and raycasts and such going on in SuperCharacterController, so I was wondering if it would have the data I need somewhere already to know where to apply the gravity to for this character, or if I need to do my own raycasts?

      • Another issue I am having is with performance. In the profiler, Physics.CapsuleCast is logging as 1.1 ms, and that’s just for one SuperCharacterController in the scene. I plan on having a minimum of a couple hundred most likely… Anything that can be done about that?

        • I’ve fixed most of my performances issues. For the raycast and sphere cast, I set the max distance to be 20 meters instead of infinity. Since those methods seemed to be primarily checking if the player is grounded, I think it was a safe change to do that since they definitely are not grounded at more than 20 meters. This is much more efficient when there’s a lot of objects in the scene.

          Another optimization I did was in BSPTree to not build the tree on Awake, but instead do it in BSPTree.ClosestPointOn if it hasn’t already been built. It seems a minimal hit on performance to do this one at a time, so not noticeable when a player collides with it on the first time, and gives a significant speed up if you have a lot of mesh colliders.

          I think my only remaining issue is the player sliding on the ground for smaller a radius. I haven’t been able to figure that one out.

          • Hey emrys, sorry for the slow replies, life is getting far too busy for me. I’ve read over your posts but to save time I’ll just reply to your last issue! The player sliding on the ground typically occurs when there’s some imprecision in calculating the ground beneath the player combined with ground clamping. Ground clamping essentially snaps the player to wherever the ground is every frame. So if the ground position is slightly below where it should be, it will snap the player lower than [i]he[/i] should be. And then over frames this will slide him around slowly.

            I’ve wanted to rework how grounding works for a long time, but I’ve actually recently switched to Unreal for personal use (I use Unity professionally, but not the SCC) so I’m not sure how much development I’ll be doing on this in the near future.

            • Alright thanks. Even with the couple issues I’ve had, this is much better than the Unity character controller, so thanks for all the work you’ve put into this. Do you happen to know off-hand what acceptable values are for the spheres and radius to not allow sliding? I know 0.3 slides, and 0.5 does not. I haven’t played around with the values yet to see what the lowest I can go without sliding.

              • In previous versions I was able to use practically any sphere radius, so this sounds like a relatively new bug. Extremely tiny sizes of anything (like 0.00005) cause issues in virtual any kind of collision, but 0.3 really shouldn’t. Not sure what the deal is atm.

                • I figured out why a radius of 0.3 was not working. I didn’t align the spheres to be in exactly the position of the bottom of my player, and so when the player was spawned in they were placed like slightly in the ground, which I guess caused issues with the character controller controller. After changing it so that the feet sphere matches the exact location of the bottom of the player, no more issues.

                  • I can see how that would be a problem, since I’m not sure I ever accounted for the bottom collision sphere not being aligned with the ground. Definitely an oversight on my part.

  16. When I run into another player that also has a SuperCharacterController component, it will sometimes push the other player out of the way instead of just stopping me. What is causing that? I haven’t been able to find anything in the code that looks if you are colliding with another SuperCharacterController.

  17. Hi! First of all, thanks for this great SCC implementation! It’s been so useful!🙂

    Although, i’m having some issues: I’m writing a prototype of an FPS game were players can change gravity. In slower machines, almost every time a player changes gravity, it passes through the ground. I tried changing to fixed delta time and tweaking some parameters as MaxPushBackIterations, Tolerance, etc. without any luck. Is there any workaround to this issue?

    Thanks!

    • Hey Zak, does it only occur when you try to change to a very high gravity? I tested using about ~100 (with a Fixed timestep) and they didn’t go through the floor. As well, does this only happen if you try to change gravity at runtime (if you start at a very high gravity, do they still clip?).

      • Hey! Thanks for your reply.

        I’ve managed to fix it by changing Update to FixedUpdate in SuperStateMachine. It seems that even with SCC running in FixedUpdate, it fails checking collisions when moving long distances when a drop in frame rate occurs. Does it make any sense?

    • Own Collider is used to store a reference to a collider on the character controller. If you want other colliders to contact your controller, you’ll need one attached (usually a capsule to match the shape). However, it’s important that the SCC doesn’t collide with itself! So setting a collider to Own Collider makes the SCC ignore it.

  18. Hi erikroystanross, I find your SCC is awesome. I’ve research it and learn it a lot. I have a question, hope help me to answer it.
    I was testing the ability of SCC to simulate the collision which the given position and PlayMachine to produce output position step by step (update scc manually with fixed deltaTime) like this:

    input position(x,y,z) + state of PlayMachine(moveDirection, lookDirection, currentState,…)
    =>(run scc’s manuallyUpdate) => output position(x,y,z) + new state of PlayMachine.
    So with the given input and state of PlayMachine, I can produce exactly the output using SCC. But I’ve found that sometimes it didn’t work correctly as after I run it several steps, with the same inputs, the outputs were still different (different in position or/and in state of PlayMachine).

    I found the reason is inside the SuperCharacterController class, it accumulated or store somethings, some values have changed here after run steps and I didn’t include them in the input above. The error happened when the clamp value was changing so my guess are lastGroundPosition, currentGround.transform, and/or some members used inside RecursivePushback(0, MaxPushbackIterations) in SingleUpdate() function;

    So hope you can point out where I missed. Thanks in advance.

    • How different are the positions we’re talking about? If you’re off by like 0.0001 or something, that’s likely floating point error. I used the SCC for a multiplayer game (the one in the header, you can see a vid on the side bar) that does server-reconciliation; basically it reruns inputs to match the server’s position every frame. This required some pretty high levels of precision and worked well, but you’re right that you would need to sync up the SCC (and PlayerMachine)’s state every frame if you wanted to simulate from an arbitrary position (basically a deep copy of the SCC’s data). I’d like to help but I might need more information here on the specific issue you’re encountering!

      • Hi, After hours of hard debugging, I’ve found the answers, that I need to track the “currentGround.transform.position” carefully in SCC along with other members in PlayerMachine. So the SCC at the time A is the same as the SCC in the time B, Now I can achieve same output as long as I give it same input with same environment. All the hard things always happen when the isClamp variable is changed.
        About the floating point error, I’ve tested on the same machine and same unity editor so I don’t think it is a potential reason.
        Another question is how to climb a ladder vertically with SCC like some FPS game did?
        I really appreciate your willingness to help.

        • Hey daddlylongleg, I had laser eye surgery yesterday so I’m gonna have to keep this short, but to climb a ladder you could use a trigger to check when the player is near the ladder (make sure the SCC object has a collider attached and is applied to the SCC’s OwnCollider variable). Then, add a new state to the PlayerMachine that is activated that would disable ground clamping and allow the player to move up and down using the input.

  19. Hey Erik. So I’m once again looking into using your SCC for my project. However, I’m having an issue. I’ve set up my layers properly, but when I start up the game, it says there was no ground found beneath the player. I’m using a terrain by the way. As far as I can tell in your terrain sample scene, I don’t need to add any components to the terrain itself. What might I be doing wrong?

    • Hey dr, since you said that you setup your layers I’m assuming you have the TempCast layer added, but did you properly make sure to set the player’s Walkable layer to include the terrain’s layer? And you are correct that the terrain does not need a BSPTree script (or anything else) added to it.

      • Hmm. It’s possible that I forgot to set the Terrain’s layer as a walk-able layer…because I just gave it another try in a new scene and it’s working fine. Thanks for the speedy reply!

      • Can’t edit posts, so I’ll just post a second reply. I’m not getting the “no ground found” error, however, after moving along on the terrain a bit, the character just falls through. I’m not jumping or falling from anything, just walking forward. Also the terrain is a 100% flat surface at this point.

        • It also happens when my character jumps. I pretty much am testing this using the SuperStateMachine controller that you made so I disable clamping to allow the jump and re-enable clamping when the player lands. It works with a plane, but with the terrain the player goes through.

          • Did one more test trying to figure this out on my own. I took the Player object from the TestZone scene and brought it into the TestTerrain scene. It happens there too. Sometimes when the player is coming down from a jump, it’ll go right through the terrain.

            • Hey Dr, sorry for the radio silence for the past week, been a really busy time for me…I’ll try to take a look shortly, but things have been hectic. It’s possible one of the raycasts is clipping through the terrain collider and missing the ground collision, not sure. Will try to figure it out!

              • No worries! I completely understand life happens and it comes first. I just appreciate you continue to monitor and respond to these findings. It’s great to see someone keeping up with their work. Looking forward to any solutions/answers you find.

  20. What’s the purpose of the ‘currentlyGrounded’ parameter in the ‘IsGrounded’ bool in the Super Character Controller script? I’ve effectively removed it, and it seems to work exactly the same. I initially thought that when the character is already grounded you may want to give the spherecast a little bit more distance in order to hit steeper slopes while walking down them, but when i searched ‘currentlyGrounded’ the only results were when the method is being executed and in the parameters of the method.

  21. Hey Erik! Thanks a lot for developing and sharing this great character controller! So far I love it!! It’s exactly what I needed for my FPS project!

    Everything works great except from an annoying bug. Sometimes the character controller goes through the wall/ground. It often happens when the SCC is on a moving platforms (which moves vertically) but also when the SCC moves fast.
    For some reason the SCC stops colliding for a frame (maybe more) when its movespeed is high. The project runs at 60fps without any fps drop and I enabled Fixed timestep on the controller.

    I tried looking into your code but it’s too complex for me to understand it. Is there any fix or quick hack for this bug?

    • I’m having the same issue with moving platforms in a project I’m working on…which seems odd, since it was definitely working for my Mario project. Gonna look into it. Basically the controller doesn’t detect the ground on the platform and instead keeps building up downwards acceleration from gravity, eventually phasing through it.

      • Yup, one thing that I’ve noticed is that the bug often occurs when clamping is disabled on the controller.
        Try logging the name/tag of the ground detected below the controller. You will notice that when the controller falls towards a vertically moving platform or jumps on it, the SCC skips the moving platform but detects the ground below it. As a result the controller is stuck in the fall state for about a second (as if the scc was blocked by the moving platform’s collider but without detecting it..?) then quickly goes through the collider and clamps to the ground below (the one that is detected by the scc).
        So far, this bug did not happen with a horizontally moving platform but maybe I haven’t tested them enough.

        Thanks !

  22. Hey erikroystanros!

    I need to make normal and wall/ceiling walking NPCs and I decided to use your nice script.

    I have one question for you, I want to make NPCs like Sheeps, how can I modify this script to support spheres with x/z offset?

    Btw, I have optimized your BSPTree script heavily, now it makes no allocations & is around 100x faster. You want it?

    • To change the position of the spheres used, you’ll need to modify the CollisionSphere class (found in SuperCharacterController.cs). Right now it has a single float named offset that translates it up or down. If you want to support directional offset, I’d recommend replacing this with a Vector3. You’ll have to update the SpherePosition method as well, which takes a collision sphere and uses it’s offset to determine where in World Space it is. There might be a couple other little things but that should be the main stuff.

      As far as a better BSPTree, definitely! Feel free to submit it as a pull request to the main repo if you like.

  23. when i am testing the SCC, when i walk off the edge i get this error:

    “NullReferenceException: Object reference not set to an instance of an object
    SuperCharacterController+SuperGround.ProbeGround (Vector3 origin, Int32 iter) (at Assets/SuperCharacterController/Core/SuperCharacterController.cs:666)
    SuperCharacterController.ProbeGround (Int32 iter) (at Assets/SuperCharacterController/Core/SuperCharacterController.cs:226)
    SuperCharacterController.SingleUpdate () (at Assets/SuperCharacterController/Core/SuperCharacterController.cs:194)
    SuperCharacterController.Update () (at Assets/SuperCharacterController/Core/SuperCharacterController.cs:158)”

    what is this error and how do i fix it?

    • The SCC expects there always to be some sort of ground beneath the player to make sure it hasn’t clipped through the world. I usually just put a big endless invisible box collider for this purpose.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s