So i’ve been trying to learn how to build my own custom editor tools by building a 2d level map editor and it seems to be working fine when i run it but if i make any changes, even commenting out a line in the editor script, or press the play button, i get an error from the TileGrid() in my editor script that says GetTile() in the level script has a null reference, but any new levels i make work perfectly fine. I know i’m probably missing something extremely simple to do with building an editor or a scriptable object but i don’t know what. If anyone can point me in the right direction as to where i’ve gone wrong, that’d be great!
TileBase
using UnityEngine;
using System.Collections;
public class TileBase {
private int tileValue;
public TileBase(){
tileValue = 0;
}
public int TileValue {
get {
return tileValue;
}
set {
tileValue = value;
}
}
}
Level Script
using UnityEngine;
using System.Collections;
[System.Serializable]
public class Level {
[SerializeField]private string levelName;
[SerializeField]private int levelWidth;
[SerializeField]private int levelHeight;
private TileBase[,] levelMap;
private int minSize = 5;
private int maxSize = 20;
public Level(string name, int width, int height){
levelName = name;
levelWidth = Mathf.Clamp(width, minSize, maxSize);
levelHeight = Mathf.Clamp(height,minSize,maxSize);
ResetTiles();
}
/// <summary>
/// Gets or sets the width of the level and then resets the map int array.
/// </summary>
/// <value>The width of the level.</value>
public int LevelWidth {
get {
return levelWidth;
}
set {
if(value != levelWidth){
levelWidth = value;
ResetTiles();
}
}
}
/// <summary>
/// Gets or sets the height of the level and then resets the map int array.
/// </summary>
/// <value>The height of the level.</value>
public int LevelHeight {
get {
return levelHeight;
}
set {
if(value != levelHeight){
levelHeight = value;
ResetTiles();
}
}
}
/// <summary>
/// Gets the tile.
/// </summary>
/// <returns>The tile.</returns>
/// <param name="x">The x coordinate.</param>
/// <param name="y">The y coordinate.</param>
public TileBase GetTile(int x, int y){
TileBase temp = levelMap [x, y]; // <-----Unity flags this line as causing an error
return temp;
}
/// <summary>
/// Resets the tiles.
/// </summary>
public void ResetTiles(){
levelMap = new TileBase[levelWidth,levelHeight];
for (int x = 0; x < levelWidth; x++) {
for(int y = 0; y < levelHeight; y++){
levelMap[x,y] = new TileBase();
}
}
Debug.Log ("Tiles Reset");
}
All the levels are added to this level storage
Level Storage
public class LevelStorage : ScriptableObject {
[SerializeField]
private List<Level> allLevels;
private int numberOfLevels;
/// <summary>
/// Initializes the list.
/// </summary>
public void InitializeList(){
allLevels = new List<Level> ();
}
/// <summary>
/// Gets the number of levels.
/// </summary>
/// <value>The number of levels.</value>
public int NumberOfLevels {
get {
numberOfLevels = allLevels.Count;
return numberOfLevels;
}
}
/// <summary>
/// Gets the level.
/// </summary>
/// <returns>The level.</returns>
/// <param name="levelNumber">Level number.</param>
public Level GetLevel(int levelNumber){
Level temp;
if (numberOfLevels == 0) {
temp = new Level ("Default", 10,10);
allLevels.Add(temp);
return temp;
}
if (levelNumber < allLevels.Count && levelNumber > -1) {
Debug.Log("test");
if(allLevels[levelNumber] != null){
temp = allLevels [levelNumber];
return temp;
} else {
Debug.Log("error");
return null;
}
} else {
Debug.Log("Level:" + levelNumber.ToString() + " Does not exist");
return allLevels[allLevels.Count-1];
}
}
/// <summary>
/// Adds the new level.
/// </summary>
/// <param name="newLevel">New level.</param>
public void AddNewLevel(Level newLevel){
allLevels.Add (newLevel);
newLevel.ResetTiles ();
numberOfLevels = allLevels.Count;
}
/// <summary>
/// Removes the level.
/// </summary>
/// <param name="levelToRemove">Level to remove.</param>
public void RemoveLevel(int levelToRemove){
allLevels.RemoveAt (levelToRemove);
numberOfLevels = allLevels.Count;
}
}
This is my editor script at the moment
Custom Editor Script
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
public class LevelEditor : EditorWindow {
private LevelStorage levelStorage;
private string database = "Assets/Resources/LevelDatabase.Asset";
private Level openLevel;
private Vector2 levelSelectScrollPos;
[MenuItem("Level Editor/Open Editor")]
public static void Init(){
LevelEditor levelEditor = EditorWindow.GetWindow<LevelEditor> ();
levelEditor.Show ();
}
void OnEnable(){
levelStorage = AssetDatabase.LoadAssetAtPath (database, typeof(LevelStorage))as LevelStorage;
if (levelStorage == null) {
levelStorage = ScriptableObject.CreateInstance<LevelStorage>();
AssetDatabase.CreateAsset(levelStorage,database);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
levelStorage.InitializeList();
Level temp = new Level("Default",5,5);
levelStorage.AddNewLevel (temp);
RefreshAndSave();
}
openLevel = levelStorage.GetLevel (0);
}
void OnLostFocus(){
}
void OnFocus(){
RefreshAndSave ();
openLevel = levelStorage.GetLevel (0);
}
void OnGUI(){
GUILayout.BeginHorizontal ();
GUILayout.BeginVertical (GUILayout.Width(150));
GUILayout.Label ("# of Levels:" + levelStorage.NumberOfLevels.ToString());
LevelSelector ();
GUILayout.EndVertical ();
GUILayout.BeginVertical ();
LevelDetails ();
TileGrid ();
GUILayout.EndVertical ();
GUILayout.EndHorizontal ();
}
void RefreshAndSave(){
AssetDatabase.Refresh();
EditorUtility.SetDirty(levelStorage);
AssetDatabase.SaveAssets();
}
void LevelSelector(){
GUILayout.Label ("Levels");
levelSelectScrollPos = GUILayout.BeginScrollView (levelSelectScrollPos,false,true);
for (int i = 0; i < levelStorage.NumberOfLevels; i++) {
GUILayout.BeginHorizontal ();
if(GUILayout.Button ("Level: " + i.ToString(),GUILayout.Width(100),GUILayout.Height(20))){
if(openLevel == levelStorage.GetLevel(i)){
// Debug.Log("same level selected ");
} else {
openLevel = levelStorage.GetLevel(i);
// Debug.Log("different level selected ");
}
// Debug.Log ("Press");
// Debug.Log (openLevel.GetTile(0,0).TileValue.ToString());
}
if(GUILayout.Button ("X",GUILayout.Width(20),GUILayout.Height(20))){
levelStorage.RemoveLevel(i);
}
GUILayout.EndHorizontal ();
}
if(GUILayout.Button ("+",GUILayout.Width(100),GUILayout.Height(20))){
AddNewLevel();
}
GUILayout.EndScrollView ();
}
void AddNewLevel(){
Level temp = new Level (levelStorage.NumberOfLevels.ToString(), 5, 5);
temp.ResetTiles ();
levelStorage.AddNewLevel (temp);
RefreshAndSave ();
}
void LevelDetails(){
GUILayout.BeginVertical ();
GUILayout.BeginHorizontal ();
GUILayout.Label (openLevel.LevelName);
GUILayout.EndHorizontal ();
GUILayout.BeginHorizontal ();
GUILayout.Label ("Width: " + openLevel.LevelWidth,GUILayout.Width(100));
openLevel.LevelWidth = (int)GUILayout.HorizontalSlider(Mathf.RoundToInt(openLevel.LevelWidth),openLevel.MinSize,openLevel.MaxSize);
GUILayout.EndHorizontal ();
GUILayout.BeginHorizontal ();
GUILayout.Label ("Height: " + openLevel.LevelHeight,GUILayout.Width(100));
openLevel.LevelHeight = (int)GUILayout.HorizontalSlider (Mathf.RoundToInt(openLevel.LevelHeight), openLevel.MinSize, openLevel.MaxSize);
GUILayout.EndHorizontal ();
GUILayout.EndVertical ();
}
void TileGrid(){
for (int y = openLevel.LevelHeight - 1; y > -1; y--) {
GUILayout.BeginHorizontal();
for(int x = 0; x < openLevel.LevelWidth; x++){
if(openLevel.GetTile(x,y).TileValue != null){
if(GUILayout.Button(openLevel.GetTile(x,y).TileValue.ToString(),GUILayout.Width(25),GUILayout.Height(25))){
openLevel.GetTile(x,y).TileValue = openLevel.GetTile(x,y).TileValue + 1;
RefreshAndSave();
}
}
}
GUILayout.EndHorizontal();
}
}
}