2. Трохи про гру та її виклики
прив’язка до сітки
різні конвеєри труби та інше...
3. Готові асети vs свій велосипед
SpriteTile
Tile Builder
UniTile
Tiler
U2DEX
Nostalgia
...
ціни: $15-30
різноманітні можливості з асетів:
● зручності редагування
● додаткові скрипти
● колекції картинок
● оптимізації drawcall’ів
● геренерація оптимальних колайдерів
● запікання шляхів
● прив’язка до сітки - єдине що нам треба
4. Вікно редактора.
початки
Assets/Editor/LevelEditorWindow.cs:
public class LevelEditorWindow : EditorWindow
{
public static bool editorEnabled = true;
[MenuItem("Window/Level Editor %_l")]
public static void Init()
{
var window = (LevelEditorWindow)GetWindow(typeof(LevelEditorWindow));
window.title = "Level Editor";
window.minSize = new Vector2(100, 50);
}
public void OnGUI()
{
editorEnabled = EditorGUILayout.Toggle("Enabled", editorEnabled);
}
}
5. Зберігаємо дані.
EditorPrefs
Assets/Editor/LevelEditorWindow.cs:
...
public void OnFocus()
{
if (EditorPrefs.HasKey("LevelEditorEnabled"))
editorEnabled = EditorPrefs.GetBool("LevelEditorEnabled");
}
public void OnLostFocus()
{
EditorPrefs.SetBool("LevelEditorEnabled", editorEnabled);
}
public void OnDestroy()
{
EditorPrefs.SetBool("LevelEditorEnabled", editorEnabled);
}
...
6. Головна логіка
редактора
Assets/Editor/LevelEditorWindow.cs:
Vector3 lastSelectedPos;
Transform selectedTrans;
public void Update()
{
if (editorEnabled && !EditorApplication.isPlaying && Selection.transforms.Length == 1)
{
selectedTrans = SelectImportantObj();
if (selectedTrans.position != lastSelectedPos)
{
var snapped = SnapSelected();
if (snapped) { … }
lastSelectedPos = selectedTrans.position;
}
}
}
...
7. Вибір головного
об’єкта
Assets/Editor/LevelEditorWindow.cs:
static Transform SelectImportantObj()
{
var selectedTrans = Selection.activeTransform;
var rootTrans = selectedTrans.root;
if (rootTrans != null && rootTrans != selectedTrans)
{
if (rootTrans.CompareTag("Toggle") || rootTrans.CompareTag("Press") || ...)
{
Selection.activeTransform = rootTrans;
}
else if (rootTrans.CompareTag("Conveyer"))
{
Transform trans;
if ((trans = selectedTrans.ClosestWithTag("ConveyerCorner")) != null)
Selection.activeTransform = trans;
else
Selection.activeTransform = rootTrans;
}
8. Прив’язка з
різними
якорями
Assets/Editor/LevelEditorWindow.cs:
enum Snap { Half, One, OneHalf }
bool SnapSelected()
{
if (selectedTrans.CompareTag("Arrow"))
return false;
if (selectedTrans.CompareTag("MainCamera") || ...)
{
return SnapCustom(selectedTrans, Snap.Half, Snap.Half);
}
if (selectedTrans.CompareTag("Ground") || ...)
{
return SnapCustom(selectedTrans, Snap.Half, Snap.OneHalf);
}
…
return SnapCustom(selectedTrans, Snap.OneHalf, Snap.OneHalf);
}
static bool SnapCustom(Transform trans, Snap snapX,
Snap snapY)
{
var pos = trans.position;
var x = SnapCustomVal(pos.x, snapX);
var y = SnapCustomVal(pos.y, snapY);
if (Mathf.Abs(x - pos.x) > Mathf.Epsilon || Mathf.Abs(y -
pos.y) > Mathf.Epsilon)
{
trans.position = new Vector3(x, y, pos.z);
return true;
}
return false;
}
9. Інтерактивні
елементи
рівня
Assets/Editor/LevelEditorWindow.cs:
if (snapped)
{
rootTrans = selectedTrans.RootIfExists();
else if (rootTrans.CompareTag("Conveyer") && selectedTrans != rootTrans)
HandleConveyer();
...
}
void HandleConveyer()
{
...
conveyer.Resize(...);
EditorUtility.SetDirty(conveyer);
}
10. Custom Editors
база
Assets/Editor/ConveyerEditor.cs:
[CustomEditor(typeof(Conveyer))]
public class ConveyerEditor : Editor
{
Conveyer conveyer;
protected void OnEnable()
{
if (EditorUtility.IsPersistent(target) || !LevelEditorWindow.editorEnabled)
return;
conveyer = (Conveyer) target;
}
public override void OnInspectorGUI()
{
if (EditorUtility.IsPersistent(target) || !LevelEditorWindow.editorEnabled)
{
DrawDefaultInspector();
return;
}
…
}
}
11. Custom Editors
дія на зміну поля
Assets/Editor/ConveyerEditor.cs:
public override void OnInspectorGUI()
{
...
GUI.changed = false;
var canChangeDirBefore = conveyer.canChangeDir;
…
DrawDefaultInspector();
if (GUI.changed)
{
if (canChangeDirBefore != conveyer.canChangeDir)
conveyer.UpdateDirArrowDisabled();
…
EditorUtility.SetDirty(conveyer);
}
}
12. Скрипти-
фіксери
всіх рівнів
Assets/Editor/LevelFixer.cs:
public class LevelFixer : MonoBehaviour
{
[MenuItem("Custom/Fix Conveyers")]
public static void FixLevelConveyers()
{
var conveyers = FindObjectsOfType<Conveyer>();
foreach (var conveyer in conveyers)
{
// ... fix conveyer …
EditorUtility.SetDirty(conveyer.gameObject);
}
}
[MenuItem("Custom/Fix Conveyers (All levels)")]
public static void FixAllLevelsConveyers()
{
RunOnAllLevels(FixLevelConveyers);
}
}
static void RunOnAllLevels(Action action)
{
if (!EditorApplication.SaveCurrentSceneIfUserWantsTo())
return;
var currentScene = EditorApplication.currentScene;
foreach (var scene in EditorBuildSettings.scenes)
if (scene.path.Contains("Level"))
{
EditorApplication.OpenScene(scene.path);
action();
EditorApplication.SaveScene();
}
EditorApplication.OpenScene(currentScene);
}