Search Unity

The Proper Way to Control the Player?

Discussion in 'Physics' started by Cybearg, Sep 4, 2016.

  1. Cybearg

    Cybearg

    Joined:
    Jan 18, 2015
    Posts:
    46
    I've seen plenty of opposing recommendations for how to move the player, but I'd like to hear which way is recommended by people with experience (preferably someone with several years of experience or at least one non-hobbyist published game that uses said method).

    Specifically, dealing with movement and jumping. I've seen several recommended ways of doing so:
    • Don't use Unity's physics at all and instead position the player using position.transform.Translate() (or by setting the transform value directly). Then, several raycasts are used to ensure the player doesn't move excessively in any particular direction. Now, I know that this is a bad idea because it means lots and lots of raycasts in Update(), while I've read that it is recommended not to use ANY raycasts in Update or FixedUpdate() due to performance issues.
    • Set the rigidbody's velocity directly, so every update the horizontal velocities (X and Z) is cleared and then re-set depending on what directional key the player is holding. Jumping is also performed by setting the vertical velocity just once to perform the jump. This set-up works fairly well, but it creates problems when you're moving against a wall while jumping or falling; namely, your character will "stick" to the wall until you release the movement key.
      • I've read several recommendations to resolve this issue by applying a Physics Material with friction set to 0 on all colliders, but this becomes a problem whenever two physics objects interact, such as one character bumping another character. Without friction, a bump can send a player sliding around the ground like it's made of ice. Since this solution requires friction to basically not exist in the game, it strikes me as a sloppy band-aid solution that doesn't resolve the real source of the problem.
    • Move by calling rigidbody.AddForce() for movement and for jumping. This seems to be the best solution, except AddForce() seems to demand arbitrarily large forces to be applied, and it's unclear why. While something like rigidbody.velocity = Vector3.forward * 10; will scoot the player forward at a decent clip, rigidbody.AddForce(Vector3.forward * 10); results in essentially no visible movement whatsoever.
      • One may say, "just increase the multiplied force value until it looks good :D", but that won't suffice; I'm using a formula to calculate the proper jump height based on how many units I want the player to reach at the apex of the jump. It works perfectly if I was setting rigidbody.velocity directly, but it doesn't lift the player off the ground at all if I'm calling rigidbody.AddForce() with the same value.
    To put a stop to all this confusion, what's the real way this should be done? Something that allows for precise, tight controls while also allowing flexibility depending on how I'd like the game to feel. Please provide justification for your answer; explain why your way is better than other ways, don't just spread a personal bias that has no reason beyond habit.

    P.S. I'd rather not use the built-in Character Controller because I want to learn how this works under the hood so I'm not reliant on black box solutions.

    Hopefully others who find the contrary results of a Google search for how to approach a player controller will find such answers useful as well.
     
    mateusdasilvarusario likes this.
  2. Geckoo

    Geckoo

    Joined:
    Dec 7, 2014
    Posts:
    144
    Personnaly I prefer a controller character without RigidBody, but one thing is certain - it is not always realistic - and especially behavior with other elements in scene. Here you had exposed two different points of view and I assume that the most important is to keep in mind - all depends to your project. Sometimes, one alternative is better than the other - and sometimes you should chose the opposite. No logical rules ++
    PS : here you could find my project which uses a custom character controller without any rigidbody - really smooth :
    https://geckoo1337.itch.io/azymuth
     
  3. Cybearg

    Cybearg

    Joined:
    Jan 18, 2015
    Posts:
    46
    I'm not really shooting for realistic at the moment, and I'm aware that things vary some on a case-by-case basis, but the problem is I don't know of a solution that meets my needs, period. As I listed above, I have issues with all the possible solutions, so I want to know what most people are using, if there is another way I'm unaware of, or if I'm doing something wrong in my implementations, hence my issues.

    To reiterate, I know of three (practical) ways to make a character move:
    1. transform.Translate()
    2. rigidbody.velocity = ...
    3. rigidbody.AddForce()
    ... Which in turn have the following problems (respectively):
    1. significant performance loss due to constant raycasting (and the awkwardness of working around Unity's opinionated physics-based solutions)
    2. physics issues such as sticking to walls and not falling (presumably due to the player's collider intersecting, then being pushed out, then intersecting again, rinse & repeat)
    3. the expected force to perform work seems arbitrarily and unpredictable (an issue when I want values to clearly indicate what they're for: a horizontal value of 5 should move the player 5 units per second, while a vertical value of 2 should move the player upward a max of 2 units before gravity pulls them back down again)
    I'm looking for solutions to problems 1, 2, and 3 or alternate character movement strategies.
     
  4. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    Everyone does things differently, so I can't cite a "proper" way really, but instead give you some idea what's out there and how I've done things in the past.

    So I'm going to answer this operating on the idea that a Character Controller is an object of sorts that allows you to A) move it around somehow and B) when it hits a wall it gets pushed out of the wall somehow, preventing the character from going through objects. There's lots more to making tight characters, but those are the essentials. So let's evaluate the options based on that:
    • Rigidbodies. They allow you to precisely move the character by manually setting the velocity. This is good! Unfortunately, like you said above, the way they get pushed out of objects can be troublesome. Sometimes they stick to walls, sometimes they don't slide properly, and so on. All around this makes for an unpleasant experience for a player.
    • CharacterController component. Unity (PhysX) has a special physics object that is built specifically for characters. This can be moved precisely using the Move() method. When it collides with objects, it slides nicely across them (for the most part). Overall, it does a pretty good job. If you're just starting with game development and Unity and you need a 3D solution to character controlling, use this one.
    • Modifying the transform's position manually. This works, but your object will just go through walls. To get around that, you'd need to manually do collision testing against all objects in the world. I made a controller that does this, and HiddenMonk recently made one too. These are open source and can be modified to do whatever you want, but are more complex to work with than the built in CC. I also wrote a bunch of articles on my website detailing how I made it.
    How to make a smooth controlling character is much more about design than math (although you'll need lots of math!). Deciding exactly how the character moves up and down slopes, across walls and so on really impacts the design. I dunno how experienced you are with Unity, but if you want an intro to the engine based around making a character I wrote some tutorials.

    Finally, if you're making a 2D controller, forget everything I said and just do all the math yourself to start. If you google around you'll be able to find out how to easily do 2D collision checks and whatnot, and it can be fun.
     
    JB3D, JoeStrout and Cybearg like this.
  5. Cybearg

    Cybearg

    Joined:
    Jan 18, 2015
    Posts:
    46
    Thank you very much for the detailed response and the links. Since you didn't mention it, I take it then that moving with rigidbody.AddForce() just doesn't really happen, then?

    I believe I've read somewhere that it's recommended one doesn't use physics-based solutions for movement when the goal is tight controls, and rather one of the more complicated homebrew solutions is preferred. Would you agree with that, and does it apply equally to 3D and 2D, or mostly just the latter?

    Is there any knowledge of what is inside of Unity's 3D CharacterController or is it entirely a black-box with no inkling of exactly how it manages to move the character without the issues that come from setting rigidbody.velocity?
     
  6. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    I meant to add: the reason AddForce doesn't seem to rocket your player around is that you're likely not using the desired ForceMode. If you want to add an instant force, use ForceMode.Impulse.

    As far as using forces for a character controller, I suspect some madman here has done it, but the built-in rigidbody physics are mainly designed to roughly model real world physics. So if you have a character using a capsule collider and you push it around with forces, it's going to be like pushing around a giant pill—not quite a fun experience.

    Most custom solutions by and large do the same thing that the built in controller does, except either better, or with different features. I originally wrote mine to allow the controller to be rotated on it's y-axis (something the built-in one cannot do) and then added some other useful stuff, like detecting ground and some tools to ensure he doesn't jitter on the ground too much. Essentially ALL the built in controller does is provide collision responses, and then it stores those responses in a variable (what objects you hit on what part of the capsule).

    What I'm mostly trying to impress here is that the vast majority of the "complex" stuff that the CC does under the hood is just collision algorithms. To answer your question about whether it's a black-box: sort of. You can get source access to PhysX on github, but Unity's interface with it is not open source.

    My second point is that the majority of the hard work to get a character to "feel" right comes after you nail down the collision aspect. Everything from how to determine ground, sliding on slopes, limiting the ability to climb slopes matters. And then comes your actual character design, like how fast they run, how much control you have when jumping, the speed of gravity. Finally, you top it all off with animations and graphics, which are super importing to giving cues to the player about the state of their character. So while Unity's component is called the CharacterController, it's more just a small set of methods that help resolve collision for characters.

    To answer 3D vs 2D: I suggested a custom solution for 2D because 2D collision is orders of magnitude simpler than 3D, and is very easy to get started with. I think @JoeStrout has some 2D character tutorial hanging around that doesn't use any built in components.

    It comes down to your goals. If your goal is to make a tight controlling character, just use the built in CC. Writing a custom solution is a big project and even with the know-how isn't always necessary. If you haven't already used the CC before, defs try out my tutorials, since it will give you a good idea of what the CC does.

    Sorry for the giant long winded answers, but this is a topic I've spent lots of time working on so have a lot to talk about on it!
     
    DrZwieback, JoeStrout and Cybearg like this.
  7. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    True; that tutorial is here. It is more about 2d animation methods, but in the course of discussing that, it also develops a pretty nice 2d character controller (if I do say so myself).

    For what it's worth, I agree with everything @Iron-Warrior has said in this thread. I only want to add that the expense of raycasts has been grossly exaggerated. A ray cast (particularly with a limited range, as you would use in this case) is the cheapest possible form of collision detection, and is absolutely trivial compared to everything that has to happen for full physics. You can't code much interesting behavior without sensing your environment, so don't be afriad to use these when you need them.
     
    Hyblademin likes this.
  8. Cybearg

    Cybearg

    Joined:
    Jan 18, 2015
    Posts:
    46
    Thanks for the responses, everyone. I suppose that after Unity being out for so long and being the basis for so many professional games, I expected there to be a handful of ideal algorithms that were pretty unquestionably the "best way" to do things (elegantly simple, yet efficiently powerful).

    When it comes to 3D raycasts to detect collision (or prevent them), in my experience things get messy when your character is moving at arbitrary angles, especially when dealing with a character that is very wide (to the point where it's easy to catch on the edge of a wall because the center-point raycast would miss it) or when you're going against something like the inner corner of an L-shaped wall (where a raycast that may be long enough to work well on most walls isn't long enough to detect that far corner). Etc.
     
  9. Albert_AS

    Albert_AS

    Joined:
    Mar 12, 2017
    Posts:
    1
    I beat your time! nice fun project, although the fall speed is way too slow.
    Best regards from Zaragoza,
    Alberto.