Can a Scriptable Object class be inside a namespace in Unity3D? - c#

I'm restructuring my code and created an namespace with the intent to put all the scriptable object classes inside it. (I'm attaching the code and screenshot below for the sake of organization.)
When I go back to Unity, it seems fine. It also displays the option to create in the menu.
But when I check the object, it seems to have no script attached to it.
Some people have shown that Unity is capable of having more than one like in this post from Unity's Forum.
The guy even lists the same behavior as mine in the Case #4. But I can't understand why my code isn't behaving like his Case #2. I'm thinking that's caused by the Attribute, but I don't know how to display the option to create the asset.. Can someone help me?
edit: I did try using a Editor class as the post but got the same result.
namespace BBMM.UI.ScriptableObjects
{
[CreateAssetMenu(fileName = "New Size Preset", menuName = "BBMM/Size Preset")]
public class SO_UI_SizePreset : ScriptableObject
{
public string presetName;
public Vector2 realSize;
public UnitType unit;
}
[CreateAssetMenu(fileName = "New Object", menuName = "BBMM/Object Container")]
public class SO_UI_ObjectList_Item : ScriptableObject
{
public string objectName;
public GameObject prefab;
}
}
Here's the asset created using the code above:

First of all make sure each class is in a separate file with matching name!
SO_UI_SizePreset.cs
namespace BBMM.UI.ScriptableObjects
{
[CreateAssetMenu(fileName = "New Size Preset", menuName = "BBMM/Size Preset")]
public class SO_UI_SizePreset : ScriptableObject
{
public string presetName;
public Vector2 realSize;
public UnitType unit;
}
}
SO_UI_ObjectList_Item.cs
namespace BBMM.UI.ScriptableObjects
{
[CreateAssetMenu(fileName = "New Object", menuName = "BBMM/Object Container")]
public class SO_UI_ObjectList_Item : ScriptableObject
{
public string objectName;
public GameObject prefab;
}
}
Then if you created the SO asset first and then changed the namespace Unity might loose the connection (depending on how exactly you made that change).
-> you can either recreate them or go to the Inspector top-right corner menu, enable Debug mode and drag&drop the correct script into the Script field.

Related

Serialize a custom class in a custom inspector in Unity

I need to serialize a class in a custom inspector (using visually the Editor) like as doing in a Monobehaviour script like this:
[System.Serializable]
public class CustomClass
{
int myInt
}
public class OtherClass : MonoBehaviour
{
[SerializeField] CustomClass customClass;
}
which gives this result:
result wanted and given using the code above, where DamageEffect = CustomClass and Damage = myInt
In my custom editor, I'd like something like this:
[CustomEditor(typeof(CardObject))]
class AnotherClassEditor : Editor
{
public override void OnInspectorGUI()
{
[SerializeField] CustomClass customclass;
}
}
but, as expected, it points out an error.
I also tried with EditorGUILayout.ObjectField() but I haven't been able to, I'm not so experienced so please try to keep the answers simple.
Actually, I need this serialization to happen only when an enum is equal to a certain value, the overall script is something like this:
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[CreateAssetMenu(fileName = "Card", menuName = "CardObject")]
public class CardObject : ScriptableObject
{
public List<CardEffectType> effectsTypes;
//other...
[HideInInspector] public List<CardEffect> effects;
//other...
}
#if UNITY_EDITOR
[CustomEditor(typeof(CardObject))]
class CardObjectEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
CardObject cardObject = (CardObject)target;
foreach(CardEffectType effectType in cardObject.effectsTypes)
{
switch (effectType)
{
case CardEffectType.DamageEffect:
{
//!!!
}
}
}
}
}
#endif
public enum CardEffectType
{
DamageEffect, //other...
}
I found some workarounds but the result is not as when a class is serialized in a Monobehaviour.
When you just want to show something like how Unity would, you can use EditorGUILayout.PropertyField(), but it asks for a SerializedProperty, what you need to get from the SerializedObject, not from the actual target. Something like this:
[CustomEditor(typeof(MyType))]
public class MyTypeEditor : Editor
{
private SerializedProperty _variableIWantToShow;
private void OnEnable()
{
_variableIWantToShow = serializedObject.FindProperty("<name-of-the-variable>");
}
public override void OnInspectorGUI()
{
// ...
if (ShowVariable) EditorGUILayout.PropertyField(_variableIWantToShow);
// ...
}
}
You can manage collections (array, list, etc.) as SerializedProperty, but it adds some complexity.
https://docs.unity3d.com/ScriptReference/SerializedProperty.html
https://answers.unity.com/questions/682932/using-generic-list-with-serializedproperty-inspect.html
Just make your int myInt public, or add [SerializeField] attribute to it - inspector is only designed to work with serialisable fields (public fields are serialised by default in unity editor), currently your myInt is private hence only visible from within
As mentioned in an answer by h4i, proper way to display objects in the editor is using SerializedProperty, casting 'target' seems like a good idea at start but is only useful if you want to call methods on the object, it will fail in several other cases.
What you may want to consider is declaring a PropertyDrawer while Editors service a single monobehaviour type, a PropertyDrawer handles displaying every instance of a serializable class, and will be used every time a default editor uses a PropertyField internally. This will probably match your use case better

Changing Player Sprite through Shop

I'm trying to create a shop where you can buy a different player sprite from in-game currency. (The shop is a separate scene from the level) I was told that using scriptableobject is the way to go, so I made the following:
[CreateAssetMenu(fileName = "New Sprite", menuName = "Player Sprites")]
public class PlayerSprites : ScriptableObject
{
public string spriteName;
public int cost;
public Sprite sprite;
}
And I just added to the player script
public SpriteRenderer spriteRenderer;
void Start()
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
I'm not really sure where to go from here... how to render the sprite on to the player from a different scene when the sprite button is pressed... Any help is greatly appreciated thank you!
Though your question is quite hazy and I don't really see what you tried so far:
As you have it right now you will need one ScriptableObject for each Sprite item ... I don't think that's what you want. You should rather use one ScriptableObject storing the information of all Sprite items.
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Assets/New Store", menuName = "Sprites-Store")]
public class SpriteStoreContainer : ScriptableObject
{
public List<StoreSpriteItem> SpriteItems = new List<StoreSpriteItem>();
// you can/should also implement methods here as in any usual component!
}
Also make sure your fileName starts with Assets/
And a separate class for the Items which uses [System.Serializable] so you can display it in the Inspector.
using UnityEngine;
[System.Serializable]
public class StoreSpriteItem
{
public string spriteName;
public int cost;
public Sprite sprite;
public bool IsAvailable;
// also here you could/should implement some methods e.g.
public void BuyItem()
{
IsAvailable = true;
}
}
And back in Unity:
Now you first have to Instantiate the ScriptableObject asset:
Go to the Project View (Assets) -> right mouse click -> in the menu click on Create -> click on Sprites-Store
This should create a new asset called New Store(.asset) under Assets
Now in the Inspector of this created asset you fill in the information you need. You should see our List SpriteItems with Size = 0.
To create elements just increase the Size value and hit enter(Carefull: Unity doesn't ask if you change this value => take care you don't delete items by reducing this value accidently later)
Now you can adjust all information for those SpriteItems
Later wherever you need access to the information of this Asset you can just use the reference as any other Component => you can assign it e.g. via the Inspector using a public field e.g.
using UnityEngine;
public class ScriptThatUsesStore : MonoBehaviour
{
public SpriteStoreContainer Store;
public void DoSomthingWithStore()
{
// example: just get the first List element
Sprite someSprite = Store.SpriteItems[0].sprite;
}
}
and than access the data in it. Though I strongly recommend you rather implement some methods in the ScriptableObject like e.g. BuyItem, GetSprite, etc.

Unity3d no Monobehaviour scripts in the file, or their names don't match the file name

Hello everyone I've been working on my first game and suddenly I got this error and cannot run the game, I already installed the new version of unity but it persists. I see it can be caused for several reasons but had no luck so far, do you know the most probable causes and how to fix it?
When I select the scripts the only one where I do not get this error is the following:
using UnityEngine;
using Assets.Code.States;
using Assets.Code.Interfaces;
public class StateManager : MonoBehaviour
{
private IStateBase activeState;
void Start ()
{
activeState = new BeginState (this);
}
void Update ()
{
if (activeState != null)
activeState.StateUpdate();
}
void OnGUI()
{
if (activeState != null)
activeState.ShowIt ();
}
public void SwitchState(IStateBase newState)
{
activeState = newState;
}
}
But for example here I get the error:
using UnityEngine;
using Assets.Code.Interfaces;
namespace Assets.Code.States
{
public class BeginState : IStateBase
{
private StateManager manager;
public BeginState (StateManager managerRef)
{
manager = managerRef;
Debug.Log ("Constructing BeginState");
Time.timeScale = 0;
}
public void StateUpdate()
{
if (Input.GetKeyUp(KeyCode.Space))
manager.SwitchState(new PlayState (manager));
}
public void ShowIt()
{
if (GUI.Button (new Rect (10, 10, 150, 100), "Press to Play"))
{
Time.timeScale = 1;
manager.SwitchState (new PlayState (manager));
}
}
}
}
And so on with every other script.
I've already installed a newer version of unity3d, uninstalled the antivirus, checked the file names but the error persists. I also do not have any class in a namespace..
Make sure your script file is named exactly as the MonoBehaviour class it contains.
MyScript.cs:
using UnityEngine;
public class MyScript : MonoBehaviour
{
}
Also, if your script file contains helper classes, make sure they are placed on the bottom of the file, never on top of the MonoBehaviour subclass.
Also, Unity will sometimes have problems when your MonoBehaviour classes are in namespaces. I don't know when exactly this happens, it simply does every now and then. Taking the class out of a namespace fixes the error.
I had a strange variation of this issue, and did not see the solution posted anywhere else online.
TLDR
I had whitespace in the namespace for the classes which Unity could not identify as MonoBehaviours.
Background
I had the No MonoBehaviour scripts in the file or ... warning text on two script files in my project, one a MonoBehaviour, and the other a ScriptableObject. Each issue had different symptoms:
MonoBehaviour
The MonoBehaviour class was named like MyMonoBehaviourService.
The script file was named MyMonoBehaviourService.cs
The class was not partial, it had no nested types, nor any other types defined within the file.
I could not attach MyMonoBehaviourService to a GameObject via the inspector, but I could via gameObject.AddComopnents<MyMonoBehaviourService>(). This resulted in the script attached to the object, looking and behaving correctly.
Upon entering PlayMode, I would get the error: The referenced script (Unknown) on this Behaviour is missing!
After exiting PlayMode, the GameObject with the MyMonoBehaviourService attached would now say that it was missing.
Scriptable Object
The ScriptableObject class was named like MyScriptableServiceData.
The script file was named MyScriptableServiceData.cs.
The class was not partial, it had no nested types, nor any other types defined within the file.
I was able to create a MyScriptableServiceData via C# and save it using the AssetDatabase.
I was then able to see and modify the properties using the inspector.
Upon exiting and re-opening the project, the asset would say that its script was missing.
Resolution
Strangely enough, the issue was actually whitespace in the namespace for each class. I mistakenly thought I was seeing word-wrapping but actually, there was a newline in the namespace.
namespace Appalachia.Prototype.KOC.Areas.DeveloperInterface.V01.Features.PerformanceProfiling.Services.
FPS
{
public sealed class FPSProfilerService : MonoBehaviour
{
}
}
needed to be changed to this:
namespace Appalachia.Prototype.KOC.Areas.DeveloperInterface.V01.Features.PerformanceProfiling.Services.FPS
{
public sealed class FPSProfilerService : MonoBehaviour
{
}
}
I'm not sure why this causes an issue with MonoBehaviour/ScriptableObject detection but it really took a while for me to uncover.

copying custom class list to another list

I have figured out (or at least it feels like I have figured out) a way to make a list of weapons without having to hardcode everything like I was last trying.
So I have got my custom class for a weapon specifying type name attribute etc.
I have created another script will show a list in Unity's inspector that I can keep adding weapons to and changing the values to what I see fit.
I also have a game manager script that is the route of everything to from player stats game state etc..
I want to have this list now transferred to the game manager as a new list in that script.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class GameManager {
public static GameManager GM;
public static int FileIndex;
public static List<Weapons> WeaponM = new List<WeaponDB>();
public PlayerData Player;
public bool SaveEnabled;
public bool IsPaused;
public int TimeHour;
public int TimeMinute;
public GameManager(){
Player = new PlayerData();
}
}
that would be my game manager. Would it not be as easy as typing
public static List<Weapons> WeaponM = new List<WeaponDB>();
and have all the list entries stored in there copied over to the list entry in my gamemanager?
Here is my WeaponDB script so you can see and I have another script but it just sets all the different attributes.
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
public class WeaponDB : MonoBehaviour{
public List<Weapons> Weapon;
public WeaponDB(){
}
public WeaponDB(List<Weapons> wpl){
Weapon = wpl;
}
void Start(){}
void Update(){}
}
I am getting an error of
CS0246 the type or namespacename "weaponsdb" could not be found"

CustomPropertyDrawer in unity3d for inherited classes

I'm trying to do something I think is relatively simple, but maybe I'm way off base. I don't know. I have a class that inherits from ScriptableObject, like such:
using UnityEngine;
class Automobile : ScriptableObject {
[SerializedField]
private int _baseCost = 0;
public int BaseCost { get { return _baseCost;} set {_baseCost = value;} }
// More fields and logic
void OnValidate() {
BaseCost = _baseCost;
}
}
Then I have a class that inherits from Automobile, like such:
public class Car : Automobile {
}
This class is actually empty, there may be logic added to this later, right now I'm just keep Cars separate from Trucks, Motorcycles, and who knows what else I'll think up. I used this script from the unity wiki to create a bunch of asset files for different cars. All are located under the \resources\cars folder so I can load them at runtime
I then have a player class that contains an array of cars (and trucks, but for brevity I'm excluding all that)
using UnityEngine;
public class Player : MonoBehaviour {
[SerializeField]
private Car[] _cars;
public Car[] Cars { get { return _cars; } set { _cars = value; } }
void Start () {
// Load all cars into array
Cars = Resources.LoadAll<Car>("Cars")
}
}
To cap this all off, and to actually explain the issue I'm having, I want to create a custom property drawer for every object that is derived from the Automobile class. So I created a script in the editor folder like this:
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(Automobile), true)]
public class AutomobileDrawer : PropertyDrawer {
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
EditorGUI.BeginProperty(position, label, property);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
Rect BaseCLabel = new Rect(position.x, position.y, 200, position.height);
Rect BaseC = new Rect(position.x + 35, position.y, 15, position.height);
EditorGUI.LabelField(BaseVLabel, "Base Cost");
// This is my error line
EditorGUI.PropertyField(BaseV, property.FindPropertyRelative("_baseCost"),GUIContent.none);
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
//base.OnGUI(position, property, label);
}
The issue I'm facing is that FindPropertyRelative always returns null. I'm almost 100% confident that due to the levels of inheritance I need to build a path (which would present a problem when I move on to trucks as the path will most likely be different), but I have no idea what it should be. Any suggestions?
EDIT: I forgot to mention that the Automobile class utilizes the OnValidate function. I added it to the code.
The issue seems to have been that classes that inherit from ScriptableObject do not seem to work well with propertydrawers. By removing the inheritance from my Automobiles class, and simply applying the [Serializable] attribute to the class declaration I was able to get the propertydrawer working correctly.
I am hoping that I am wrong about this, as I have some classes that I need to leave inheriting from ScriptableObject and hopefully somebody will see this and correct me, but until that time, this is my solution.

Categories

Resources