Search Unity

Coding practice

Discussion in 'Scripting' started by AlTheSlacker, Oct 21, 2020.

  1. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    Suppose I have a gameobject with a rigidbody that is moved by the player via WASD.

    I write a class that inherits from mono, gets the player input and applies some movement in FixedUpdate. Great, the object now moves around as the player wishes.

    Then I want to add a camera that follows this rigidbody. I do not want to parent the camera to the gameobject with the rigidbody. So I update the camera's position in FixedUpdate to match the new rigidbody position.

    Initially I thought I would separate out my camera class and apply it to the camera object, but this did not work, as I could not be sure that the movement of the rigidbody would happen before the camera class FixedUpdate...

    I need to be sure that the camera follows the rigidbody as it moves and does not lag behind it by 1 physics cycle. So I have to apply my camera motion in the rigidbody movement class FixedUpdate after the application of the physics movement.

    My camera class then gets much more involved as I want to allow various camera positioning functionality. My rigidbody movement class gets more involved as I add extra control options. I'm heading towards one big ugly pile of code that does several very different things and I feel sad.

    I'm tied to both inheriting from mono and I'm tied to ensuring the rigidbody move is applied before the camera moves in that physics cycle.

    What is the best approach to writing what appears to be 2 self contained classes, that aren't quite self contained and can't inherit from each other? Do I have to have a single controller class that then calls methods from both the movement and camera classes, or is there a better way?
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    There's a few possibilities.
    1. Like you said, create a "controller" or "orchestrator" class that has a FixedUpdate method which calls methods on the other 2 scripts in the correct order
    2. Use Unity's Script Execution order settings to ensure that the camera FixedUpdate happens after the other: https://docs.unity3d.com/Manual/class-MonoManager.html
    3. Use events. Your moving object can declare a public event with the notion of "Finished my movement this physics frame". The camera script can listen for this event and do its thing in response.
    4. Use Cinemachine for your camera movement which has FixedUpdate camera following built-in as part of its transposer system.
     
    AlTheSlacker likes this.
  3. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    Thanks for that... I had not looked at Cinemachine before, it looks very cool.
     
  4. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    FixedUpdate can be called several times per frame or not at all. Any reason you don't update your camera in LateUpdate? IMO FixedUpdate is not suited for input and/or camera movement.

    Edit: Some more details: https://jacksondunstan.com/articles/4824
     
  5. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    As the rigidbody is updating it's position on fixed update it seems to work much better updating the camera position at the same time, I've found it can become quite jittery using LateUpdate to track a physics object. Although I agree with you that LateUpdate is far better for tracking non-physics objects.
     
  6. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    It's valid to use FixedUpdate for a camera that tracks an object that itself moves during FixedUpdate. (Unless there's something else going on like Rigidbody interpolation, in which case, back to LateUpdate).
     
    Last edited: Oct 21, 2020
  7. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    Even with interpolation I have managed to get jitters.
     
  8. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    I had researched the stutter/jitter issue a while ago. And there were some contradictional advices. My advise refers to the following comment of Mortoc from here.
    It is from 2012 and I don't know how "true" this advise is. I just remembered FixedUpdate equals bad ;).

    So it is no wonder that we get confused when there is no definitive solution. I read my notes about this and post some suggestions which helped other people for you to try.

    Test the build, not in Editor!
    set QualitySettings.vSyncCount = 0
    set fixed timestep = 0.01666667 (60 Hz)
    use Time.smoothDeltaTime as delta time of last frame can vary.
    set Application.targetFrateRate = -1, and make sure to turn vSync off

    So different methods helped different people. Perhaps the reason is different too? And some of the threads about this topic were pretty outdated so I don't know what of this still applies these days. Maybe something of this helps you too.
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,686
    Welcome to game programming! :)

    I agree with Praetor, check out Cinemachine... might just get you 100% of the way there.

    Regards the timing of Fixed vs Late vs regular Update()... there IS a method to all this madness and you can see it here in this timing chart.

    https://docs.unity3d.com/Manual/ExecutionOrder.html

    Camera updating in Fixed is fine. Explicitly calling the camera to update it after your Fixed motion is fine too. The construct I like is to have a factory method for the camera controller, and the player MAKES the camera controller, and the player is responsible for CALLING it directly. That way when the player's logic changes, it can directly control the camera.

    Be careful with "doing too much" in the FixedUpdate function because Unity WILL ABSOLUTELY call that as many times per second as you ask it to, and everything else will suffer as a consequence. Doing your own camera smoothing filtering and chasing a target is fine... I got bitten when I tried updating 300 particles AND checking their bounds AND wrapping them, all in my fixed update... once that started to take a significant chunk of the update cycle (16ms at 60fps), the game FPS just hit a wall, like from 60fps to 10fps... chonk.
     
    AlTheSlacker and PraetorBlue like this.
  10. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I disagree. True that FixedUpdate can be called multiple times per frame, but you never actually see the results of any FixedUpdate until LateUpdate has been called at least once. So even if FixedUpdate was called 1000 times between frames, you still get a call to LateUpdate to reposition the camera before the results of any of it are sent to the GPU to be seen by the player.
     
  11. AlTheSlacker

    AlTheSlacker

    Joined:
    Jun 12, 2017
    Posts:
    326
    Joe, I'm sure you know more about this than me, and it appears reasonable, but I get jitters on LateUpdate and by simply changing the method to FixedUpdate the problem is solved.
     
    Joe-Censored and PraetorBlue like this.
  12. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,686
    I actually agree with Joe's assessment that LateUpdate() actually SHOULD be acceptable, but I have seen that @AlTheSlacker 's actual experience matches mine: even Late will jitter when following a Rigidbody, but Fixed will not.

    Studying the flow timing diagram, this initially made me think I had a bug, but it was fairly easy to replicate with a trivial project, so I am thinking perhaps something different than what the timing diagram would indicate is actually happening.
     
    Joe-Censored likes this.
  13. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I'm very curious about this. I'm wondering what the player's rigidbody.interpolation setting is set to? (if not the player, whatever rigidbody the camera is following) If set to anything other than RigidbodyInterpolation.None I would actually expect moving the camera in FixedUpdate would cause jitter instead of resolving it (especially at high frame rates), since the player object should be moving between frames even if there was no physics update and FixedUpdate called in between. But if set to None then moving the camera in FixedUpdate should be perfectly fine.

    https://docs.unity3d.com/ScriptReference/Rigidbody-interpolation.html
     
  14. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,909
    Yep same here. Maybe it has something to do with synchronization of transforms and rigidbodies? I never looked too deep into it.
     
  15. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,104
    I will address only the issue of having a camera updated by FixedUpdate. I'm sorry if I'm missing some crucial point to which you've arrived in the meantime, this is a big thread, but there is a simple trick to having smooth motion, even though it's updated either irregularly or independently, that I'd like to share.

    First declare a private class field that contains the actual parameter of the camera that needs to update. Whether it's position, fov, pitch etc. In other words, do not update the camera itself, but use this as a proxy. Apply your logic as usual, but feed these fields instead.

    These values will be used as targets leads, and do not reflect the reality of camera motion.

    Then introduce a single serialized variable that is in the 0..1 range, for each parameter; add 'anim' prefix to it, to indicate it is supposed to animate or tween the target lead. Set this to 0.5 by default.

    Finally, declare a full new set of variables, this time with a prefix 'final', 'tween' or something similar. These will be used to contain the actual, current values. These are likely redundant, and you can do without them, but the code tends to be more readable with them.

    Exclusively in Update (or LateUpdate; but not FixedUpdate), evaluate _finalParam as follows
    Code (csharp):
    1. _tweenParam += (_param - _tweenParam) * animParam;
    2. myCamera.whatever = _tweenParam;
    Your camera is now animated independently of how regularly you update _param, and you can individually modify the interpolation rate (animParam) for each parameter, until satisfied with the motion.

    The end result is very pleasing for the eyes. At least in my opinion.

    Sorry again if I'm crashing into party uninvited, but having read the OP, it made sense to share this technique especially when the camera parameters are to be updated in FixedUpdate instead of Update.

    This is also mighty useful for mouse-scroll zooming, to reduce and customize the motion between the snapping steps. It is also very cheap to implement, cheap to execute, and very easy to maintain.

    I understand this might not be fully on-topic either, hopefully I won't get murdered.