CustomEditor Unity: chosing enums at once + showing array - c#

I'm trying to do a thing on Unity and got caught up;
This may be a bit complex, but see if you can help me:
What I got and what I want (image)
This is the script I got now
public class ClasseTest : MonoBehaviour{
public enum TiposDeMissoes
{
TipoMatarGente,
TipoPegarItem,
};
public TiposDeMissoes TiposMissoes;
[Serializable]
public class MatarGente
{
public int IDmissao;
public GameObject[] ObjetosAtivarPraMissao;
public GameObject[] Checkpoints;
public GameObject[] InimigosPraMatar;
}
[Serializable]
public class PegarItem
{
public int IDmissao;
public GameObject[] ObjetosAtivarPraMissao;
public GameObject[] Checkpoints;
public GameObject[] ItemsEntregar;
}
[Serializable]
public class Missoes
{
public TiposDeMissoes TiposMissoes;
public PegarItem[] PegarItem;
public MatarGente[] MatarGente;
}
public Missoes[] MissoesJogasso;
}
I only what to show the class PegarItem if PegarItem was chosen in the enum.
There's only PegarItem and MatarGente right now, but there will be more classes.
I did some research and find out Im supposed to use the OnInspectorGUI if I want to be that specific (if there's other way round, pls tell me)
I got 0 experience on CustomEditor so what I got so far is
[CustomEditor(typeof(ClasseTest))]
public class TestCustomInspector : Editor
{
public int numMissoes;
public override void OnInspectorGUI()
{
ClasseTest script = (ClasseTest)target;
numMissoes = EditorGUILayout.IntField("Numero de Missoes", numMissoes);
EditorGUILayout.LabelField("Editante");
var serializedObject = new SerializedObject(target);
var property = serializedObject.FindProperty("MissoesJogasso");
serializedObject.Update();
EditorGUILayout.PropertyField(property, true);
serializedObject.ApplyModifiedProperties();
for (int i = 0; i < numMissoes; i++)
{
script.TiposMissoes = (ClasseTest.TiposDeMissoes)EditorGUILayout.EnumPopup("TIPO DE MISSAO", script.TiposMissoes);
if (script.TiposMissoes == ClasseTest.TiposDeMissoes.TipoMatarGente)
{
script.TiposMissoes = (ClasseTest.TiposDeMissoes)EditorGUILayout.EnumPopup("Matar Gentes", script.TiposMissoes);
}
if (script.TiposMissoes == ClasseTest.TiposDeMissoes.TipoPegarItem)
{
script.TiposMissoes = (ClasseTest.TiposDeMissoes)EditorGUILayout.EnumPopup("Pegar Item", script.TiposMissoes);
}
}
}
}
And that means, in the editor:
If I change the value of one enum, all the others copy it (image)
And That's exactly what I do not want.
And y'all, keep in mind when the editor chooses MatarGente or PegarItem I'd like to show all those possible variables that these classes hold. That includes arrays with non-specific lengths.
And also, if there's 2 'MatarGente' I'd like to be able to fill those arrays with different objects and retrieve that information later to use it somewhere else.

Tried to understand what you were doing. Your mistake was not modifying the array elements and few other issues.
This is your new ClasseTest:
using System;
using UnityEngine;
using System.Collections.Generic;
public class ClasseTest : MonoBehaviour
{
public enum TiposDeMissoes
{
TipoMatarGente,
TipoPegarItem,
}
[Serializable]
public class MatarGente
{
public int IDmissao;
public GameObject[] ObjetosAtivarPraMissao;
public GameObject[] Checkpoints;
public GameObject[] InimigosPraMatar;
}
[Serializable]
public class PegarItem
{
public int IDmissao;
public GameObject[] ObjetosAtivarPraMissao;
public GameObject[] Checkpoints;
public GameObject[] ItemsEntregar;
}
[Serializable]
public class Missoes
{
public TiposDeMissoes TiposMissoes;
public PegarItem[] PegarItem;
public MatarGente[] MatarGente;
}
public List<Missoes> MissoesJogasso;
public int NumMissoes;
}
This is your new TestCustomInspector class:
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
[CustomEditor(typeof(ClasseTest))]
public class TestCustomInspector : Editor
{
public override void OnInspectorGUI()
{
ClasseTest script = (ClasseTest)target;
script.NumMissoes = EditorGUILayout.IntField("Numero de Missoes", script.NumMissoes);
// Ensure it cannot go into negative numbers.
if (script.NumMissoes < 0) script.NumMissoes = 0;
// Create the list if it does not exist.
if(script.MissoesJogasso == null) script.MissoesJogasso = new List<ClasseTest.Missoes>();
// numMissoes being greater than the current count means we need to extend the list.
if (script.NumMissoes > script.MissoesJogasso.Count)
{
for (int i = 0; i < script.NumMissoes; i++)
{
script.MissoesJogasso.Add(new ClasseTest.Missoes());
}
}
// numMissoes being less than the current count means we need to decrease the list.
else if(script.NumMissoes < script.MissoesJogasso.Count)
{
int difference = script.MissoesJogasso.Count - script.NumMissoes;
// Remove the last element difference number of times.
for (int i = 0; i < difference; i++)
{
script.MissoesJogasso.RemoveAt(script.MissoesJogasso.Count - 1);
}
}
var serializedTarget = new SerializedObject(target);
for (int i = 0; i < script.MissoesJogasso.Count; i++)
{
var missoes = script.MissoesJogasso[i];
switch(missoes.TiposMissoes)
{
case ClasseTest.TiposDeMissoes.TipoMatarGente:
missoes.TiposMissoes = (ClasseTest.TiposDeMissoes)EditorGUILayout.EnumPopup("Matar Gentes", missoes.TiposMissoes);
DrawProperty(serializedTarget, string.Format("MissoesJogasso.Array.data[{0}].MatarGente", i));
break;
case ClasseTest.TiposDeMissoes.TipoPegarItem:
missoes.TiposMissoes = (ClasseTest.TiposDeMissoes)EditorGUILayout.EnumPopup("Pegar Item", missoes.TiposMissoes);
DrawProperty(serializedTarget, string.Format("MissoesJogasso.Array.data[{0}].PegarItem", i));
break;
}
}
}
private void DrawProperty(SerializedObject serializedObject, string propertyName)
{
var property = serializedObject.FindProperty(propertyName);
serializedObject.Update();
EditorGUILayout.PropertyField(property, true);
serializedObject.ApplyModifiedProperties();
}
}
In Unity:
Hope this works.

Related

How do I store the same types of classes that have a different generic inside of a list?

I've been tinkering with this and I have a 'RespawnManager' that I want to use to manage my multiple 'SpawnPoint' classes with different generics but it ended up forcing me to use generics for my 'RespawnManager' which I don't want.
Let's say I had a SpawnPoint<T> class and I made a SpawnPoint<Enemy1>, SpawnPoint<Enemy2>, and SpawnPoint<Enemy3>. Is there any way I can make a list that can just manage multiple 'SpawnPoint's of any generic?
Base class:
public abstract class SpawnPoint<T> : MonoBehaviour
{
//how big the range of the spawn protection is
public int spawnProtectionRadius = 20;
public bool Occupied { get; set; }
public bool IsInSpawn(Transform target)
{
Debug.Log((target.position - transform.position).magnitude);
if ((target.position - transform.position).magnitude <= spawnProtectionRadius)
{
return true;
}
return false;
}
public abstract T Get();
}
Class that Inherits this
public class SeaMineSpawnPoint : SpawnPoint<Seamine>
{
public override Seamine Get()
{
return SeaMineObjectPool.PoolInstance.Get();
}
private void Start()
{
RespawnManager<Seamine>.respawnManager.AddSpawn(this);
}
}
Respawn manager:
public class RespawnManager<T> : MonoBehaviour where T : Component
{
public static RespawnManager<T> respawnManager;
[SerializeField]
private List<Transform> playerList;
[SerializeField]
private List<SpawnPoint<T>> spawnpoints;
private float respawnCounter;
private void Awake()
{
respawnManager = this;
}
private void Start()
{
foreach (SpawnPoint<T> sp in spawnpoints)
{
Debug.Log(sp.transform.position);
}
}
public void AddSpawn(SpawnPoint<T> spawnPoint)
{
spawnpoints.Add(spawnPoint);
}
public void RespawnSeaMines()
{
if (respawnCounter > 5)
{
respawnCounter = 0;
foreach (SpawnPoint<T> sp in spawnpoints)
{
foreach (Transform playerT in playerList)
{
if (sp.Occupied == false && !sp.IsInSpawn(playerT))
{
Component ourGameObj = sp.Get();
ourGameObj.transform.position = sp.transform.position;
ourGameObj.gameObject.SetActive(true);
sp.Occupied = true;
return;
}
}
}
}
}
private void Update()
{
respawnCounter += Time.deltaTime;
Debug.Log(respawnCounter);
RespawnSeaMines();
}
}
ObjectPool
//Class that's used for object pooling of different types.
//'T' must be a Unity component or it will error.
public abstract class ObjectPool<T> : MonoBehaviour where T : Component
{
//An object with this specific component that we use to copy.
[SerializeField]
private T prefab;
//Makes sure that only 1 coroutine runs at a time
private bool coroutineIsRunning;
//The singleton instance to our object pool.
public static ObjectPool<T> PoolInstance { get; private set; }
//A queue is used to organize plus activate and deactivate objects which
//have this component.
protected Queue<T> objects = new Queue<T>();
private void Awake()
{
//Set the instance of this pool to this class instance. Only one of these can be set.
if (PoolInstance != null)
{
throw new System.Exception("Singleton already exists. Cannot make another copy of this");
}
PoolInstance = this;
}
public T Get()
{
//If the queue happens to be empty, then add a brand new component.
if (objects.Count == 0) AddObjects(1);
//Returns the generic component and removes it from the queue.
return objects.Dequeue();
}
public void ReturnToPool(T objectToReturn)
{
//Disables the game object that the T component is attached to.
objectToReturn.gameObject.SetActive(false);
//Stores the T component in the queue.
objects.Enqueue(objectToReturn);
}
public void AddObjects(int count)
{
for (int i = 0; i < count; i++)
{
//Create a new copy of the prefab.
//The prefab is a game object with the T component attached to it.
T newObject = Instantiate(prefab);
//Disable the game object.
newObject.gameObject.SetActive(false);
//Add the T component to the queue.
//The T component is attached to the game object we created earlier.
objects.Enqueue(newObject);
}
}
public T GetWithDelay(int time)
{
T genericToReturn = null;
if (!coroutineIsRunning)
{
coroutineIsRunning = true;
StartCoroutine(GetCoroutine(time, genericToReturn));
}
return genericToReturn;
}
private IEnumerator GetCoroutine(int time, T generic)
{
float counter = 0;
while (counter < time)
{
counter += Time.deltaTime;
yield return null;
}
generic = Get();
generic.gameObject.SetActive(true);
coroutineIsRunning = false;
}
}
You should be able to declare your spawnpoints property in RespawnManager as a List<SpawnPoint<Component>> instead of List<SpawnPoint<T>>. That will allow you to get rid of the <T> type parameter entirely from RespawnManager and make it non-generic.

I cant use the variable in a different script (unity 2D). Any suggestions

i want to use variables levelButtons and levelunlocked from the script below :-
public class LevelSelector : MonoBehaviour
{
public Button[] levelButtons;
private void Start()
{
int levelunlocked = 1;
int levelReached = PlayerPrefs.GetInt("levelReached", levelunlocked);
for (int i = 0; i < levelButtons.Length; i++)
{
if (i + 1 > levelReached)
{
levelButtons[i].interactable = false;
}
}
}
}
and use them in this script below. it would be a huge help.
public class Level : MonoBehaviour
{
[SerializeField] int breakableBlocks;
SceneLoader sceneloader;
private void Start()
{
sceneloader = FindObjectOfType<SceneLoader>();
}
public void CountBreakableBlocks()
{
breakableBlocks++;
}
public void BlockDestroyed()
{
breakableBlocks--;
if (breakableBlocks <= 0)
{
sceneloader.LoadWinScreen();
}
}
}
any ideas?? its unity c#.
You can declare levelunlocked and levelReached outside of the Start method and make them public, like this:
public class LevelSelector : MonoBehaviour
{
public Button[] levelButtons;
public int levelunlocked = 1;
public int levelReached;
private void Start()
{
levelReached = PlayerPrefs.GetInt("levelReached", levelunlocked);
for (int i = 0; i < levelButtons.Length; i++)
{
if (i + 1 > levelReached)
{
levelButtons[i].interactable = false;
}
}
}
}
Then to access levelunlocked and levelReached, get a reference to LevelSelector by using GetComponent, or make the fields static and access them by doing LevelSelector.levelunlocked or LevelSelector.levelReached.

How to access arrays of prefabs through the inspector in a given hierarchy

The script below is added to an empty game object WeaponGroup, which can be populated using the editor. I have made a new game object WeaponGroups which should have a script SetupWeaponGroupsScript. How can I transfer the properties below so that each WeaponGroup (SetupWeaponGroupsScript will have an array of WeaponGroup objects) is setup in a way similar to what is done below, so that I make the SetupWeaponsScript properties hidden to the inspector and populate them through SetupWeaponGroupsScript?
public class SetupWeaponsScript {
// Here's our tuple combining a weapon prefab and a direction.
[System.Serializable]
public struct DirectedWeapon {
public WeaponScript weapon;
public WeaponScript.Direction direction;
}
// The prefabs array is now this tuple type.
public DirectedWeapon[] weaponPrefabs;
public WeaponScript[] weapons;
void Start ()
{
weapons = new WeaponScript[weaponPrefabs.Length];
for (int i = 0; i < weaponPrefabs.Length; i++)
{
// Using typed Instantiate to save a GetComponent call.
weapons[i] = Instantiate<WeaponScript>(weaponPrefabs[i].weapon);
weapons[i].weaponDir = weaponPrefabs[i].direction;
}
}
}
Graphically, I would like to have the following hierarchy in the editor for a new script SetupWeaponGroupsScript:
This should be close :
public class SetupWeaponsScript
{
[System.Serializable]
public struct DirectedWeaponGroup
{
public DirectedWeaponScript[] weaponPrefabs;
}
// Here's our tuple combining a weapon prefab and a direction.
[System.Serializable]
public struct DirectedWeapon
{
public WeaponScript weapon;
public WeaponScript.Direction direction;
}
// The prefabs array is now this tuple type.
public DirectedWeaponGroup[] weaponGroups;
public DirectedWeapon[] weaponPrefabs;
public WeaponScript[] weapons;
void Start()
{
weaponGroups = new DirectedWeaponScript[weaponGroups.Length];
for (int h = 0; h < weaponGroups.Length; h++)
{
weaponPrefabs = weaponGroups[h].weaponPrefabs;
weaponPrefabs[h] = Instantiate<DirectedWeaponScript>(weaponGroups[h].weaponPrefabs);
weapons = new WeaponScript[weaponPrefabs.Length];
for (int i = 0; i < weaponPrefabs.Length; i++)
{
// Using typed Instantiate to save a GetComponent call.
weapons[i] = Instantiate<WeaponScript>(weaponPrefabs[i].weapon);
weapons[i].weaponDir = weaponPrefabs[i].direction;
}
}
}

The member XXX cannot be used as a method or delegate

I have done copied this of a tutorial online, but when I compile it shows 2 errors as below:
Non-invocable member 'Feature.choices' cannot be used like a method
and
The member `Feature.choices' cannot be used as method or delegate
The last "choices" in the script has a red squiggly line. Any idea that might be wrong?
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[ExecuteInEditMode]
public class FeatureManager : MonoBehaviour {
public List<Feature> features;
public int currFeature;
void OnEnable()
{
LoadFeatures();
}
void OnDisable()
{
SaveFeatures();
}
void LoadFeatures()
{
features = new List<Feature>();
features.Add(new Feature(("hair"), transform.FindChild("hair").GetComponent<SpriteRenderer>()));
}
void SaveFeatures()
{
}
}
[System.Serializable]
public class Feature
{
public string ID;
public int currIndex;
public Sprite[] choices;
public SpriteRenderer renderer;
public Feature(string id, SpriteRenderer rend)
{
ID = id;
renderer = rend;
UpdateFeature();
}
public void UpdateFeature()
{
choices = Resources.LoadAll<Sprite>("Character/" + ID);
if (choices == null || renderer == null)
return;
if (currIndex < 0)
currIndex = choices.Length - 1;
if (currIndex >= choices.Length)
currIndex = 0;
renderer.sprite = choices(currIndex);
}
}
Change this
renderer.sprite = choices(currIndex);
to this:
renderer.sprite = choices[currIndex]; // use square brackets not parenthesis

How to add all MonoBehaviours of Scene into the List?

I've read an article 10000 UPDATE() CALLS.
Author uses UpdateManager. It has Update method which calls Update method in all of other MonoBehaviours of objects. It works faster than calling Update method from each MonoBehaviour separately.
This Manager looks like:
private ManagedUpdateBehavior[] list;
private void Start() {
list = GetComponents<ManagedUpdateBehavior>();
}
private void Update() {
var count = list.Length;
for (var i = 0; i < count; i++) {
// UpdateMe
list[i].UpdateMe();
}
}
And every objects now contains component with the code:
public class ManagedUpdateBehavior : MonoBehaviour {
// some variables
public void UpdateMe() {
// some logic
}
}
It's okay if 5-6-7-8- objects will be have that component.
But what if I have 100 objects? 1000? 10000?
How to find and add all ManagedUpdateBehaviors from all objects of scene? Should I use some recursive method on On Start? Cause every object may contain other objects with the script, they may contain the other objects etc...unlimited nesting
Also some objects can be instantiating dynamically...How to add their mono to manager? What is the right way?
Also some objects can be instantiating dynamically
Let each ManagedUpdateBehavior subscribe to the ManagedUpdateBehavior list by adding its instance to the ManagedUpdateBehavior instance. They should also unsubscribe from the ManagedUpdateBehavior when destroyed by removing itself from the ManagedUpdateBehavior instance.
Your new ManagedUpdateBehavior script:
public class ManagedUpdateBehavior : MonoBehaviour
{
UpdateSubscriber updateSUBSCR;
//Add it self to the List
void Start()
{
updateSUBSCR = GameObject.Find("UpdateSUBSCR").GetComponent<UpdateSubscriber>();
updateSUBSCR.addManagedUpdateBehavior(this);
}
//Remove it self from the List
void OnDestroy()
{
updateSUBSCR.removeManagedUpdateBehavior(this);
}
public void UpdateMe()
{
// some logic
Debug.Log("Update from: " + gameObject.name);
}
}
Create a GameObject called UpdateSUBSCR and attach the Script below to it:
public class UpdateSubscriber : MonoBehaviour
{
private List<ManagedUpdateBehavior> managedUpdateBehavior = new List<ManagedUpdateBehavior>();
public void addManagedUpdateBehavior(ManagedUpdateBehavior managedUB)
{
managedUpdateBehavior.Add(managedUB);
}
public void removeManagedUpdateBehavior(ManagedUpdateBehavior managedUB)
{
for (int i = 0; i < managedUpdateBehavior.Count; i++)
{
if (managedUpdateBehavior[i] == managedUB)
{
managedUpdateBehavior.RemoveAt(i);
break;
}
}
}
/*
public List<ManagedUpdateBehavior> getManagedUpdateBehaviorinstance
{
get
{
return managedUpdateBehavior;
}
}*/
public void updateAll()
{
for (int i = 0; i < managedUpdateBehavior.Count; i++)
{
managedUpdateBehavior[i].UpdateMe();
}
}
}
Then you can call it with:
public class Test : MonoBehaviour
{
UpdateSubscriber updateSUBSCR;
void Start()
{
updateSUBSCR = GameObject.Find("UpdateSUBSCR").GetComponent<UpdateSubscriber>();
}
void Update()
{
updateSUBSCR.callUpdateFuncs();
}
}
Be aware that array is faster than List but this is better than the having the Update() function in every script.
You can find and add all ManagedUpdateBehaviours using Object.FindObjectsOfType method.
You can add your dynamically instantiate objects using a static method that can be called from created game object to add it to list.
My suggestion is created a SceneUpdateManager, something like this:
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
public class SceneUpdateManager : MonoBehaviour {
private static List<ManagedUpdateBehavior> list;
private void Start ()
{
list = Object.FindObjectsOfType<ManagedUpdateBehavior> ().ToList ();
}
public static void AddBehavior(ManagedUpdateBehavior behaviour)
{
list.Add (behaviour);
}
private void Update () {
var count = list.Count;
for (var i = 0; i < count; i++)
{
list [i].UpdateMe();
}
}
}
Object.FindObjectsOfType does not return inactive objects.

Categories