Search Unity

  1. Engage, network and learn at Unite Austin 2017, Oct 3 - 5. Get your ticket today!
    Dismiss Notice
  2. Introducing the Unity Essentials Packs! Find out more.
    Dismiss Notice
  3. Check out all the fixes for 5.6 on the patch releases page.
    Dismiss Notice
  4. Unity 2017.1 is now released.
    Dismiss Notice
  5. Help us improve the editor usability and artist workflows. Join our discussion to provide your feedback.
    Dismiss Notice
  6. Unity 2017.2 beta is now available for download.
    Dismiss Notice

Simple arcade control system with rigidbody. What are the best practices?

Discussion in 'Editor & General Support' started by kurai, Jul 3, 2013.

  1. kurai

    kurai

    Joined:
    Jan 9, 2012
    Posts:
    103
    I know, questions like this one have been posted several times. Unfortunately, though, looking for answers raises a lot of confusion. And I have to say, the Unity documentation isn't always super clear on that matter.

    Let's say I have an object (my main character) which I want to move in an arcade way. Let's assume in a top-down world, for example. I need it to have a rigidbody attached, because I really want it to collide with walls and stuff (and maybe to make it able to push objects around). I want it to move in a linear way: when I press a button, it just moves in that direction at full velocity, and when I let go it stops.

    Just that, no drag, no inertia, nothing.

    I think I have three options here:

    - Using transform.translate on the gameobject. I've abandoned this approach because it seems to unnaturally behave with collisions. Still, part of the documentation states it could be used with rigidbodies (here, in the details section) while in other places strongly advocates against it.

    - Directly change the velocity value on input. This is not recommended by the documentation, though I cannot understand exactly why. In my experiments this seems to work exactly as I want (more in a moment) and it's way simpler than

    - using forces. This should be the way to go, but it looks to me it adds unnecessary complexity to a rather simple task. Plus, forces are often unpredictable, and while are great for realistic behavior, they could be an obstacle when dealing with arcade controls.

    That said, for now my best option is the second one, which I've implemented in a simple script like this one:

    Code (csharp):
    1.     // Update is called once per frame
    2.     void Update () {
    3.         if (Input.GetKeyDown(KeyCode.A)) moveNow=true;
    4.         else  if (Input.GetKeyUp(KeyCode.A)) moveNow =false;
    5.        
    6.         if (moveNow)
    7.         {
    8.             rigidbody.velocity=Vector3.right*10;
    9.         }
    10.         else rigidbody.velocity=Vector3.zero;
    11.     }
    Now. Is this code correct/acceptable to you? Are there better/other ways to obtain the same results? Is it correct to set velocity in the update rather than in the FixedUpdate?

    Any advice helping me clearing my mind is most welcome. Thank you.
     
  2. SpaceMammoth

    SpaceMammoth

    Joined:
    Jan 2, 2013
    Posts:
    220
    I think this is a great question. One of the confusing things about Unity (all software?) is know which way is best practice for solving a problem thats has everal possible solutions.

    For my money, I agree with your approach. I thnk the documentation is saying changing the velocity without using forces creates unrealistic behaviour because it does. You are aiming for "unrealistic" arcade behaviour. I'm very interested to hear what the best practice is for this kind of problem from more experienced Unity devs.
     
  3. moonjump

    moonjump

    Joined:
    Apr 15, 2010
    Posts:
    2,112
    Physics should be done in FixedUpdate, not Update, otherwise it can be unpredictable, just as you are seeing. This is because physics are updated on a fixed time step, not each frame.

    If you go with transform.translate, then the Rigidbody should be set to Kinematic. But you probably should not go this route if you want to push other objects around.
     
  4. kurai

    kurai

    Joined:
    Jan 9, 2012
    Posts:
    103
    I thought that, being my code just a setup of the velocity (no acceleration or other forces) it could be as well put into the update. But out of curiosity I tried to make a comparison between the code I posted before and another code where the set velocity instructions were in FixedUpdate. The update script moves my object faster, but the movement is a bit less smooth. The FixedUpdate is a bit slower but way smoother.
     
  5. Adam-Buckner

    Adam-Buckner

    Unity Technologies

    Joined:
    Jun 27, 2007
    Posts:
    5,655
    One way to keep your physics code in FixedUpdate and your input in Update is to simply use:

    Code (csharp):
    1. void Update ()
    2. {
    3.     if (Input.GetKeyDown(KeyCode.A))
    4.     {
    5.         moveNow=true;
    6.     }
    7.     else  if (Input.GetKeyUp(KeyCode.A))
    8.     {
    9.         moveNow =false;
    10.     }
    11. }
    12.  
    13. void FixedUpdate ()
    14. {
    15.     if (moveNow)
    16.     {
    17.         rigidbody.velocity=Vector3.right*10;
    18.     }
    19.     else
    20.     {
    21.         rigidbody.velocity=Vector3.zero;
    22.     }
    23. }
    When it comes to "Arcade" controllers... it somewhat depends upon what type of game you are making.

    In my live "Basics of Unity" class, we have a Space Ship that has a kinematic rigidbody and a capsule collider which is moved in Update(). The other objects, enemies and shots, are also kinematic rigidbodies being moved in Update(). The interaction of the game is based on triggers, primarily OnTriggerEnter(), not collisions, per se.

    If you are moving with forces, the experimentation I've done does show that you either need to increase your drag (and or angularDrag) to a very high value or manually set the rigidbody.velocity (and/or angularVelocity) to zero otherwise, you will drift. One of the issues with not using a kinematic rigidbody in an arcade situation, is that your non-kinematic rigidbodies can also receive forces and torque from other game objects and code. This may not be desirable.

    If you are using a character, even if it's "arcade" style, you could consider using a character controller. This is part of the PhysX system, and will have a different set of advantages over a simple rigidbody GameObject (kinematic or not...)

    -

    For an interesting graphic representation of Update vs FixedUpdate, see this page and look for "Getting your Input to work properly"
     
  6. kurai

    kurai

    Joined:
    Jan 9, 2012
    Posts:
    103
    Neat! I didn't know about those classes. I should attend one, I'm going to check if I can :)
    Do you move the kinematic object with rigidbody.moveposition? That is a great solution if you don't need obstacles like walls or static colliders in general.

    Thanks for sharing!
     
  7. Adam-Buckner

    Adam-Buckner

    Unity Technologies

    Joined:
    Jun 27, 2007
    Posts:
    5,655
    If you have a kinematic rigidbody, it's best to move it by it's transform using transform.Translate() or transform.Rotate() rather than forces or rigidbody.moveposition.
     
  8. kurai

    kurai

    Joined:
    Jan 9, 2012
    Posts:
    103
    When it's best to use moveposition, then? Just to set up the position of a rigidbody (aka to spawn a new object or to teleport it somewhere)?

    Thanks!
     
  9. Adam-Buckner

    Adam-Buckner

    Unity Technologies

    Joined:
    Jun 27, 2007
    Posts:
    5,655
    The MovePosition page says:

    I believe if you have a kinematic rigidbody object as a moving platform, and you move this in Update() using Transform.Translate(), you will leave the other objects behind.

    You could, in theory, move an arcade like ship using moveposition, and I know I've tried it, but there were some drawbacks... I can't remember what off the top of my head, but it's something like the objects gain Rigidbody.velocity when they move...

    You could try it out.

    Here's a stub of a test that I was working on a while ago to demo these issues:
    View attachment $MoveObjects.unitypackage