Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Script structure for player object

Discussion in 'Scripting' started by h0nka, Mar 14, 2019.

  1. h0nka

    h0nka

    Joined:
    Apr 7, 2013
    Posts:
    109
    Hello

    I’m making a character controller and I’m finding myself constantly redoing the structuring of the character mechanics.

    I have a player with some basic movement (run/jump) and I want the player to also have abilities like double jump, glide, dash etc.

    My question is how to properly structure this in the inspector (one monobehavior or multiple monobehaviors). Normally for OOP I would probably do something like a Factory pattern for the abilities all inheriting from a base ability class but in Unity this limits my options in terms of serializing the variables (for the inspector) used for something like speed and counters.

    Does anyone have a suggestion for a solid architecture for such cases?

    Thanks!
     
  2. pod11

    pod11

    Joined:
    Jan 6, 2019
    Posts:
    60
    I think this may be more of planning mistake.
    You are probably leaving too much room for " planning on the way".
    Just plan a game, fogure a character movement that fits it, then plan whats MINIMUM viable prototape, and do it.
    Stick to the plan, see if it works, only after that change things that testing tells you you need to change.
     
  3. h0nka

    h0nka

    Joined:
    Apr 7, 2013
    Posts:
    109
    Thank you for replying. I disagree. I know what I want in the game. The question is simply about finding a good way to structure monobehaviours in the game objects.
     
  4. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,120
    Ultimately it'll mostly come down to personal preference.

    I tend to prefer to have a single controller per kind of thing in my game (player, gun, door, etc), until I need the same functionality on multiple kinds of things. When I first built my character controller, all of the mouse-look, movement, interacting behavior was in a single script. A little while later I decided that my enemies should move around using the same mechanics as my player, so I extracted the rigidbody movement behavior out of the player scripts and made it an independent, reusable script.

    Some people look at a script with a couple thousands lines of code and assume it's all spaghetti and unmaintainable. Others looks at a gameObject with a dozen scripts on it and assume the same.

    I'm not really sure, in your case, how a different structuring of your code would have eased your implementation burden. I can't really imaging writing a script that allows jumping, as well as another script that allows double-jumping, and making those two separate components on an object. I suppose you could, but that seems like huge overkill. The reality is that refactoring is part of software development, and any new functionality you add has a chance of requiring some rework of existing code.
     
    h0nka likes this.
  5. h0nka

    h0nka

    Joined:
    Apr 7, 2013
    Posts:
    109
    Thank you for replying. I’m all to familiar with refactoring. What I’m not too familiar with is Unity’s inspector serialization and was therefore wondering if some alternatives existed between one or many monobehaviours. I guess one could have an ability base class with inheriting abilities where their constructors receive arguments from the player controller monobehaviour. Anyway, thank you for your input.
     
  6. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,120
    One quirky thing about Unity is that although it's C#, you'll generally not use constructors for your MonoBehaviour scripts. I believe it even gives you a warning if you try to add a script via "new MyScriptName()". If you want to add a component to add a script to a gameobject, you'll use the AddComponent<> method rather than newing up the component. So, constructors for components aren't really used.

    You can still sort of do constructor-like behavior by using AddComponent to create the script, then immediately disabling the component. From there you can set some values on the component, before reenabling it. When the component's Start() method is called, it should have all the values you assigned to it.

    As for inspector serialization, I agree that that's kind of always a headache if you're renaming properties. I don't know of any good approach to mitigating the potential data loss of changing "RunSpeed" to "MovementSpeed", for example, though if it's really bothering you I wouldn't be surprised if there was some asset on the asset store to help out in those cases.
     
    h0nka likes this.
  7. h0nka

    h0nka

    Joined:
    Apr 7, 2013
    Posts:
    109
    Sorry. I probably wasn’t clear on the constructors. My idea was to create pure C# scripts, then instantiate them in the monobehaviour script with serialized variables as arguments for the constructor. That way I only have one monobehaviour but also a class for each ability which I can still assign values at runtime in the inspector through fields or properties of that object. Does that make sense?
     
  8. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,120
    I don't 100% follow. Feel free to post a small code example if you want to run the specific idea past the community here in this post. You can, of course, store most any kind of data you want in pure c# scripts. But I'm not sure about your statement of "assign values at runtime in the inspector". I don't think you can view objects in the inspector unless they derive from UnityEngine.Object (https://docs.unity3d.com/ScriptReference/Object.html).

    Another possible approach you should learn about is ScriptableObjects. These are classes that allow you to create instances (typically created ahead of time as assets in your project, but you can also create them at runtime) which can be assigned to other MonoBehaviours. They're generally for holding configuration data. The cool thing about them is that you can have multiple instances of each type you create, which makes it pretty simple to create "classes" of objects that have similar, but slightly different, behavior. As a simple example, someone might decide to implement a "Monster" scriptable object, which has fields like "HP (float)", "Attack Power (float)", "Attack Sound Effect (AudioClip)", "Portait Image (Sprite)", etc, etc. That's just the class that holds the data. Then, from that class, you create create any number of instances of that class, which get saved into your Unity project, each of which would have different values for each of the fields. I use a lot of scriptable objects to allow a particular MonoBehaviour to be provided with different configuration settings depending on the circumstances.

    Anyway, I'm not questioning whether your approach is reasonable, I'm just not clear on how you'd go about it yet.
     
  9. h0nka

    h0nka

    Joined:
    Apr 7, 2013
    Posts:
    109
    Yeah but as you say, ScriptableObjects are mainly for storing data so that probably wouldn't be optimal for abilities (as I understand it) which are more behaviors. I have made a diagram of what I am proposing. Pardon my informal UML design.
     

    Attached Files:

  10. h0nka

    h0nka

    Joined:
    Apr 7, 2013
    Posts:
    109
    One could of course also make speed, gravity, velocity etc. parameters in the perform method...
     
    Last edited: Mar 16, 2019