Upload
lviv-startup-club
View
149
Download
3
Embed Size (px)
DESCRIPTION
Лев Лукомський "Розробка власного 2D-редактора рівнів на Unity"
Citation preview
Розробка власного 2D-редактора рівнів
на Unity
Трохи про гру та її виклики
прив’язка до сіткирізні конвеєри труби та інше...
Готові асети vs свій велосипедSpriteTileTile BuilderUniTileTilerU2DEXNostalgia...ціни: $15-30
різноманітні можливості з асетів:
● зручності редагування● додаткові скрипти● колекції картинок● оптимізації drawcall’ів● геренерація оптимальних колайдерів● запікання шляхів● прив’язка до сітки - єдине що нам треба
Вікно редактора. початки
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);
}
}
Assets/Editor/LevelEditorWindow.cs:
Зберігаємо дані. EditorPrefs
...
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);
}
...
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;
}
}
}
...
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;
}
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);
}
Assets/Editor/LevelEditorWindow.cs:
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; }
Інтерактивні елементи
рівня
Assets/Editor/LevelEditorWindow.cs:
if (snapped) { rootTrans = selectedTrans.RootIfExists(); else if (rootTrans.CompareTag("Conveyer") && selectedTrans != rootTrans) HandleConveyer(); ... }
void HandleConveyer()
{
...
conveyer.Resize(...);
EditorUtility.SetDirty(conveyer);
}
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; }
… }}
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); } }
Скрипти-фіксери
всіх рівнів
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); }
Демонстрація редактора
Дякую за увагу :)
Facebook: Лев Лукомськийemail: [email protected]: lllukom