Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Dalsia · Sep 26, 2019 at 07:42 AM · classesorganization

How can I improve the organization of my character controller classes?

Earlier this year I spent a long time building my own custom character controller for 2D games. It works pretty well, but I've run into an issue with how to organize its classes, particularly when it comes to creating MonoBehaviours for different uses.

For example, my controller can be broken up into a class that handles movement, MovementController2D, and another class that handles collisions with raycasting, CollisionController2D. Not every object that moves needs to collide with the game geometry, so I wanted to keep these two classes separated, as I could then simply attach MovementController2D to those objects. This works fine for some objects, but things get tricky on objects that want to collide, as the two classes must then work together. My collision execution works like this:

1. The MovementController2D receives input from the player or AI, storing the desired input in a Vector2 velocity variable
2. The CollisionController2D reads the incoming velocity and raycasts for collisions, using the movement this frame, velocity * Time.deltaTime, as the length of the raycasts
3. The CollisionController2D checks if the raycasts hit anything, and then updates the velocity this frame accordingly
4. MovementController2D moves the character using the final, constrained velocity from step 3

Here's an outline of the two classes in code:

 public class MovementController2D : MonoBehaviour
 {
     public Vector2 Velocity { get { return velocity; } }
 
     [SerializeField]
     private float gravity = -20f;
     private Vector2 velocity;
 
     private void Update()
     {
         velocity.y += gravity * Time.deltaTime;
     }

     // LateUpdate is used to ensure that inputs are handled 
     // before the controller tries to move
     private void LateUpdate() 
     {
         transform.Translate(velocity * Time.deltaTime);
     }
 
     // The player or AI controllers call this method
     public void SetVelocity(Vector2 velocity) 
     {
          this.velocity = velocity;
     }
 }
 
 public class CollisionController2D : MonoBehaviour
 {
     private MovementController2D moveController;

     private void Awake()
     {
         moveController = GetComponent<MovementController2D>();
     }

     private void Update()
     {
         Vector2 velocityThisFrame = moveController.Velocity * Time.deltaTime;
 
         CollideHorizontally(ref velocityThisFrame);
 
         // This presents an issue because MovementController2D is
         // multiplying the final velocity by Time.deltaTime in LateUpdate() as well
         moveController.SetVelocity(velocityThisFrame);
     }
 
     private void CollideHorizontally(ref Vector2 deltaMovement) 
     {
         // Check for collisions and set deltaMovement to the appropriate value
     }
 }

This code does work, but it doesn't feel particularly clean. My goal is to be able to have different controller "pieces" that I can add to game objects depending on necessity; I don't want a floating balloon to needlessly be raycasting, for example.

The problem with what I currently have is that I have to "hack" the execution order by using Unity's Script Execution Order to get any of this to work, and my code falls apart without LateUpdate() in the MovementController2D class. It's like I'm trying to sandwich pieces of code together, and I feel like that is going pose a big maintenance issue later.

The execution order looks like:
Update() from Player input --> Update() from move controller --> Update() from collision controller --> LateUpdate() from move controller

Before trying to separate my controller logic, I put everything in one big 1,000 line script. This worked, but then I ran into the aforementioned issue of needless raycasting. I also considered having CollisionController2D inherit from MovementController2D, but isn't it frowned upon to have a (non-abstract) base class that can be added as its own component?

By the way, please do not recommend a different collision handling technique, such as moving first and then resolving collisions; after many months of testing, I have found this approach (read input, raycast, update velocity, then move) to be more robust in how it handles complicated collisions, such as with slopes, rotations and circular geometry.

Yes, I know this is sort of a picky post, but I'm trying to learn how to better organize my code.

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Mouton · Sep 26, 2019 at 07:59 AM

It looks like you want to use inheritance to split your class in different behaviours: make your MovementController2D more generic by removing specific code like the computation of the gravity, then for each specific controller inherit from it to extend the functionalities.


To solve the execution order issue, consider using FixedUpdate (https://docs.unity3d.com/ScriptReference/MonoBehaviour.FixedUpdate.html) for physic related stuff, it is executed after Update and before LateUpdate.

It is not triggered as often as Update does, there is a setting in Edit > Settings > Time > Fixed Timestep to control the call rate (but you should probably don't need to change it).

Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Dalsia · Sep 26, 2019 at 07:28 PM 0
Share

Thanks for the reply. I was hesitant to use inheritance because I can't really think of much else to put in another child class other than the gravity calculation.

avatar image Mouton Dalsia · Sep 26, 2019 at 07:55 PM 0
Share

Well, you have at least 2 behaviors: with gravity, and without (for the floating balloon). There is no problem to subclass once, it splits the logic. It does not harm performances of your game.

avatar image Dalsia Mouton · Sep 27, 2019 at 03:42 AM 0
Share

It took me a little while to wrap my head around the design using inheritance, but ultimately this was the best solution for me. It kept things clean and organized.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

112 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Project Classes Organization - General Question 0 Answers

How to select a class instance from the inspector? 1 Answer

How to get a reference to the owner-Script from inside a Class which is being used as field/property 1 Answer

Show a dropdown for C# classes in inspector. 2 Answers

Make custom editor for generic class to apply to all current and future child classes 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges