It is nice to see push back on the annoying trend of internet replies which promote not trying and just generally being dumb. Making things from scratch, or making your own version of something, is very powerful. It's really at the heart of engineering, understanding, and creative expression. Thank you very much for promoting people to not just accept the status quo. Reinventing wheels isn't bad when available wheels suck. Instant like and subscribe.
Yep. So much wrong with many of the resources available to beginner game devs. I literally just responded to another comment saying "tHiS iS cOmMoN kNoWlEdGe" and that the video is pointless. So even this guy who DOES know the "right" way doesn't want it to be shared for some reason!!! So much dismissal and gatekeeping all around.
@@poke_gamedev Tbf, the reason so much misinfo is being spread is precisely due to the lack of gatekeeping. People who aren't passionate about solving problems enter the field and just copy-paste code and tell people to do the same. There's nothing wrong with letting people in who are interested, but those who do not fundamentally care should probably be pushed away.
@@user-og6hl6lv7pyup but we have then two problems here gatekeeping and copy-pasters, gatekeeping prevents knowledge to be shared and as an extra we need to deal with the flexing, copy-pasters spreads the black box way of thinking should I say ¿? Also the Swept AABB algorithm from gamedev page doesn't works
@@BleachWizz I don't understand why you didn't just use a rigidbody controller this whole time, literally the same but better. Seems there's 4.9k people who should've just used Google to learn what a rigidbody is lmao
I'm happy to see you found my old paper useful! Not quite sure I follow the reasoning behind normalizing and re-adjusting the length of the projected vector in the sliding part, but this has been discussed in the comment section already ;) Great presentation and great video though!
Basically, when you project the velocity vector onto the vector (or plane) of the wall, it's length is only as long as the velocity vector *in the direction along the wall.* If he normalizes it, converting it to a unit vector (with length of 1), and then scales it by the length of the original velocity vector, he retains the full movement of the velocity vector but in a direction along the wall instead of into it.
@@ghostbusterz It's more accurate to keep the rejection vector as-is. By normalizing and subsequently scaling, the object will always move at full speed when grinding against a wall because the surface normal will almost never be perfectly perpendicular to the object velocity.
@@1234macro In video games we don't always want accuracy. It would be more physically accurate but that's not always what's most important for a given game's design.
Update 2: Ok so Unity is currently imploding... I was working on refactoring and adding stair detection, but I might take a detour and learn Godot now lol As someone in the comments pointed out, maintaining full velocity on slopes is not very physically accurate, so I thought I should clarify that all the fancy scaling I'm doing is specifically meant for *character movement*, and should be ignored if using this code for a more general physics engine! Again, thanks for watching, and I hope you found this video helpful!
It looks like that extra scaling with dot product ends up simulating the thing you already would get by projected slide. Although, it gives a control if you want to maintain full velocity. Also, In the place of a situation with a next wall, there is also an opposite problem - overshooting a corner.
That would convince me, if you didnt scale it again based on the angle between the wall and surface. You'd get that for free if you didnt scale the projected vector. Its also what happens irl minus the friction
good video! just wanted to point out that normalizing the vector at 4:00 only to multiply it by the dot product at 9:05 is unecessary since it reverses the normalization (it's like if you would multiply a number by 10 and then divide the result by 10, the second operation reverses the effects of the previous one). if you only want that "sliding along the wall" effect you can skip both steps to get the same result cheaper.
Was gonna come to say this ^ I believe that's also part of the original paper. The slower sliding along the wall when more perpendicular appears naturally as a result of projecting the movement vector along the normal plane.
Thank you I have been on the receiving end of the dismissal quite often, when to me, yes, I could use the premade CharacterController or Rigidbody that does "everything for me" but like... I don't learn from that. All I learn is how to control that specific component, not how a Character Controller *actually* functions. Not how Collision *actually* functions. Most these people couldn't make a game without the use of these tools. Which is fine, the tools are there so it's fine to use them, but I am tired of the dismissal when someone is genuinely interested into just learning "what's under the hood"
Yeah, not to mention that in Unity, the built-in character controller is just... not very good lol. It's honestly one of the main things Unreal and Godot have over Unity, and is definitely why the "uSe DyNaMiC rIgIdBoDy" suggestion pops up so often. People know it sucks but can't be bothered to actually find a better way.
@@poke_gamedevyou're so right. Not being able to do anything on any other axis than the up axis in Unity with the CharacterController is so weird, you'd think they would've fixed it by now
For Godot users: This is said in the documentation but, if you ever want to do this in Godot (with that I mean don't want to use the built in move_and_slide function), the engine provides the method move_and_collide() that does the collision casting + the movement, and the Vector2/3.slide() function to calculate the reaction movement.
@@mohamedmoh5789 I meant if you want to implement the movement yourself. Which is better if you want to have more control over the physics of the game.
This video probably would have helped me out tremendously a few years ago. I self taught myself Unity and refused to use anything anyone else made, so I spent probably a good year perfecting the movement in my own game, trying as hard as I could to figure out the stuff you're describing in this video. I eventually did it with a slightly different method, and I don't regret it because it ultimately taught me so much about the way things work, between time, frames, how acceleration, velocity, friction, etc work, but it's good that nobody else will have to go through the hell that I did. The issue you have described around the 10:30 mark ended up with my character getting their head stuck on any negative angle ramp collision, and at a certain point, actually ended up making them able to walk up the ceiling like a spider. I don't remember what I did to fix it, but I'll have to check out the paper on it.
First of all, this video slaps, no question. I was struggling for some number of hours creating a controller for exploring a 2.5d top down map with potentially sloping terrain. I found solutions for moving on slopes, but there were always problems addressing steep slopes (among other things). This is the first video I've found that addresses those problems while still using a similar algorithm for moving up slopes. The one gripe I have is I don't like when people say something is the "right way" or "actually good" when it comes to programming, because there are multiple valid ways of doing things, and some people don't always have the time to implement everything from scratch. The Unity character controller addresses these problems and has parameters for slope limit, skin width, etc. It's a perfectly valid way of doing things. I just didnt want to use it in this instance because our game had potentially unique requirements, and I wanted to understand the math myself anyway.
To anyone who's implemented this system: I am having an absolute hell of a time with glancing collisions. The first iteration consistently takes the player a little too far into the wall and results in the function getting stuck recursing, and for the life of me I cannot figure out why. I'm doing this in DOTS, so I don't have the luxury of a collider cast with a distance function. Instead I get a fraction. I suspect the problem is with how I'm performing the math for snapToSurface, which I've adapted to function with a fractional value like this: float3 snapToSurface= (vel * (hit.Fraction - (0.015f / (math.length(vel) + 0.015f)))); The logic here is that math.length(vel)+0.015f is the expression that defines the original cast length, so I'm finding the equivalent of 0.015 in fraction-space, and subtracting it that way. That seems to work, but only at steep angles. Once the toward-the-collision component of velocity becomes low enough, I get stuck. I can only guess snapToSurface is ending up with too high of a value somehow, but I can't figure why. It also occured to me to calculate it this way: float hitDistance = (math.length(vel) + 0.015f) * hit.Fraction; float3 hitSurface = math.normalize(vel) * (hitDistance - 0.015f); but the results are the same. Anyone encounter a problem like that in their travels?
Addendum - I've solved the problem. For others dealing with the same situation: This implementation seems to be missing a detail preventing infinitely recurring collisions on stationary colliders. This implementation deals with the problem by casting a skin width worth of extra distance ahead and then subtracting this value back out of the result, leaving the character controller against the wall plus the separation defined by skin width. The problem with this is it operates along the character's velocity vector, which, at very shallow angles, might have a component normal to the wall of nearly zero. Skin width will scale with that component, so if you have a glancing enough collision, the collider remains in touch with the wall and will collide infinitely. The effect of this is very obvious if you use a very large skin width and then run directly at a wall and then slowly slide off of it. You will see that the huge skinwidth holds you a long distance from the wall at first, but that this distance gets shallower as you turn. At extremes, this causes the stuck problem. I am not sure why the problem does not manifest in this video. My best guess is it's a difference between stock unity physics and Unity's entity physics implementation. My guess is most other engines will also present the problem as I experienced it. My solution to the problem is this: Rather than increasing our cast distance by skinwidth and subtracting it off the end, we cast along velocity directly. Then, if an impact is detected, we calculate snapToSurface a bit differently: float3 snapToSurface = (math.normalize(vel) * (hitDistance)) + (hit.SurfaceNormal*skin); This performs the same separation job, but relative to the wall normal rather than the character's velocity. One thing to note though is that you should probably scale your skinwidth with the magnitude of velocity when using this approach, otherwise you may bounce off the wall due to a skin width result that exceeds the velocity pushing you into the wall. You could also simply use a value of skinwidth that will always be less than the minimum velocity that could be applied.
Finally! Someone who understands my frustration with character controllers. Lack of interpolation between fixed update steps in the built-in one is by far the biggest drawback for me. Great video! I've never considered a recursive approach, I always go iterative.
So glad someone answers this question properly, a year or two ago I couldn’t find any help on the matter and came up with my own hacky but functional solutions
I've written many many character controllers over the years and this one is pretty top quality for how simple it is. In fact, it solves an issue I currently have which is that the controller I designed for my player is very fully featured but a bit too heavy-weight to use at scale. However, I needed a character controller that functioned similarly for NPCs. This little function saved me at least a day of tearing out chunks of some of my own controllers to get something that functioned well and was still resonably performance. Great video!
And could you explain to me some things (I'm just a beginner, and in some places I may not understand). Does everything work purely on colliders, without using RidgedBody? I also didn’t understand where he got the variable "bounds" and "layerMask". I will be very grateful)
@@ReimsForYou Yeah, there are a few minor things like I noticed too. The bounds are obtained from the collider(s) you would be using for your character - in most cases a single sphere or capsule, though in theory you could also use extend the bounds to include multiple colliders. In my case I'll probably go with a public Collider field in the MonoBehaviour that allows me to pass a reference to that collider. Then in Awake() I'll calculate the bounds and extend it by the skinWidth (the code for that is shown very early in the video). Then you can simply assign 'bounds' to 'MyCollider.bounds' within the CollideAndSlide function and it should just work. 'layerMask' is a similar deal. Just make it a public field and then you can assign whatever layers you want in the inspector. This code appears to move this object via its transform which implies that the collider would be a trigger and there would be no rigidbody attached. Likely in my case I'll end up attaching a rigidbody, marking it as kinematic, and keeping the collider as-is rather than making it a trigger and then moving the object via the Rigidbody's position value instead since I'll be needing physical interactions such as pushing other things around. Also, turning off the grabity for the rigidbody would be a good idea if you intend to use the built-in gravity this example provides.
@@ReimsForYou he has a collider on the body (sphere collider in this eg). The collider has a variable called bounds you can access from collider.bounds as shown in the video. It is there for the Sphere Cast. As for layermask, you can set a specific layer for collision check when sphere casting say you want to check if your character is only colliding with a specific layer eg. Layer 8 ur layer mask value will be 2
great help. I managed to recreate exactly this in Unreal, using Blueprint. Works great for having the camera slide at the edge of the map in an RTS game
I've been trying for months to create a 3d engine on my own and came almost with the same conclusions as this video, but with some exceptions. I'll borrow some things I've missed and if it works you'll have my eternal gratitude :D
This is exactly the thing I was working on myself and came as close as a hair's width to figuring out before switching to simply using velocity-based rigidbody controller. I didn't go as deep as reading an actual SCIENCE PAPER, though. Have been waiting for a continuation of this for quite a while and if you're still going to do this - it'd be incredibly interesting to see how you would solve stairs and slopes in a controller like this. Cheers!
I remember searching for this when first studying game dev years ago, it was incredibly infuriating the amount of trash replies you'd find. Thanks for this, I wish this video was around when I first started
It's funny how I tried to make a character controler on my own quite a while ago and was smart enough to came up with this, but stupid(inexperienced) enough to fuck up when coding it, your vid makes me want to come back to that project and try again
Poke Dev, you are amazing. Poke Dev please continue posting videos. Just writing your name so the algorithm can push you up. Damm, this video was amazing. From the editing to the content, it was so nice to watch I was thinking I was watching a Million subscribers youtube channel. Keep it up and you are going to make it far!
Well well well if it isn't the exact video I was attempting to find two months ago. I'm pretty sure normalizing the redirected vector and multplying by the magnitude is the piece I was missing and looking for someone to tell me. Nice job on this video, it's high quality.
@@poke_gamedev Something you might want to consider in future content is that parameters like "gravityPass" both presuppose that the game will have gravity and don't adequately explain what the effect of that parameter is. It requires the reader to fully read the method body before being able to use the method which is not always what you want from a premade solution. Something like "shouldSlideOnSlopes" might be easier to use.
I agree it could be more descriptive, however the way I wrote the function was as a private method to be called inside a larger character controller class. So in my case, someone using the character controller class wouldn't have any reason (beyond curiosity) to know the exact implementation for collision, as this method wouldn't even be exposed to them.
NO, please don't do that part! It doesn't make any sense to normalize and rescale it, and just leads to the problem at 8:50. That part in the video is just wrong, so please don't copy it. In real life it does lose magnitude, so it is not a problem.
Really nice overview! Even though I would personally keep the velocity decrease on hitting an edge instead of decreasing later on. Saw your pinned comment on this too. Thank you!
Thank you so much! I was looking for a solution when I was working on my own movement controller, and adding a speed boost would thrust me through geometry.
I was making a custom physics engine for unity. I gave up on it because the collisions and the slopes just did not work. I did not delete the code just in case I needed part of it because I added a lot to it. I saw this video and I was like "Hmm this looks interesting." so I watched it then I added the code to my project, and it did not work. the reason why is because I had the old code and this new code running at the same time. but I got it to work after about 30 min of trying. THANK YOU! for the video!
"did not delete the code" if you're not using version control, you should be. Seriously, it's worth the pain of learning (and actually with GitHub Desktop it's not even much pain)
I was toying around with this a while back but I was having a miserable time finding good sources of information for it, as comprehensive as this anyway. How are you able to find all this information so easily? I even consider myself pretty good at googling.
Right on time. I was just doing collisions today (for my game-engine-less game). Guess I'll be rewriting it tomorrow after watching this video lol. Anyways, this is just what I was looking for and couldn't find. Thanks!
To anyone who tries to implement this using a box collider: Don't divide the algorithm up into horizontal and vertical passes if you plan to have sloped floors in your game. Doing this will occasionally "miss" a collision detection and pass the player straight through floors. My solution to this problem was to only use one "pass" with the entire velocity vector, not separated into horizontal and vertical passes. And if the floor was angled within a walkable steepness, I would set the leftover vector3's y value to 0. This will stop the player from slowly sliding down slopes that he should be able to walk on, but would still slide him down along a steep wall.
The only video to appear on the subject that actually looks at the real problem, and everything you showed us in this video is exactly what I came to as a conclusion in my own testing over the past 2 years back and forth. Everyone else, just says use Rigidbodies or the built-in char controller, as you clearly showed as well. I have a slight different approach to mine, but the resulting vectors and movment is exacly the same, besides some trigonometry approaches to corner collisions. Another slight issue I have found with this, using raycasts, is that it more often than not sets itself inside the geometry of the world, thus the raycast does not report any hits for that collider. But I guess that the skin width solves that problem fairly okay-ish.
Great explaination! With the help of your video i implemented the algorithm in a custom godot character controller. Right now i only use a raycast, but i will transfer the logic to shape casts, which is the equivalent of the sphere cast in unity.
Awesome! Godot actually has a lot of this functionality built-in to its CharacterBody3D class (move_and_slide(), or something), which is what I would start with if I were to make the switch :)
@@poke_gamedev in the end I went with the built in move and slide function. It got me very far into the controller without many adjustments. I use it for a skateboarding game.
Oh my god, why couldnt this video have existed years ago? As someone who tends to become obsessed with doing things the "correct" way, every time I've gone to make a game I get a bit of the initial setup done, then get fixated on the character controller for weeks, unhappy with most of what I see online.
I'm quite late to this but there is an issue with your implementation, in the sphere cast params, you should put the original velocity as the direction not the normalized version of it, this is to ensure that the cast covers the full distance you'll cover in that same frame, this is needed when moving at higher velocities so you don't tunnel into walls. Other than that, this video is amazing, I'm currently working on my collision handler for my character controller and this helped tremendously, can't thank you enough ♥
I think Kasper's stuff was inspired by Paul Nettle's stuff (actually, it was, just checked the references in Kasper's doc). I remember this stuff back then, did an implementation of it myself. I seem to recall one of the problems with Paul Nettle's implementation (and perhaps Kasper's original implementation) was that clipping the side of a ramp at a very shallow angle would clip through/get stuck. Imagine you wanted to go straight up the ramp was off to one side too far and just missed it. It's been 20+ years, so things may be a little fuzzy. I never followed through with an implementation of the improved version. I'll probably dig back into this topic again in the near future.
A small thing to keep in mind if you want to use this for a more general physics engine, is that the thing at 4:04 where they rescaled the vector is not physically accurate. You should just leave it as is with the shorter length. Then you also don't need to do what they did at 8:50, which they only did to compensate for the rescaling earlier. Those things in the video really only apply to character physics specifically, since it handles walking up or down slopes inaccurately, which feels better in some situations.
@@Zicrus yeah thanks, This will only be helpful for CharacterBody and KinematicBody structures. I should make a move and slide function for them to make it easier for game devs. But for other physical structures like RigidBody I’m using more accurate approaches for collision responses.
Instead of scaling the projected vector to be the same magnitude as the leftover velocity, and then scaling it again based on the angle to the perpendicular, can we not just use the projected vector as is? Will the projected vector naturally contain the desired perpendicular component if we wish to slow down our character? I've not implemented this myself yet, so it's a genuine question. Thanks.
this was my thought. I haven't done the math but i believe it would give the same result and he's just taking an extra step for no reason. Although you could make the case that once you have the vector normalized you can tweak the on-wall velocity to your liking
Something is off. I followed the tutorial thoroughly and tried implementing the algorithm for a capsule shaped character. Collisions were fine on the surface, but when I had the character collide with tent shaped geometry (two stretched boxes tilting toward each other, and oriented so that the further inside you go, the lower the ceiling is), it started clipping through the tent, unless you head into the tent straight on. any horizontal movement is going to cause clipping. I figures it is with the skinWidth doing some goofy stuff like pushing the player away from the colliding object when the object is inside the skinWidth. can anyone help? Edit: Another thing that may proves this has something to do with skinWidth is when I set the skinWidth to something like 0.45f, then move at an angle that is almost paralel to the surface, instead of the moving the character a skinWidth distance away from the wall, it just slowly inches towards the wall, until it the distance between player and wall reaches the radius of the capsule cast (aka capsuleRadius - skinWidth), as if "snapDist = hit.distance - skinWidth" have very little effect. When looking and moving at the wall at an angle almost perpendicular, this behavior doesn't happen} Edit 2: Found out the hard way that just doing "snapDist = hit.distance - skinWidth" isn't correct at all. the closer to getting perpendicular to the surface's normal, the more you'd have to subtract from hit.distance. so here's the proposed solution: float bigHypotenuse = capsuleRadius / Mathf.Sin((90 - Vector3.Angle(hit.normal, -vel.normalized)) * Mathf.Deg2Rad); float smallHypotenuse = (capsuleRadius - skinWidth) / Mathf.Sin((90 - Vector3.Angle(hit.normal, -vel.normalized)) * Mathf.Deg2Rad); Vector3 snapToSurface = vel.normalized * (hit.distance - (bigHypotenuse - smallHypotenuse)); where as bigHypotenuse is the length between the big capsule center (aka the capsule without any skinWidth) at the position where it is contacting the hit point, and the point where the velocity would hit if raycasted (very much oversimplified, my wording is very off). smallHypotenuse is the same but for the capsule with its radius - skinWidth. for some reason this would create jittering if skinWidth is too high, so if the whole purpose of skinWidth is to stop floating point inaccuracy, then setting it real small (like 0.0001f) would work. still could'nt figure out why it is jittery though. this solution still isn't perfect however, because the collider can still clip through the walls. I suspect this is because my implementation only work on the XZ plane, since I tried sketching it at a top down view private Vector3 CollideAndSlide(Vector3 pos, Vector3 vel, int depth, bool gravityPass) { if (depth > maxBounces) { return Vector3.zero; } if (vel.magnitude < minMoveDistance) { return Vector3.zero; } Vector3 botCenter = pos + transform.up * capsuleRadius; Vector3 topCenter = pos + transform.up * (capsuleHeight - capsuleRadius); float dist = vel.magnitude + skinWidth; RaycastHit hit; if (Physics.CapsuleCast(botCenter, topCenter, capsuleRadius - skinWidth, vel.normalized, out hit, dist, collidingLayers)) { float snapDist = hit.distance - skinWidth; Vector3 snapToSurface = vel.normalized * snapDist; Vector3 leftover = vel - snapToSurface; leftover = Vector3.ProjectOnPlane(leftover, hit.normal); return snapToSurface + CollideAndSlide(pos + snapToSurface, leftover, depth + 1, gravityPass); } return vel; }
4:10 Why scale the projected vector up, if we're just gonna scale it down (based on the angle) anyway? If you leave the code there, it should be so that the straighter you go into the wall, the slower you move which is what we want.
If you just keep the projection of the velocity on the plane, without normalizing and scaling, you'll have a velocity that automatically increases with the angle of the plane to the velocity...
I appreciate your approach and also your general calling out of the people so quick to act as if they know what they're talking about, all across Unity. For as many people as you mention, there are plenty of little "script-bros" who will yell "lol don't use rigidbody at all!" when you ask for help with your dynamic rigidbody player controller. When you ask them why, you usually hear something like "lol I don't even use the physics system much." Anyway, I was wondering if you have any helpful information about crushing that horrible, classic "Unity jitter" when using a first person dynamic rigidbody controller. I've come a long way with mine, but it's by no means perfect. "lol just interpolate the rigidbody bro" is not something that "just works." I've done a few different things along the way, but I'd like to know if you have helpful insights around this, or would like to make a video on it if you do. Thanks.
The 'just use a dynamic rigid body' thing gets on my nerves too. Outside of all the reasons you wouldn't want to do that, number one is that applying forces directly through the physics system in order to code a character movement system is just so wrong and no matter which way you do it is going to end up being unintuitive in one way or another. I find the built in character controller to be 'fine' for my use cases, but a custom kinematic controller is undoubtedly the best and most flexible way to go about it (After all, I assume the built in controller is very similar to a kinematic controller behind the scenes). As far as I'm aware, that would be how it is done in a larger studio, and how it has been done in general for decades.
Yeah, relying on the physics system with forces, friction, dampening, etc, for core mechanics like character movement has always given me "trying to drive a car by pushing it from behind" vibes. Not at all intuitive and certainly less predictable/reliable.
How is using a rigid body and moving it through forces unintuitive? It's literally how things move in real life. Now there are a few "cheats" we need to use to make the physics respond the way players expect them to *feel* because Unity's physics engine is limited in fidelity, namely disabling gravity when grounded (basically assuming the normal force of the ground perfectly counter gravity ) and disabling friction when the player is giving movement input (assuming that kinetic friction is so low it might as well be 0). But otherwise I have found the rigidbody system to be fine for implementing Quake style movement that is used in countless first person games. Not trying to argue. I'm genuinely curious as to your reasons.
@@FakeGuthix01 Nah I get it dw, it's all opinion too. To me it's unintuitive because it's not precise, I'm just giving it a force rather than actually controlling it's velocity precisely. When I'm designing something that needs to be as precise as a character controller, I want to work with the exact values rather than just applying forces and trying to feel out what's right. From a fundamental level, controlled movement is also not what physics rigidbody's are designed for (Hence why they provide the built in character controller to begin with), which causes additional issues. When you have to give your character spring-based floating to work around the issues of rigidbody's, you're fighting against the system that's not designed to do what you're asking it to do. And hey, if it works for you or if in your specific situation that's what you want and works better than other options then more power to you, everything unity offers is a tool you can choose how to use. I just think it's not great general advice to give to people.
If you want your character to move at 5m/s, you could apply a force of ~5, figure out which ForceMode is correct, then realize you never actually reach top speed due to friction, then you want to change how fast you accelerate without changing the top speed, then you realize that instantaneous movement is basically impossible without completely mangling the physics material and rigidbody properties, etc, etc. And then at the end you still might not be moving at exactly 5m/s... Or you could go kinematic and just have a 'moveSpeed' variable with a value of 5 and it just works and you don't have to screw around endlessly with the physics system. You even mentioned workarounds in your question that could be avoided by using kinematic instead. Add in things like jumping, wall sliding, ledge grabbing, etc, and you eventually have a tangled mess of those physics system workarounds that could be accomplished easier without using the physics system at all.
That's a good explanation. Though when I tried to implement this a year ago I eventually gave up and replaced my own solution with Kinematic Controller asset which became free, convenietly It really gets just so much more darn complicated when you start working with stairs, slopes and edge cases. Maintaining "skin width" is probably the most annoying part as PhysX won't correctly do CapsuleCast if you already touch the wall, so you have to maintain little distance
AWESOME!!! It would be really great if you would upload a sample project so that I (or others) can analyze the code and understand it better Tomorrow i will try to implements this in my characterController! Thank you
I‘m really new to unity and also like to search for the perfect player controller,but I habe a few questions(sry if they‘re maybe stupid, like i said im new to unity: do you makw basic movement for the player with a rb and addForce() or another way to add velocity, and if not how do u do it then? And basic collisions with the ground, are they also managed with a rb? Thanks already and this is a really cool vid!
This is good, and I tend to use similar techniques when writing CCs, but the big issue I always face is the collision detection, i.e. the spherecast. Specifically, if any geometry is inside the origin sphere at all, it will return messed up sphere hits for that collision. So if the character ends up clipping into anything, it won't be able to push itself back out correctly. I really need a way to get info about surfaces inside a given sphere. You can use an overlap sphere and then closestpoint on colliders, but it doesn't work with concave mesh colliders.
I've only been able to find one edge-case so far where the collider can clip into a surface. But yeah, it is currently missing the ability to push out of overlapping objects.
i test every position for collision before moving the player there. if the player starts a frame colliding, then they spawned inside a collider, and i allow them to fall through the floor until they start a frame *not* colliding.
Thanks for explaining the basics, I tried tackling this problem on my own but got stuck at the response, the character either stopped too far from the wall leaving a visible gap or for some reason completely passed through it, thanks for the video
Probably want to stick with the iterative approach over the recursive one to make better sense of what mathematical operations you're doing to calculate what you need. While recursion's nice to deal with recursive problems, this is fairly trivial simple algorithm which is easy to transform without recursion. The calculation should naturally terminate anyway assuming your floats or doubles aren't infinite. Removing recursion here can be easily done with a temporary and a check that the new "slide" is or is not a collision. There's just not much reason to use recursion here. It may make more sense to use the "bounces" when the floats are infinite to make sure you can't have an infinite loop (a stack overflow from recursion that terminates an infinite case may be an ok thing for this reason).
I'm working on implementing stairs for the next video and ended up coming to this same conclusion. Recursion was nice when it was *just* collision, but got completely out of hand as I started adding exceptions and features on top of it. So now I've started over, using the improved algorithm from the 2nd paper as a base.
Holy shit, looks like I've come up with collide and slide on my own when trying to improve on a basic character controller tutorial that didn't do what I wanted. Originally I just wanted a wall slide effect but in the end I arrived pretty much exactly here. It felt like nothing special when I wrote it, I totally dismissed it as probably a bad solution since it wasn't in the tutorial but someone else based a paper on this. Huh.
you dont need to acount for the direction your facing to slow your speed on a wall you could just leave the progected vector alone and not scale by the distance of the removed vector to get a very similar result
Hey! I just wanted to mention that there is a small bug with this implementation. When moving into corners, the player can end up passing through because the check to make sure SnapToSurface is more than SkinWidth doesn't take into account the angle in which the player is moving into the obstacle, so sometimes the player ends up inside the skinwidth distance from a wall, letting them pass through. I fixed this by changing the code to: Vector3 SnapToSurface = Vel.normalized * (hit.distance + SkinWidth / Mathf.Cos(Vector3.Angle(Vel, hit.normal) * Mathf.PI / 180)); *Edit: As it turned out, this only fixed being able to climb up corners, clipping through corners by jumping and moving forwards still exists. If anyone knows a fix, that would be much appreciated.
Incredible video, mister Poke, thanks a lot for helping me with dealing with the wall Im bashing against last half a year :) A thing that looks unclear to me - at 07:48 you showed your test sphere moving down at any slope but how is that possible by that moment without gravity checks applied yet? In my backpedalling case my capsule just floats in the air which is fine to my opinion because I (as well as you in the video) only apply XZ movement :) thanks again!
It is nice to see push back on the annoying trend of internet replies which promote not trying and just generally being dumb. Making things from scratch, or making your own version of something, is very powerful. It's really at the heart of engineering, understanding, and creative expression. Thank you very much for promoting people to not just accept the status quo. Reinventing wheels isn't bad when available wheels suck. Instant like and subscribe.
Yep. So much wrong with many of the resources available to beginner game devs. I literally just responded to another comment saying "tHiS iS cOmMoN kNoWlEdGe" and that the video is pointless. So even this guy who DOES know the "right" way doesn't want it to be shared for some reason!!! So much dismissal and gatekeeping all around.
E.X.A.C.T.L.Y
@@poke_gamedev Tbf, the reason so much misinfo is being spread is precisely due to the lack of gatekeeping. People who aren't passionate about solving problems enter the field and just copy-paste code and tell people to do the same. There's nothing wrong with letting people in who are interested, but those who do not fundamentally care should probably be pushed away.
Yes but it's far from making game engine.
@@user-og6hl6lv7pyup but we have then two problems here gatekeeping and copy-pasters, gatekeeping prevents knowledge to be shared and as an extra we need to deal with the flexing, copy-pasters spreads the black box way of thinking should I say ¿? Also the Swept AABB algorithm from gamedev page doesn't works
bro came out of literally nowhere and made one of the most helpful and high quality ive seen in a very long time
This is unreasonably underrated, incredibly high quality content. Keep it up man
the video literally starts with a tale i've been trough. I've been looking up to specifics on the collide and slide in video form for a while man...
@@BleachWizz I don't understand why you didn't just use a rigidbody controller this whole time, literally the same but better. Seems there's 4.9k people who should've just used Google to learn what a rigidbody is lmao
@@appleseedgames6934 we are interested in how it all works. This is how we develop our logic, and we can do something based on this
I'm happy to see you found my old paper useful! Not quite sure I follow the reasoning behind normalizing and re-adjusting the length of the projected vector in the sliding part, but this has been discussed in the comment section already ;) Great presentation and great video though!
ah I picked that up as well, glad I was not the only one
Do you like Frank gambale?
Basically, when you project the velocity vector onto the vector (or plane) of the wall, it's length is only as long as the velocity vector *in the direction along the wall.* If he normalizes it, converting it to a unit vector (with length of 1), and then scales it by the length of the original velocity vector, he retains the full movement of the velocity vector but in a direction along the wall instead of into it.
@@ghostbusterz It's more accurate to keep the rejection vector as-is. By normalizing and subsequently scaling, the object will always move at full speed when grinding against a wall because the surface normal will almost never be perfectly perpendicular to the object velocity.
@@1234macro In video games we don't always want accuracy. It would be more physically accurate but that's not always what's most important for a given game's design.
Update 2: Ok so Unity is currently imploding... I was working on refactoring and adding stair detection, but I might take a detour and learn Godot now lol
As someone in the comments pointed out, maintaining full velocity on slopes is not very physically accurate, so I thought I should clarify that all the fancy scaling I'm doing is specifically meant for *character movement*, and should be ignored if using this code for a more general physics engine!
Again, thanks for watching, and I hope you found this video helpful!
And stairs?
It looks like that extra scaling with dot product ends up simulating the thing you already would get by projected slide. Although, it gives a control if you want to maintain full velocity.
Also, In the place of a situation with a next wall, there is also an opposite problem - overshooting a corner.
That would convince me, if you didnt scale it again based on the angle between the wall and surface. You'd get that for free if you didnt scale the projected vector. Its also what happens irl minus the friction
im ready for waves of godot tutorials 🤤
I would like to see the next part with the stairs detection 😅
good video! just wanted to point out that normalizing the vector at 4:00 only to multiply it by the dot product at 9:05 is unecessary since it reverses the normalization (it's like if you would multiply a number by 10 and then divide the result by 10, the second operation reverses the effects of the previous one). if you only want that "sliding along the wall" effect you can skip both steps to get the same result cheaper.
Was gonna come to say this ^
I believe that's also part of the original paper. The slower sliding along the wall when more perpendicular appears naturally as a result of projecting the movement vector along the normal plane.
He makes a distinction between horizontal and vertical movement so no you do not get the same result
U only move in horizontal tho
@@TheChucknoxus but why not just not do the scaling at all?
Thank you
I have been on the receiving end of the dismissal quite often, when to me, yes, I could use the premade CharacterController or Rigidbody that does "everything for me" but like... I don't learn from that. All I learn is how to control that specific component, not how a Character Controller *actually* functions. Not how Collision *actually* functions.
Most these people couldn't make a game without the use of these tools. Which is fine, the tools are there so it's fine to use them, but I am tired of the dismissal when someone is genuinely interested into just learning "what's under the hood"
Yeah, not to mention that in Unity, the built-in character controller is just... not very good lol. It's honestly one of the main things Unreal and Godot have over Unity, and is definitely why the "uSe DyNaMiC rIgIdBoDy" suggestion pops up so often. People know it sucks but can't be bothered to actually find a better way.
@@poke_gamedevyou're so right. Not being able to do anything on any other axis than the up axis in Unity with the CharacterController is so weird, you'd think they would've fixed it by now
For Godot users: This is said in the documentation but, if you ever want to do this in Godot (with that I mean don't want to use the built in move_and_slide function), the engine provides the method move_and_collide() that does the collision casting + the movement, and the Vector2/3.slide() function to calculate the reaction movement.
also, the move_and_slide, and move_and_collide both handle Collison overlapping so it will push out of a clipped surface.
Wait , isn't the move_and_slide_on_slops function supposed to do this work for us ?
@@mohamedmoh5789 I meant if you want to implement the movement yourself. Which is better if you want to have more control over the physics of the game.
Doesn't work so good unfortunately. It sort of breaks and introduces horrid jitters if you introduce complex geometry.
@@user-og6hl6lv7p What about when using the Jolt physics plugin?
This video probably would have helped me out tremendously a few years ago. I self taught myself Unity and refused to use anything anyone else made, so I spent probably a good year perfecting the movement in my own game, trying as hard as I could to figure out the stuff you're describing in this video. I eventually did it with a slightly different method, and I don't regret it because it ultimately taught me so much about the way things work, between time, frames, how acceleration, velocity, friction, etc work, but it's good that nobody else will have to go through the hell that I did.
The issue you have described around the 10:30 mark ended up with my character getting their head stuck on any negative angle ramp collision, and at a certain point, actually ended up making them able to walk up the ceiling like a spider. I don't remember what I did to fix it, but I'll have to check out the paper on it.
This is easily one of the best videos about movement and collision I've seen on youtube.
such good visualization of vector math!
First of all, this video slaps, no question. I was struggling for some number of hours creating a controller for exploring a 2.5d top down map with potentially sloping terrain. I found solutions for moving on slopes, but there were always problems addressing steep slopes (among other things). This is the first video I've found that addresses those problems while still using a similar algorithm for moving up slopes.
The one gripe I have is I don't like when people say something is the "right way" or "actually good" when it comes to programming, because there are multiple valid ways of doing things, and some people don't always have the time to implement everything from scratch. The Unity character controller addresses these problems and has parameters for slope limit, skin width, etc. It's a perfectly valid way of doing things. I just didnt want to use it in this instance because our game had potentially unique requirements, and I wanted to understand the math myself anyway.
To anyone who's implemented this system:
I am having an absolute hell of a time with glancing collisions. The first iteration consistently takes the player a little too far into the wall and results in the function getting stuck recursing, and for the life of me I cannot figure out why.
I'm doing this in DOTS, so I don't have the luxury of a collider cast with a distance function. Instead I get a fraction. I suspect the problem is with how I'm performing the math for snapToSurface, which I've adapted to function with a fractional value like this:
float3 snapToSurface= (vel * (hit.Fraction - (0.015f / (math.length(vel) + 0.015f))));
The logic here is that math.length(vel)+0.015f is the expression that defines the original cast length, so I'm finding the equivalent of 0.015 in fraction-space, and subtracting it that way. That seems to work, but only at steep angles. Once the toward-the-collision component of velocity becomes low enough, I get stuck. I can only guess snapToSurface is ending up with too high of a value somehow, but I can't figure why.
It also occured to me to calculate it this way:
float hitDistance = (math.length(vel) + 0.015f) * hit.Fraction;
float3 hitSurface = math.normalize(vel) * (hitDistance - 0.015f);
but the results are the same.
Anyone encounter a problem like that in their travels?
Addendum - I've solved the problem. For others dealing with the same situation:
This implementation seems to be missing a detail preventing infinitely recurring collisions on stationary colliders. This implementation deals with the problem by casting a skin width worth of extra distance ahead and then subtracting this value back out of the result, leaving the character controller against the wall plus the separation defined by skin width. The problem with this is it operates along the character's velocity vector, which, at very shallow angles, might have a component normal to the wall of nearly zero. Skin width will scale with that component, so if you have a glancing enough collision, the collider remains in touch with the wall and will collide infinitely. The effect of this is very obvious if you use a very large skin width and then run directly at a wall and then slowly slide off of it. You will see that the huge skinwidth holds you a long distance from the wall at first, but that this distance gets shallower as you turn. At extremes, this causes the stuck problem.
I am not sure why the problem does not manifest in this video. My best guess is it's a difference between stock unity physics and Unity's entity physics implementation. My guess is most other engines will also present the problem as I experienced it. My solution to the problem is this:
Rather than increasing our cast distance by skinwidth and subtracting it off the end, we cast along velocity directly. Then, if an impact is detected, we calculate snapToSurface a bit differently:
float3 snapToSurface = (math.normalize(vel) * (hitDistance)) + (hit.SurfaceNormal*skin);
This performs the same separation job, but relative to the wall normal rather than the character's velocity. One thing to note though is that you should probably scale your skinwidth with the magnitude of velocity when using this approach, otherwise you may bounce off the wall due to a skin width result that exceeds the velocity pushing you into the wall. You could also simply use a value of skinwidth that will always be less than the minimum velocity that could be applied.
I have roughly 5 years of pretty much daily experience with Unity and C#, and I still learned a lot in this video. Awesome stuff!
Dude, this is THE most underrated channel I've ever seen. Top notch content, will be using this, and I subscribed
Finally! Someone who understands my frustration with character controllers. Lack of interpolation between fixed update steps in the built-in one is by far the biggest drawback for me.
Great video! I've never considered a recursive approach, I always go iterative.
So glad someone answers this question properly, a year or two ago I couldn’t find any help on the matter and came up with my own hacky but functional solutions
I've written many many character controllers over the years and this one is pretty top quality for how simple it is. In fact, it solves an issue I currently have which is that the controller I designed for my player is very fully featured but a bit too heavy-weight to use at scale. However, I needed a character controller that functioned similarly for NPCs. This little function saved me at least a day of tearing out chunks of some of my own controllers to get something that functioned well and was still resonably performance. Great video!
And could you explain to me some things (I'm just a beginner, and in some places I may not understand). Does everything work purely on colliders, without using RidgedBody? I also didn’t understand where he got the variable "bounds" and "layerMask". I will be very grateful)
@@ReimsForYou Yeah, there are a few minor things like I noticed too. The bounds are obtained from the collider(s) you would be using for your character - in most cases a single sphere or capsule, though in theory you could also use extend the bounds to include multiple colliders. In my case I'll probably go with a public Collider field in the MonoBehaviour that allows me to pass a reference to that collider. Then in Awake() I'll calculate the bounds and extend it by the skinWidth (the code for that is shown very early in the video). Then you can simply assign 'bounds' to 'MyCollider.bounds' within the CollideAndSlide function and it should just work. 'layerMask' is a similar deal. Just make it a public field and then you can assign whatever layers you want in the inspector. This code appears to move this object via its transform which implies that the collider would be a trigger and there would be no rigidbody attached. Likely in my case I'll end up attaching a rigidbody, marking it as kinematic, and keeping the collider as-is rather than making it a trigger and then moving the object via the Rigidbody's position value instead since I'll be needing physical interactions such as pushing other things around. Also, turning off the grabity for the rigidbody would be a good idea if you intend to use the built-in gravity this example provides.
@@ReimsForYou he has a collider on the body (sphere collider in this eg). The collider has a variable called bounds you can access from collider.bounds as shown in the video. It is there for the Sphere Cast. As for layermask, you can set a specific layer for collision check when sphere casting say you want to check if your character is only colliding with a specific layer eg. Layer 8 ur layer mask value will be 2
good stuff, used this for a lightweight 2d ECS character controller
here before this blows up
Let's see how it goes
Lettuce see how this goes
Can i stand here with you
yea
here while this is blowing up
Yes! Thank you for helping more game devs break out of their rigidbody crutches!
great help. I managed to recreate exactly this in Unreal, using Blueprint. Works great for having the camera slide at the edge of the map in an RTS game
Underrated. I see this is the first video in this channel so, no hopes are gone. Expect to pop off!
I've been trying for months to create a 3d engine on my own and came almost with the same conclusions as this video, but with some exceptions. I'll borrow some things I've missed and if it works you'll have my eternal gratitude :D
Arguably one of the most well presented tutorials Ive ever seen. Fantastic job!
This is exactly the thing I was working on myself and came as close as a hair's width to figuring out before switching to simply using velocity-based rigidbody controller. I didn't go as deep as reading an actual SCIENCE PAPER, though. Have been waiting for a continuation of this for quite a while and if you're still going to do this - it'd be incredibly interesting to see how you would solve stairs and slopes in a controller like this. Cheers!
Thanks, this made my movemant way better
Awesome video! Welcome to RUclips! Subbed 💛
I remember searching for this when first studying game dev years ago, it was incredibly infuriating the amount of trash replies you'd find. Thanks for this, I wish this video was around when I first started
I like how this video teaches you why it does instead of only how it does
nice video, this is probably gonna get boosted by the algorithm
It's funny how I tried to make a character controler on my own quite a while ago and was smart enough to came up with this, but stupid(inexperienced) enough to fuck up when coding it, your vid makes me want to come back to that project and try again
Poke Dev, you are amazing. Poke Dev please continue posting videos.
Just writing your name so the algorithm can push you up. Damm, this video was amazing. From the editing to the content, it was so nice to watch I was thinking I was watching a Million subscribers youtube channel.
Keep it up and you are going to make it far!
Well well well if it isn't the exact video I was attempting to find two months ago. I'm pretty sure normalizing the redirected vector and multplying by the magnitude is the piece I was missing and looking for someone to tell me.
Nice job on this video, it's high quality.
Yes, there was shockingly little info about it online! When I found Fauerby's paper I knew I had to make a video.
@@poke_gamedev Something you might want to consider in future content is that parameters like "gravityPass" both presuppose that the game will have gravity and don't adequately explain what the effect of that parameter is. It requires the reader to fully read the method body before being able to use the method which is not always what you want from a premade solution. Something like "shouldSlideOnSlopes" might be easier to use.
I agree it could be more descriptive, however the way I wrote the function was as a private method to be called inside a larger character controller class. So in my case, someone using the character controller class wouldn't have any reason (beyond curiosity) to know the exact implementation for collision, as this method wouldn't even be exposed to them.
NO, please don't do that part! It doesn't make any sense to normalize and rescale it, and just leads to the problem at 8:50. That part in the video is just wrong, so please don't copy it. In real life it does lose magnitude, so it is not a problem.
Really nice overview! Even though I would personally keep the velocity decrease on hitting an edge instead of decreasing later on. Saw your pinned comment on this too. Thank you!
hit and run? nah, I prefer collide and slide.
Thank you so much! I was looking for a solution when I was working on my own movement controller, and adding a speed boost would thrust me through geometry.
I was making a custom physics engine for unity. I gave up on it because the collisions and the slopes just did not work. I did not delete the code just in case I needed part of it because I added a lot to it. I saw this video and I was like "Hmm this looks interesting." so I watched it then I added the code to my project, and it did not work. the reason why is because I had the old code and this new code running at the same time. but I got it to work after about 30 min of trying. THANK YOU! for the video!
"did not delete the code" if you're not using version control, you should be. Seriously, it's worth the pain of learning (and actually with GitHub Desktop it's not even much pain)
I was toying around with this a while back but I was having a miserable time finding good sources of information for it, as comprehensive as this anyway. How are you able to find all this information so easily? I even consider myself pretty good at googling.
Well done btw this video will blow up soon so remember me when you become famous ❤!
The presentation is awesome. I wish I was just a bit smarter and understood this. Maybe one day.
Thanks bro for the link to the material, it helped me a lot.
Right on time. I was just doing collisions today (for my game-engine-less game). Guess I'll be rewriting it tomorrow after watching this video lol. Anyways, this is just what I was looking for and couldn't find. Thanks!
194 Subs? How, this has great video quality.
To anyone who tries to implement this using a box collider:
Don't divide the algorithm up into horizontal and vertical passes if you plan to have sloped floors in your game. Doing this will occasionally "miss" a collision detection and pass the player straight through floors.
My solution to this problem was to only use one "pass" with the entire velocity vector, not separated into horizontal and vertical passes. And if the floor was angled within a walkable steepness, I would set the leftover vector3's y value to 0. This will stop the player from slowly sliding down slopes that he should be able to walk on, but would still slide him down along a steep wall.
The only video to appear on the subject that actually looks at the real problem, and everything you showed us in this video is exactly what I came to as a conclusion in my own testing over the past 2 years back and forth.
Everyone else, just says use Rigidbodies or the built-in char controller, as you clearly showed as well.
I have a slight different approach to mine, but the resulting vectors and movment is exacly the same, besides some trigonometry approaches to corner collisions.
Another slight issue I have found with this, using raycasts, is that it more often than not sets itself inside the geometry of the world, thus the raycast does not report any hits for that collider. But I guess that the skin width solves that problem fairly okay-ish.
Thank you bro, your video is really helpful and easy to understand.
Great explaination! With the help of your video i implemented the algorithm in a custom godot character controller. Right now i only use a raycast, but i will transfer the logic to shape casts, which is the equivalent of the sphere cast in unity.
Awesome! Godot actually has a lot of this functionality built-in to its CharacterBody3D class (move_and_slide(), or something), which is what I would start with if I were to make the switch :)
@@poke_gamedev in the end I went with the built in move and slide function. It got me very far into the controller without many adjustments. I use it for a skateboarding game.
great explanation
This was life changing 😮
The explanations and production value of this video is off the charts! Awesome video :) thanks
Truly underrated
Oh my god, why couldnt this video have existed years ago? As someone who tends to become obsessed with doing things the "correct" way, every time I've gone to make a game I get a bit of the initial setup done, then get fixated on the character controller for weeks, unhappy with most of what I see online.
Yeah I've gotten stuck in the character controller rabbit hole like 5 times now lol
This is incredible video quality! Needs to be seen by more people, hope you get the attrntion you deserve
Man this is such a well made video, genuinely got better quality than me-whos been doing yt for a year, on your 2nd video????? You are going places
I'm quite late to this but there is an issue with your implementation, in the sphere cast params, you should put the original velocity as the direction not the normalized version of it, this is to ensure that the cast covers the full distance you'll cover in that same frame, this is needed when moving at higher velocities so you don't tunnel into walls.
Other than that, this video is amazing, I'm currently working on my collision handler for my character controller and this helped tremendously, can't thank you enough ♥
I can't seem to figure out why it drags going uphill at times, but the video taught me a lot.
I think Kasper's stuff was inspired by Paul Nettle's stuff (actually, it was, just checked the references in Kasper's doc). I remember this stuff back then, did an implementation of it myself. I seem to recall one of the problems with Paul Nettle's implementation (and perhaps Kasper's original implementation) was that clipping the side of a ramp at a very shallow angle would clip through/get stuck. Imagine you wanted to go straight up the ramp was off to one side too far and just missed it. It's been 20+ years, so things may be a little fuzzy.
I never followed through with an implementation of the improved version.
I'll probably dig back into this topic again in the near future.
how does this have barely attention bro, this vid is extremely useful
It's not exactly a light watch
I just started working on my physics engine, thank you for this great explanation!
Glad it was helpful!
A small thing to keep in mind if you want to use this for a more general physics engine, is that the thing at 4:04 where they rescaled the vector is not physically accurate. You should just leave it as is with the shorter length. Then you also don't need to do what they did at 8:50, which they only did to compensate for the rescaling earlier.
Those things in the video really only apply to character physics specifically, since it handles walking up or down slopes inaccurately, which feels better in some situations.
@@Zicrus
yeah thanks, This will only be helpful for CharacterBody and KinematicBody structures. I should make a move and slide function for them to make it easier for game devs. But for other physical structures like RigidBody I’m using more accurate approaches for collision responses.
Absolutely fantastic! Please keep making videos.
Critically underrated
Also in collide and slide is really easy to implement friction
Instead of scaling the projected vector to be the same magnitude as the leftover velocity, and then scaling it again based on the angle to the perpendicular, can we not just use the projected vector as is? Will the projected vector naturally contain the desired perpendicular component if we wish to slow down our character? I've not implemented this myself yet, so it's a genuine question. Thanks.
this was my thought. I haven't done the math but i believe it would give the same result and he's just taking an extra step for no reason. Although you could make the case that once you have the vector normalized you can tweak the on-wall velocity to your liking
THANK YOU SO MUCH
Something is off. I followed the tutorial thoroughly and tried implementing the algorithm for a capsule shaped character. Collisions were fine on the surface, but when I had the character collide with tent shaped geometry (two stretched boxes tilting toward each other, and oriented so that the further inside you go, the lower the ceiling is), it started clipping through the tent, unless you head into the tent straight on. any horizontal movement is going to cause clipping. I figures it is with the skinWidth doing some goofy stuff like pushing the player away from the colliding object when the object is inside the skinWidth. can anyone help?
Edit: Another thing that may proves this has something to do with skinWidth is when I set the skinWidth to something like 0.45f, then move at an angle that is almost paralel to the surface, instead of the moving the character a skinWidth distance away from the wall, it just slowly inches towards the wall, until it the distance between player and wall reaches the radius of the capsule cast (aka capsuleRadius - skinWidth), as if "snapDist = hit.distance - skinWidth" have very little effect. When looking and moving at the wall at an angle almost perpendicular, this behavior doesn't happen}
Edit 2: Found out the hard way that just doing "snapDist = hit.distance - skinWidth" isn't correct at all. the closer to getting perpendicular to the surface's normal, the more you'd have to subtract from hit.distance. so here's the proposed solution:
float bigHypotenuse = capsuleRadius / Mathf.Sin((90 - Vector3.Angle(hit.normal, -vel.normalized)) * Mathf.Deg2Rad);
float smallHypotenuse = (capsuleRadius - skinWidth) / Mathf.Sin((90 - Vector3.Angle(hit.normal, -vel.normalized)) * Mathf.Deg2Rad);
Vector3 snapToSurface = vel.normalized * (hit.distance - (bigHypotenuse - smallHypotenuse));
where as bigHypotenuse is the length between the big capsule center (aka the capsule without any skinWidth) at the position where it is contacting the hit point, and the point where the velocity would hit if raycasted (very much oversimplified, my wording is very off). smallHypotenuse is the same but for the capsule with its radius - skinWidth.
for some reason this would create jittering if skinWidth is too high, so if the whole purpose of skinWidth is to stop floating point inaccuracy, then setting it real small (like 0.0001f) would work. still could'nt figure out why it is jittery though.
this solution still isn't perfect however, because the collider can still clip through the walls. I suspect this is because my implementation only work on the XZ plane, since I tried sketching it at a top down view
private Vector3 CollideAndSlide(Vector3 pos, Vector3 vel, int depth, bool gravityPass)
{
if (depth > maxBounces)
{
return Vector3.zero;
}
if (vel.magnitude < minMoveDistance)
{
return Vector3.zero;
}
Vector3 botCenter = pos + transform.up * capsuleRadius;
Vector3 topCenter = pos + transform.up * (capsuleHeight - capsuleRadius);
float dist = vel.magnitude + skinWidth;
RaycastHit hit;
if (Physics.CapsuleCast(botCenter, topCenter, capsuleRadius - skinWidth, vel.normalized, out hit, dist, collidingLayers))
{
float snapDist = hit.distance - skinWidth;
Vector3 snapToSurface = vel.normalized * snapDist;
Vector3 leftover = vel - snapToSurface;
leftover = Vector3.ProjectOnPlane(leftover, hit.normal);
return snapToSurface + CollideAndSlide(pos + snapToSurface, leftover, depth + 1, gravityPass);
}
return vel;
}
my goat
this should be very helpful for my botw inspired controller
Amazing! Thank you!
4:10 Why scale the projected vector up, if we're just gonna scale it down (based on the angle) anyway? If you leave the code there, it should be so that the straighter you go into the wall, the slower you move which is what we want.
If you just keep the projection of the velocity on the plane, without normalizing and scaling, you'll have a velocity that automatically increases with the angle of the plane to the velocity...
I appreciate your approach and also your general calling out of the people so quick to act as if they know what they're talking about, all across Unity. For as many people as you mention, there are plenty of little "script-bros" who will yell "lol don't use rigidbody at all!" when you ask for help with your dynamic rigidbody player controller. When you ask them why, you usually hear something like "lol I don't even use the physics system much."
Anyway, I was wondering if you have any helpful information about crushing that horrible, classic "Unity jitter" when using a first person dynamic rigidbody controller. I've come a long way with mine, but it's by no means perfect. "lol just interpolate the rigidbody bro" is not something that "just works." I've done a few different things along the way, but I'd like to know if you have helpful insights around this, or would like to make a video on it if you do.
Thanks.
Fantastic video and explanation.
Exactly the Kinematic Character Controller, the one i'm using in my game, it's too complicated, but im trying my best to understand it
The 'just use a dynamic rigid body' thing gets on my nerves too. Outside of all the reasons you wouldn't want to do that, number one is that applying forces directly through the physics system in order to code a character movement system is just so wrong and no matter which way you do it is going to end up being unintuitive in one way or another.
I find the built in character controller to be 'fine' for my use cases, but a custom kinematic controller is undoubtedly the best and most flexible way to go about it (After all, I assume the built in controller is very similar to a kinematic controller behind the scenes). As far as I'm aware, that would be how it is done in a larger studio, and how it has been done in general for decades.
Yeah, relying on the physics system with forces, friction, dampening, etc, for core mechanics like character movement has always given me "trying to drive a car by pushing it from behind" vibes. Not at all intuitive and certainly less predictable/reliable.
How is using a rigid body and moving it through forces unintuitive? It's literally how things move in real life. Now there are a few "cheats" we need to use to make the physics respond the way players expect them to *feel* because Unity's physics engine is limited in fidelity, namely disabling gravity when grounded (basically assuming the normal force of the ground perfectly
counter gravity ) and disabling friction when the player is giving movement input (assuming that kinetic friction is so low it might as well be 0). But otherwise I have found the rigidbody system to be fine for implementing Quake style movement that is used in countless first person games.
Not trying to argue. I'm genuinely curious as to your reasons.
@@FakeGuthix01 Nah I get it dw, it's all opinion too. To me it's unintuitive because it's not precise, I'm just giving it a force rather than actually controlling it's velocity precisely. When I'm designing something that needs to be as precise as a character controller, I want to work with the exact values rather than just applying forces and trying to feel out what's right. From a fundamental level, controlled movement is also not what physics rigidbody's are designed for (Hence why they provide the built in character controller to begin with), which causes additional issues. When you have to give your character spring-based floating to work around the issues of rigidbody's, you're fighting against the system that's not designed to do what you're asking it to do. And hey, if it works for you or if in your specific situation that's what you want and works better than other options then more power to you, everything unity offers is a tool you can choose how to use. I just think it's not great general advice to give to people.
If you want your character to move at 5m/s, you could apply a force of ~5, figure out which ForceMode is correct, then realize you never actually reach top speed due to friction, then you want to change how fast you accelerate without changing the top speed, then you realize that instantaneous movement is basically impossible without completely mangling the physics material and rigidbody properties, etc, etc. And then at the end you still might not be moving at exactly 5m/s...
Or you could go kinematic and just have a 'moveSpeed' variable with a value of 5 and it just works and you don't have to screw around endlessly with the physics system. You even mentioned workarounds in your question that could be avoided by using kinematic instead. Add in things like jumping, wall sliding, ledge grabbing, etc, and you eventually have a tangled mess of those physics system workarounds that could be accomplished easier without using the physics system at all.
Very good i like this alot
That's a good explanation. Though when I tried to implement this a year ago I eventually gave up and replaced my own solution with Kinematic Controller asset which became free, convenietly
It really gets just so much more darn complicated when you start working with stairs, slopes and edge cases. Maintaining "skin width" is probably the most annoying part as PhysX won't correctly do CapsuleCast if you already touch the wall, so you have to maintain little distance
Great video
AWESOME!!!
It would be really great if you would upload a sample project so that I (or others) can analyze the code and understand it better
Tomorrow i will try to implements this in my characterController! Thank you
awesome!
Thanks for the high quality content, keep going!!
would love to see more videos like this
This is a great video, but i gotta say the way you formatted your spherecast conditional lines is pretty insane haha
THIS IS AWESOME
I‘m really new to unity and also like to search for the perfect player controller,but I habe a few questions(sry if they‘re maybe stupid, like i said im new to unity: do you makw basic movement for the player with a rb and addForce() or another way to add velocity, and if not how do u do it then? And basic collisions with the ground, are they also managed with a rb? Thanks already and this is a really cool vid!
This is good, and I tend to use similar techniques when writing CCs, but the big issue I always face is the collision detection, i.e. the spherecast. Specifically, if any geometry is inside the origin sphere at all, it will return messed up sphere hits for that collision. So if the character ends up clipping into anything, it won't be able to push itself back out correctly. I really need a way to get info about surfaces inside a given sphere. You can use an overlap sphere and then closestpoint on colliders, but it doesn't work with concave mesh colliders.
I've only been able to find one edge-case so far where the collider can clip into a surface. But yeah, it is currently missing the ability to push out of overlapping objects.
i test every position for collision before moving the player there. if the player starts a frame colliding, then they spawned inside a collider, and i allow them to fall through the floor until they start a frame *not* colliding.
Love this. Super useful 💪🏽
Thanks for explaining the basics, I tried tackling this problem on my own but got stuck at the response, the character either stopped too far from the wall leaving a visible gap or for some reason completely passed through it, thanks for the video
very nice
aight man your amazing video earned you a new subscriber
Probably want to stick with the iterative approach over the recursive one to make better sense of what mathematical operations you're doing to calculate what you need. While recursion's nice to deal with recursive problems, this is fairly trivial simple algorithm which is easy to transform without recursion. The calculation should naturally terminate anyway assuming your floats or doubles aren't infinite. Removing recursion here can be easily done with a temporary and a check that the new "slide" is or is not a collision. There's just not much reason to use recursion here. It may make more sense to use the "bounces" when the floats are infinite to make sure you can't have an infinite loop (a stack overflow from recursion that terminates an infinite case may be an ok thing for this reason).
I'm working on implementing stairs for the next video and ended up coming to this same conclusion. Recursion was nice when it was *just* collision, but got completely out of hand as I started adding exceptions and features on top of it. So now I've started over, using the improved algorithm from the 2nd paper as a base.
Thanks! Editing must have taken a good while - neat visualizations.
This shit is real, keep it coming!
Ypur work is incredibly good!, thakn you so much for leaving this piece of education for free!!!
Sadly unity became a greedy mess. R.I.P.
Awesome video man, mind telling me (or someone who knows) what do you use to create the animated slides and graphics?? Thanks!
I used Motion Canvas!
motioncanvas.io/
Holy shit, looks like I've come up with collide and slide on my own when trying to improve on a basic character controller tutorial that didn't do what I wanted. Originally I just wanted a wall slide effect but in the end I arrived pretty much exactly here. It felt like nothing special when I wrote it, I totally dismissed it as probably a bad solution since it wasn't in the tutorial but someone else based a paper on this. Huh.
you dont need to acount for the direction your facing to slow your speed on a wall you could just leave the progected vector alone and not scale by the distance of the removed vector to get a very similar result
Good video. Keep it up brother.
Hey! I just wanted to mention that there is a small bug with this implementation. When moving into corners, the player can end up passing through because the check to make sure SnapToSurface is more than SkinWidth doesn't take into account the angle in which the player is moving into the obstacle, so sometimes the player ends up inside the skinwidth distance from a wall, letting them pass through. I fixed this by changing the code to:
Vector3 SnapToSurface = Vel.normalized * (hit.distance + SkinWidth / Mathf.Cos(Vector3.Angle(Vel, hit.normal) * Mathf.PI / 180));
*Edit: As it turned out, this only fixed being able to climb up corners, clipping through corners by jumping and moving forwards still exists. If anyone knows a fix, that would be much appreciated.
good video
Incredible video, mister Poke, thanks a lot for helping me with dealing with the wall Im bashing against last half a year :) A thing that looks unclear to me - at 07:48 you showed your test sphere moving down at any slope but how is that possible by that moment without gravity checks applied yet? In my backpedalling case my capsule just floats in the air which is fine to my opinion because I (as well as you in the video) only apply XZ movement :) thanks again!