Unity scriptable object only serializing the unity objects - unity3d

I am trying to make a spell system. My base class for the system looks like this:
public abstract class MagicBook : ScriptableObject {
public Sprite bookSprite;
public float manaCost;
public float coolDown;
public bool rapidFire;
[HideInInspector] public bool coolDownElapsed;
public virtual void OnActivate() {
}
public virtual void OnActivate(Vector3 position, Quaternion rotation) {
}
}
Then I have another class extending from MagicBook where I override the OnActivate function. My problem now is that in the inspector only the variable bookSprite is showing and all the other values are not there. I tried adding an [SerializeField] in front of the variables and define them new in the extending class. But they still dont show. Has anyone an idea why they are not showing or how I can fix this?
Thanks for your help and time.

Did you create Asset for ScriptableObject?
Add attribute to extended class:
[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/NewBook", order = 1)]
And then create asset with
Another idea:
Probably you have custom drawer for this object, but...
Turn your inspector into debug mode and check if properties are visible there.
Cannot reproduce this issue with Unity 2020.1.0f1.
public class NewBook : MagicBook
{
}

Related

How to reference a gameObject inside a prefab? Using GameObject.Find does not work

I am trying to use GameObject.Find within a prefab to reference an object that exists in the scene. The name of the object is exactly the same as the actual object.
LoseLives loseLives;
GameObject loseLivesing;
private void Awake()
{
loseLivesing = GameObject.Find("MasterManager");
}
private void Start()
{
loseLives = loseLivesing.GetComponent<LoseLives>();
target = Waypoints.waypoints[0];
}
loseLives.LoseLife();
Null Reference
I get a null reference when "loseLives.LoseLife();" runs.
Any help with solving the no reference problem would be greatly appreciated!
If you think there is a better way to create the connection between a script on a prefab and a script on a in-scene gameObject could you please explain it or point me to a tutorial, because in all my research I did not have any luck.
Thank you!
It seems like your LoseLives is a singular, constant object.
First Method
Simply making use of the inspector by exposing the fields.
public class YourScript : MonoBehaviour {
[SerializeField]
private LoseLives loseLives;
// ..
private void Start() {
loseLives.LoseLife();
}
// ..
}
It comes with the problem of needing to constantly drag your constant object into the inspector whenever you need it.
For a Prefab, it works if the target object is part of the Prefab, or the Prefab is generated in the same scene as the targeted object.
Second Method
Using singletons.
LoseLives.cs
public class LoseLives : MonoBehaviour {
public static LoseLives Instance {
get => Instance;
}
private static LoseLives instance;
private void Start() {
instance = this;
}
public void LoseLife() {
// ...
}
}
YourScript.cs
public class YourScript : MonoBehaviour {
// ..
private void Start() {
LoseLives.Instance.LoseLife();
}
// ..
}
This works if you have a global, singular instance of LoseLives object.
Even though it might not be the best approach, I found a workaround for my issue.
I am using a static variable that resides in the other script (CurrencyManager), calling it from the LoseLives script.
Instead of:
loseLives.LoseLife();
I do:
CurrencyManager.playerHealth -= 1;
Where CurrencyMananger is the other script and playerHealth is a static variable that is in the CurrencyManager script. This works because a static variable does not require the objects to be referenced in the inspector.
From now on, when I have a problem where I want to reference a gameObject within a script that is on a prefab, I will consider using static variables in my solution.

Unity Inspector: require elements when boolean checked

How do I automatize the inspector such that if you don't click a serialized boolean, a component doesn't show up, when you click it, the corresponding component automatically shows up (as if it was required)?
Dropping journal not checked
You see I have not checked the boolean and the other component is not showing up indeed.
But when I click on the boolean I'd like that automatically this script shows up on the inspector.
Like this:
Dropping journal checked image
How can I do this?
You need to create custom editor.
Official documentation
using UnityEngine;
public class MyScript : MonoBehaviour
{
public bool Checked = false;
public GameObject SomeField;
}
Editor script.
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(MyScript))]
[CanEditMultipleObjects]
public class MyScriptEditor : Editor
{
SerializedProperty Checked;
SerializedProperty SomeField;
void OnEnable()
{
Checked = serializedObject.FindProperty("Checked");
SomeField = serializedObject.FindProperty("SomeField");
}
public override void OnInspectorGUI()
{
var myScript = (MyScript)target;
serializedObject.Update();
EditorGUILayout.PropertyField(Checked);
if(myScript.Checked)
EditorGUILayout.PropertyField(SomeField);
serializedObject.ApplyModifiedProperties();
}
}
You can use Attribute ExecuteInEditMode and then check in update function if boolean is true then just Add the second script to base component Here I attached simple script that give you idea
[ExecuteInEditMode]
public class TestScript : MonoBehaviour
{
public bool show = false;
void Update()
{
if (show)
{
base.gameObject.AddComponent<objectyouwanttoadd>();
}
}
}
Also you can implement logic that if other object is already attached or not also if you uncheck show bool then remove added object, also if current platform is unity Editor then only attach other component etc...

Unity - issue with AudioClip array

I'm trying to make an audio player in Unity.
I first created a ResourceManager, that has a
AudioClip[] array.
public class ResourceManager : MonoBehaviour
{
public AudioSource audioSrc;
public AudioClip[] clips;
private int trackNum = 0;
public void Awake()
{
audioSrc = GetComponent<AudioSource>();
audioSrc.Stop();
...omitted...
public void LoadClip()
{
audioSrc.clip = clips[trackNum];
}
}
So that they appear in the Clips drag-and-drop box.
However, I want to make the AudioClip array static in ResourceManager static, so other scripts (e.g. PlayPause.cs can access clips statically (via function calls).
But the problem is once I make clips static, the drag-and-drop for disappears.
Is there a way to fix that? Or are there some better design patterns? Thanks.
You can e.g. simply do
public class ResourceManager : MonoBehaviour
{
...
// This is the one in the Inspector
// Usually I keep the Inspector private and only provide public access
// where needed via properties
[SerializeField] private AudioClip[] clips;
// This will be set in Awake to the ones from the Inspector
// readonly for others, only this class can assign it
public static AudioClip[] Clips { get; private set; }
public void Awake()
{
Clips = clips;
...
}
}
The alternative suggested is make the ResourceManager a so called singleton like e.g.
public class ResourceManager : MonoBehaviour
{
// Here you store your own instance reference
// and provide public readonly access
public static ResourceManager Instance { get; private set; }
[SerializeField] private AudioClip[] clips;
public AudioClip[] Clips => clips;
...
public void Awake()
{
// Make sure only one instance exists at a time
if(Instance && Instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
// optional: Don't destroy this instance when the scene is switched
// Careful: Make sure this applies also to the AudioSource instance!
// For now I assume it is either attached to this object or a child
// if not simply do the same DontDestroyOnLoad on it as well
DontDestroyOnLoad (this.gameObject);
...
}
public void LoadClip()
{
audioSrc.clip = clips[trackNum];
}
}
And then access everywhere ReaourcesManager.Instance.Clips
Or a built-in alternative to the singleton is using FindObjectOfType so any other script in the Scene can actually simply access
FindObjectOfType<ResourcesManager>().clips
without you having to change anything at all ;)
Except as said I would make it
[SerializeField] private AudioClip[] clips;
public IReadonlyList<AudioClip> Clips => clips;
depending a bit of course what exactly you want to do with the clips but this way the array can not be altered by any other script

'PlaySounds.audio' hides inherited member 'Component.audio'. Use the new keyword if hiding was intended

I have seen a similar error to this but for Video, however I can not find anywhere that explains my current error.
I will attach my code below for reference, does anyone know what the error means here?
using UnityEngine;
using System.Collections;
public class PlaySounds : MonoBehaviour {
public AudioClip SoundToPlay;
public float Volume;
AudioSource audio;
public bool alreadyPlayed = false;
void Start()
{
audio = GetComponent<AudioSource>();
}
void OnTriggerEnter2D(Collider2D other)
{
if (!alreadyPlayed)
{
audio.PlayOneShot(SoundToPlay, Volume);
alreadyPlayed = true;
}
}
}
The MonoBehaviour class is already presenting a member called "audio" so your PlaySounds class has that member already. If you want to ignore the one from the base class you should declare it like so:
private new AudioSource audio;
But if you want the inherited member to be available as well as the one in your class simply give it a name other than "audio". To be honest unless you have a particular reason you want to override the inherited member I'd do the latter.

Instantiating random or chosen prefabs with sub-container thru Factory or Pool

I have an array of prefabs and I want to be able to Instantiate randomly picked prefabs thru Zenject Factory and perform their bindings in their sub-containers.
What I want to do is the same as in this code sample from Zenject documentation, but for randomly selected prefabs.
https://github.com/modesttree/Zenject/blob/master/Documentation/SubContainers.md#using-game-object-contexts-no-monobehaviours
using UnityEngine;
using Zenject;
public class GameInstaller : MonoInstaller
{
[SerializeField]
GameObject ShipPrefab;
public override void InstallBindings()
{
Container.BindInterfacesTo<GameRunner>().AsSingle();
Container.BindFactory<float, ShipFacade, ShipFacade.Factory>()
.FromSubContainerResolve().ByNewPrefabInstaller<ShipInstaller>(ShipPrefab);
}
}
I was able to partially make it work with
[SerializeField] private GameObject[] ships;
...
Container.BindFactory<float, ShipFacade, ShipFacade.Factory>()
.FromSubContainerResolve().ByNewGameObjectMethod(SpawnShip);
...
private void SpawnShip(DiContainer container, float speed)
{
container.Bind<ShipFacade>().AsSingle();
container.Bind<Transform>().FromComponentOnRoot();
var shipPrefab = ships[Random.Range(0, ships.Length)];
var ship = container.InstantiatePrefab(shipPrefab);
container.Bind<ShipHealthHandler>().FromNewComponentOn(ship).WhenInjectedInto<ShipFacade>();
container.BindInstance(speed).WhenInjectedInto<ShipInputHandler>();
}
But it's awkward and in this case I guess I'm not using an advantage of sub-container. And also prefabs spawns in an empty GameObject.
What I want to achieve is to be able to use ShipInstaller for sub-container binding.
You're right, there wasn't really a very elegant way to choose the sub-container prefab dynamically.
I took some time to make this better today with this commit. If you use the latest version of Extenject then you can now do things like this:
public class QuxInstaller : Installer {
float _speed;
public QuxInstaller(float speed) {
_speed = speed;
}
public override void InstallBindings() {
Container.BindInstance(_speed);
Container.Bind<QuxFacade>().AsSingle();
}
}
public class CubeInstaller : MonoInstaller
{
public List<GameObject> QuxPrefabs;
public override void InstallBindings()
{
Container.BindFactory<float, QuxFacade, QuxFacade.Factory>()
.FromSubContainerResolve().ByNewPrefabInstaller<QuxInstaller>(ChooseQuxPrefab);
}
UnityEngine.Object ChooseQuxPrefab(InjectContext _) {
return QuxPrefabs[Random.Range(0, QuxPrefabs.Count)];
}
}