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 nx_alpha · Aug 08, 2019 at 04:12 PM · assetscriptableobject

Using ScriptableObjects as Types/Enums

I'm having trouble setting up a simple Attribute system with ScriptableObjects. The design goals/components are:

  1. An extensible system for managing RPG attribute types like "Health" or "Mana". Instead of using Enums or subclassing, I want to create types and edit their properties (display name, min & max values, progression table etc.) from the Unity Editor. This is the purpose of the ScriptableObject class AttributeType.

  2. A very generic, serializable Attribute class that holds a specific value and is injected with a specific AttributeType to determine the actual type of attribute. This way an instance of Attribute can be generic and delegate it's behavior to its concrete AttributeType reference.

  3. A host of ScriptableObject classes that represent many different entities like characters, items, modifiers, etc. These need to be able to instantiate Attributes of specific AttributeTypes like a health attribute or a mana attribute. These objects too, shall be Assets created and edited in the Unity Editor.

A simplified example with a Character class as user of Attribute:

 [CreateAssetMenu(fileName = "New Attribute Type", menuName ="Data/Attribute Type")]
 public class AttributeType : ScriptableObject
 {
     public string key = "undefined";
     public string displayName = "Unnamed attribute";
     public string displayFormat = "";
 
     public string PrettyPrint(float value)
     {
         return this.displayName + ":\t" + value.ToString(this.displayFormat);
     }
 }

 [Serializable]
 public class Attribute
 {
     [SerializeField]
     private AttributeType attributeType;
     [SerializeField]
     public float value;
 
     public override string ToString()
     {
         if(this.attributeType == null) return this.value.ToString();
         return this.attributeType.PrettyPrint(this.value);
     }
 
     public Attribute() : this(null) { }
 
     public Attribute(AttributeType type)
     {
         this.attributeType = type;
     }
 }

 [CreateAssetMenu(fileName ="New Character", menuName = "Data/Character")]
 public class Character : ScriptableObject
 {
     public Attribute health;
     public Attribute mana;

     // wild speculation, does not work at all
     private void Awake()
     {
         health = new Attribute(Resources.Load<AttributeType>("Assets/Data/Health"));
         mana = new Attribute(Resources.Load<AttributeType>("Assets/Data/Mana"));
     }
 }

In the Assets folder there is one asset for Health and Mana each at Assets/Data/Health and Assets/Data/Manarespectively.

What causes me headaches is the initialization of a new Character asset. Whenever a new Instance of Character is created (either through the Editor's context menu or ScriptableObject<Character>.CreateInstance()), the attributes should be injected with the appropriate references to the Health and Mana assets.

I'm having serious problems getting any reference to the ScriptableObject assets through code. Neither Resources.Load(), AssetDatabase.LoadAssetAtPath(), not ScriptableObject.FindObjectsOfType() returns anything (null or empty array).

Even if I did manage to fetch the reference to the AttributeType assets, the question remains, where/when to perform the initialization in the Character class. Constructors are a no-go, OnEnable() runs every time the object is touched in the editor. The most sane place seems to be Awake(), but again I'm uncertain regarding unintended side-effects.

Any advise on how to architect a working solution and best practice in this situation is highly welcomed.

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

2 Replies

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

Answer by nx_alpha · Aug 10, 2019 at 08:12 AM

After playing around a bit with Dankey_Kang & Blubberfisch's suggestions I found that their solution has one drawback: When you change the structure of your user's class (e.g. add another Attribute to Character), existing assets will break, because the initialization only runs at creation time in the editor.

The solution is to set all members to null in their declaration and perform the initialisation lazily in the OnEnable method:

  [CreateAssetMenu(fileName ="New Character", menuName = "Data/Character")]
  public class Character : ScriptableObject
  {
      public Attribute health = null;
      public Attribute mana = null;
 
      private void OnEnable()
      {
          if (health == null) health = new Attribute(Resources.Load<AttributeType>("Data/Health"));
          if (mana == null) mana = new Attribute(Resources.Load<AttributeType>("Data/Mana"));
      }
  }

This way, whenever you add a new member to your Character class these new members will be initialized properly when the object is used by either the Editor or the game.

Comment
Add comment · 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
1

Answer by Dankey_Kang · Aug 08, 2019 at 07:08 PM

To initialize a ScriptableObject you need to create an initialization function and call it sometime after you create it using ScriptableObject.CreateInstance. That means you will have to create your own function to instantiate your ScriptableObject and save it which is annoying but here is a link that you may find useful: https://wiki.unity3d.com/index.php/CreateScriptableObjectAsset, I will sometimes use this kind of structure to create them using a context menu and call my initialization function right after I call CreateInstance. Not sure if I understood your problem in its entirety but I hope this helps.

Comment
Add comment · Show 2 · 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 Blubberfisch · Aug 08, 2019 at 08:41 PM 2
Share

Yes, that should work. In the static CreateAsset() function seen in the wiki code, you would add the stuff currently in your Awake method. Resources.Load() should be able to load your objects if they are located in a folder named "Resources", see https://docs.unity3d.com/ScriptReference/Resources.Load.html.

The following loads the attributes from folder Assets\Resources\Attributes:

     [$$anonymous$$enuItem("Assets/Create/Data/Character")]
     public static void CreateAsset()
     {
         Character c = ScriptableObjectUtility.CreateAsset<Character>();
         c.health = new Attribute(Resources.Load<AttributeType>("Attributes/Health"));
         c.mana = new Attribute(Resources.Load<AttributeType>("Attributes/$$anonymous$$ana"));
     }
avatar image nx_alpha · Aug 08, 2019 at 08:51 PM 0
Share

Thanks to both Dankey_$$anonymous$$ang & Blubberfisch! The static factory method was what I was trying to avoid in the hopes that Unity would have some other mechanism in place. Since both of you seem to agree that this is the way to go and not under$$anonymous$$ing any best practice, I can certainly live with it. Also thanks for the friendly re$$anonymous$$der regarding the assets folder structure. Once I put everything in the magic "Resources" folder, the references to the existing assets are resolved as intended.

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

113 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 avatar image

Related Questions

Custom assets give Missing (Mono Script) 0 Answers

How to change an object's name inside an asset file? 0 Answers

Why is it that I can make prefabs (which are assets) reference scene objects but not be able to do the same for ScriptableObjects? 1 Answer

Get Current Project Window Folder 2 Answers

Scriptable object keeps referencing the objects? 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