Skip to content

Instantly share code, notes, and snippets.

@fangzhangmnm
Last active March 1, 2022 04:51
Show Gist options
  • Select an option

  • Save fangzhangmnm/bdb16f3970c2158c3bb829bf2685bb94 to your computer and use it in GitHub Desktop.

Select an option

Save fangzhangmnm/bdb16f3970c2158c3bb829bf2685bb94 to your computer and use it in GitHub Desktop.

Revisions

  1. fangzhangmnm revised this gist Mar 1, 2022. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion GridPaintEditor.cs
    Original file line number Diff line number Diff line change
    @@ -233,7 +233,9 @@ bool ShowPreviewObject(out Vector3 placePos,out Quaternion placeRot, out Bounds
    bounds.max = new Vector3((x0 + paintWidthX) * cellSize, 1, (z0 + paintWidthZ) * cellSize);
    bounds.size = bounds.size - Vector3.one * cellSize * 0.01f;

    placePos = a + (b + c) / 2 - d-palette[paletteIndex].transform.position;
    Vector3 e= palette[paletteIndex].transform.position;
    Rotate(rotate, ref e.x, ref e.z);
    placePos = a + (b + c) / 2 - d + e;
    placeRot= container.rotation * Quaternion.Euler(0, 90 * rotate, 0) * palette[paletteIndex].transform.rotation;

    previewObject.transform.position = placePos;
  2. fangzhangmnm revised this gist Feb 23, 2022. 1 changed file with 5 additions and 3 deletions.
    8 changes: 5 additions & 3 deletions GridPaintEditor.cs
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,6 @@
    //https://www.synnaxium.com/en/2019/01/unity-custom-map-editor-part-1/
    //https://gist.github.com/fangzhangmnm/bdb16f3970c2158c3bb829bf2685bb94

    // credits: https://www.synnaxium.com/en/2019/01/unity-custom-map-editor-part-1/

    #if UNITY_EDITOR
    using UnityEngine;
    @@ -194,8 +196,8 @@ bool ShowPreviewObject(out Vector3 placePos,out Quaternion placeRot, out Bounds
    float pmz = (plz + phz) / 2;
    float prmx = pmx, prmz = pmz; Rotate(rotate, ref prmx, ref prmz);

    Debug.Log(palette[paletteIndex].transform.position);
    Debug.Log($"{prefabBounds.min.x} {plx} {prefabBounds.min.z} {plz}");
    //Debug.Log(palette[paletteIndex].transform.position);
    //Debug.Log($"{prefabBounds.min.x} {plx} {prefabBounds.min.z} {plz}");


    int paintWidthX = pwx, paintWidthZ = pwz;
  3. fangzhangmnm revised this gist Feb 23, 2022. No changes.
  4. fangzhangmnm revised this gist Feb 23, 2022. No changes.
  5. fangzhangmnm created this gist Feb 23, 2022.
    348 changes: 348 additions & 0 deletions GridPaintEditor.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,348 @@
    //https://www.synnaxium.com/en/2019/01/unity-custom-map-editor-part-1/

    #if UNITY_EDITOR
    using UnityEngine;
    using UnityEditor;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;

    public class GridPaintEditor : EditorWindow
    {
    private bool paintMode = false;
    private string path = "Assets/GridPaintPalette";
    [SerializeField]
    private List<GameObject> palette = new List<GameObject>();
    private List<GUIContent> paletteIcons = new List<GUIContent>();
    [SerializeField]
    private int paletteIndex;
    private Vector2 scrollPosition;

    private Transform container;
    private int selectedY;
    private float cellHeight = 1f;
    private float cellSize = 1f;
    private int rotate = 0;
    private bool halfIntegerX = false;
    private bool halfIntegerZ = false;
    private GameObject previewObject;
    private Bounds lastPlace = new Bounds();
    private Vector3 oldA = Vector3.zero;
    private bool mouseMoved = true;
    [MenuItem("Window/GridPaint")]
    private static void ShowWindow()
    {
    EditorWindow.GetWindow(typeof(GridPaintEditor));
    }
    void OnFocus()
    {
    SceneView.duringSceneGui -= this.OnSceneGUI;
    SceneView.duringSceneGui += this.OnSceneGUI;
    }
    void OnDestroy()
    {
    SceneView.duringSceneGui -= this.OnSceneGUI;
    }

    private void LoadPalette()
    {
    palette.Clear();
    paletteIcons.Clear();
    string[] prefabFiles = System.IO.Directory.GetFiles(path, "*.prefab");
    foreach (string prefabFile in prefabFiles)
    {
    GameObject prefab = AssetDatabase.LoadAssetAtPath(prefabFile, typeof(GameObject)) as GameObject;
    if (prefab.GetComponentInChildren<Renderer>() != null)
    {
    palette.Add(prefab);
    var guic = new GUIContent(prefab.name);
    guic.image = null;
    paletteIcons.Add(guic);
    }
    }
    RefreshPaletteIcons(0, 40);
    paletteIndex = 0;
    EnterPaintModeAndFocus();
    }
    private void RefreshPaletteIcons(int begin, int end)
    {
    for (int i = Mathf.Max(0, begin); i < Mathf.Min(palette.Count, end); ++i)
    paletteIcons[i].image = AssetPreview.GetAssetPreview(palette[i]);
    }
    private void OnGUI()
    {
    GUILayout.BeginHorizontal();
    container = EditorGUILayout.ObjectField("Container", container, typeof(Transform), true) as Transform;
    /*if (GUILayout.Button("Clear"))
    {
    for (int i = container.childCount - 1; i >= 0; --i)
    {
    Undo.DestroyObjectImmediate(container.GetChild(i));
    }
    }*/
    if (container == null)
    if (GUILayout.Button("Create"))
    container = new GameObject("container").transform;
    GUILayout.EndHorizontal();
    cellSize = EditorGUILayout.FloatField("Grid Size", cellSize);
    cellHeight = EditorGUILayout.FloatField("Grid Height", cellHeight);
    selectedY = EditorGUILayout.IntField("Height Layer[Alt+MouseWheel]", selectedY);

    GUILayout.BeginHorizontal();
    GUILayout.Label("Align to Center?[C]");
    halfIntegerX = GUILayout.Toggle(halfIntegerX, "");
    halfIntegerZ = GUILayout.Toggle(halfIntegerZ, "");
    GUILayout.Label("Rotation[R]");
    rotate = EditorGUILayout.IntSlider("", rotate, 0, 3);
    GUILayout.EndHorizontal();

    bool oldPaintMode = paintMode;
    paintMode = GUILayout.Toggle(paintMode, (paintMode ? "End Paint[1]" : "Start Paint[1]"), "Button", GUILayout.Height(60f));
    if (container == null) paintMode = false;
    if (!oldPaintMode && paintMode)
    EnterPaintModeAndFocus();

    GUILayout.Space(10);



    GUILayout.Label("Prefab Palette Folder");
    GUILayout.BeginHorizontal();
    path = GUILayout.TextField(path);
    if (GUILayout.Button("Load")) LoadPalette();
    GUILayout.EndHorizontal();
    if (paletteIndex < palette.Count)
    {
    GUILayout.Label(palette[paletteIndex].name);
    }
    else
    {
    GUILayout.Label("");
    }

    scrollPosition = GUILayout.BeginScrollView(scrollPosition);
    int xCount = Mathf.Max(1, Mathf.FloorToInt((EditorGUIUtility.currentViewWidth - 30) / 128));
    RefreshPaletteIcons(Mathf.FloorToInt(scrollPosition.y / 135) * xCount - 10, Mathf.FloorToInt(scrollPosition.y / 135) * xCount + 30);

    GUIStyle guis = new GUIStyle(GUI.skin.button);
    guis.fixedWidth = 128;
    guis.fixedHeight = 135;
    guis.wordWrap = true;
    guis.imagePosition = ImagePosition.ImageAbove;
    guis.padding = new RectOffset(3, 3, 3, 3);

    int newIndex = GUILayout.SelectionGrid(paintMode ? paletteIndex : -1, paletteIcons.ToArray(), xCount, guis, GUILayout.Width(xCount * 128));

    if (newIndex != -1)
    {
    paintMode = true;
    if (paletteIndex != newIndex)
    EnterPaintModeAndFocus();
    paletteIndex = newIndex;
    }
    HidePreviewObject();
    GUILayout.EndScrollView();

    }
    void Rotate(int rotate, ref float x, ref float z)
    {
    float x0 = x, z0 = z;
    switch (rotate)
    {
    case 0: x = x0; z = z0; break;
    case 1: x = z0; z = -x0; break;
    case 2: x = -x0; z = -z0; break;
    case 3: x = -z0; z = x0; break;
    }
    }
    static Bounds getCompoundGameobjectBound(GameObject g)
    {
    var a = g.GetComponentsInChildren<Renderer>();
    if (a.Length == 0) return new Bounds();
    Bounds b = a[0].bounds; //new Bounds contains original point dont use that
    for (int i = 0; i < a.Length; ++i)
    b.Encapsulate(a[i].bounds);
    return b;
    }
    bool ShowPreviewObject(out Vector3 placePos,out Quaternion placeRot, out Bounds bounds)
    {
    placePos = Vector3.zero;
    placeRot = Quaternion.identity;
    bounds = new Bounds();
    if (paletteIndex >= palette.Count) { paintMode = false; return false; }
    if (container == null) return false;

    if (previewObject == null)
    {
    previewObject = PrefabUtility.InstantiatePrefab(palette[paletteIndex]) as GameObject;
    previewObject.name = "[PREVIEW]";
    }

    Bounds prefabBounds = getCompoundGameobjectBound(palette[paletteIndex]);
    float biasX = halfIntegerX ? 0.5f : 0;
    float biasZ = halfIntegerZ ? 0.5f : 0;

    float plx = (Mathf.RoundToInt(prefabBounds.min.x / cellSize + biasX - 0.1f) - biasX + 0.1f) * cellSize;
    float plz = (Mathf.RoundToInt(prefabBounds.min.z / cellSize + biasZ - 0.1f) - biasZ + 0.1f) * cellSize;
    float phx = (Mathf.RoundToInt(prefabBounds.max.x / cellSize + biasX + 0.1f) - biasX - 0.1f) * cellSize;
    float phz = (Mathf.RoundToInt(prefabBounds.max.z / cellSize + biasZ + 0.1f) - biasZ - 0.1f) * cellSize;
    int pwx = Mathf.RoundToInt((phx - plx) / cellSize);
    int pwz = Mathf.RoundToInt((phz - plz) / cellSize);
    if (pwx == 0) { pwx = 1; phx += cellSize; }
    if (pwz == 0) { pwz = 1; phz += cellSize; }
    float pmx = (plx + phx) / 2;
    float pmz = (plz + phz) / 2;
    float prmx = pmx, prmz = pmz; Rotate(rotate, ref prmx, ref prmz);

    Debug.Log(palette[paletteIndex].transform.position);
    Debug.Log($"{prefabBounds.min.x} {plx} {prefabBounds.min.z} {plz}");


    int paintWidthX = pwx, paintWidthZ = pwz;
    if (rotate == 1 || rotate == 3) { paintWidthX = pwz; paintWidthZ = pwx; }

    Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
    Plane plane = new Plane(container.up, container.position + container.up * cellHeight * selectedY);
    float enter;
    if (!plane.Raycast(ray, out enter))
    {
    HidePreviewObject();
    return false;
    }
    Vector3 intersection = ray.origin + enter * ray.direction;
    Vector3 coord = container.InverseTransformPoint(intersection);

    int x0 = Mathf.RoundToInt(coord.x / cellSize - paintWidthX / 2f);
    int z0 = Mathf.RoundToInt(coord.z / cellSize - paintWidthZ / 2f);

    Vector3 newA = container.TransformPoint(new Vector3(x0 * cellSize, 0, z0 * cellSize));
    if (mouseMoved) oldA = newA;
    Vector3 a = oldA + container.up * cellHeight * selectedY;

    Vector3 b = container.right * cellSize * paintWidthX;
    Vector3 c = container.forward * cellSize * paintWidthZ;
    Vector3 d = container.right * prmx+ container.forward * prmz;
    Handles.DrawLine(a, a + b);
    Handles.DrawLine(a + b, a + b + c);
    Handles.DrawLine(a + b + c, a + c);
    Handles.DrawLine(a + c, a);

    bounds.min = new Vector3(x0 * cellSize, 0, z0 * cellSize);
    bounds.max = new Vector3((x0 + paintWidthX) * cellSize, 1, (z0 + paintWidthZ) * cellSize);
    bounds.size = bounds.size - Vector3.one * cellSize * 0.01f;

    placePos = a + (b + c) / 2 - d-palette[paletteIndex].transform.position;
    placeRot= container.rotation * Quaternion.Euler(0, 90 * rotate, 0) * palette[paletteIndex].transform.rotation;

    previewObject.transform.position = placePos;
    previewObject.transform.rotation = placeRot;
    previewObject.transform.parent = null;
    return true;
    }
    void HidePreviewObject()
    {
    if (previewObject != null)
    {
    DestroyImmediate(previewObject);
    previewObject = null;
    }
    }
    bool rightMouseDown = false;
    private void OnSceneGUI(SceneView sceneView)
    {
    Event ev = Event.current;
    int sca = (ev.shift ? 4 : 0) + (ev.control ? 2 : 0) + (ev.alt ? 1 : 0);
    if (ev.type == EventType.MouseMove)
    mouseMoved = true;
    if (ev.type == EventType.MouseDown && ev.button == 1) rightMouseDown = true;
    if (ev.type == EventType.MouseUp && ev.button == 1) rightMouseDown = false;
    if (paintMode)
    {
    if (ev.type == EventType.Layout)
    HandleUtility.AddDefaultControl(0); // Consume the event
    if (true || sca == 0)
    {
    if (ShowPreviewObject(out Vector3 placePos,out Quaternion placeRot, out Bounds bounds))
    {
    if ((ev.type == EventType.MouseDown && ev.button == 0 && sca == 0)
    ||(ev.type == EventType.MouseDrag && ev.button == 0 && sca == 0 && !bounds.Intersects(lastPlace)))
    {
    GameObject gameObject = PrefabUtility.InstantiatePrefab(palette[paletteIndex]) as GameObject;
    gameObject.transform.position = placePos;
    gameObject.transform.rotation = placeRot;
    gameObject.transform.parent = container;
    lastPlace = bounds;
    Undo.RegisterCreatedObjectUndo(gameObject, "");
    ev.Use();
    }
    }
    else
    HidePreviewObject();
    }
    else
    HidePreviewObject();
    if (ev.type == EventType.KeyDown && ev.keyCode == KeyCode.R && sca == 0)
    {
    rotate = (rotate + 1) % 4;
    ev.Use();
    Repaint();
    }
    if (ev.type == EventType.KeyDown && ev.keyCode == KeyCode.C && sca == 0)
    {
    halfIntegerX = !halfIntegerX;
    if (halfIntegerX) halfIntegerZ = !halfIntegerZ;
    ev.Use();
    Repaint();
    }
    if (ev.type == EventType.KeyDown && (ev.keyCode == KeyCode.Alpha1 || Tools.current!=Tool.None) && sca == 0)
    {
    paintMode = false;
    HidePreviewObject();
    ev.Use();
    Repaint();
    }
    if (ev.type == EventType.ScrollWheel && sca == 1)
    {
    if (ev.delta.y < 0)
    selectedY += 1;
    if (ev.delta.y > 0)
    selectedY -= 1;
    mouseMoved = false;
    ev.Use();
    Repaint();
    }
    }
    else
    {
    HidePreviewObject();
    if (ev.type == EventType.KeyDown && ev.keyCode == KeyCode.Alpha1)
    {
    EnterPaintModeAndFocus();
    ev.Use();
    Repaint();
    }
    }

    string path1 = AssetDatabase.GetAssetPath(Selection.activeObject);
    if (path1 != "")
    {
    if (Path.GetExtension(path1) != "")
    path1 = path1.Replace(Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
    if (path != path1)
    {

    path = path1;
    Repaint();
    }
    }
    }
    void EnterPaintModeAndFocus()
    {

    (SceneView.sceneViews[0] as SceneView).Focus();
    Selection.SetActiveObjectWithContext(null, null);
    Tools.current = Tool.None;
    paintMode = true;
    }
    }
    #endif