I recommend you avoid custom inspectors if at all possible unless you’re making editor store content or you’re 100% certain the shape of what you’re decorating with an inspector is not ever going to change. Otherwise you’re often just adding extra classes to maintain and keep in sync with your most likely constant changing game classes and anything that makes you not want to adjust the composition of your game code simply because you’ve built editor tools against it’s current shape should be avoided.
You might want to do some reading on data structures and relational databases. A very large part of programming is modelling relationships like MonsterLevel >> AvaliableMoveSet where you might want to model different monsters having access to some moves at earlier levels than others etc. What your describing is actually a fairly simple set of relationships to model.
a) A monster’s state can change to represent its strength as a level
b) Monster move definitions are shared among all monsters.
c) Access to moves is gated by a monsters current level.
d) Different monsters have different restrictions as to what moves they can access at what level.
That might look like this in code:
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Assertions;
public class Monster : MonoBehaviour
{
public int level = 1;
public Dictionary<int,HashSet<Move>> moves;
}
public class Move
{
// declarative move model
}
If you wanted to serialize that move data and make it editable in the inspector you can wrap it up in a list and model the relationship declaratively like so:
public class Monster : MonoBehaviour
{
public int level = 1;
public Dictionary<int,HashSet<Move>> moves;
[SerializeField] List<MoveGroup> moveGroup;
// etc...
}
[Serializable] // this makes unity save the data to the scene/prefab when exposed in the inspector.
public class MoveGroup
{
public int level;
public List<Move> moves;
}
Then add some code in awake to map that into the dictionary:
void Awake()
{
moves.Clear();
foreach (var item in moveGroup)
{
if (moves.ContainsKey(item.level))
{
Debug.LogWarning($"Monster {name} has multiple move sets for level {item.level}, moves will be merged.");
moves[item.level].UnionWith(item.moves);
}
else
{
moves.Add(item.level, new HashSet<Move>(item.moves));
}
}
}
You could then add some methods for access the moves:
public List<Move> GetAllValidMoves()
{
return moves.Where(x => x.Key <= level)
.SelectMany(x => x.Value)
.ToList();
}
public List<Move> GetMovesForLevel(int moveLevel)
{
Assert.IsTrue(moveLevel <= level, $"Monster {name} attempted to access moves for level {moveLevel} when it is only level {level}");
if (moves.TryGetValue(moveLevel, out var movesForLevel))
{
return new List<Move>(movesForLevel); // return a copy of the list so it can't be modified
}
return new List<Move>(); // empty list is no moves for give level.
}