Skip to content

Commit

Permalink
Introduced Dual Grid Tilemap Preview
Browse files Browse the repository at this point in the history
  • Loading branch information
skner-dev committed Jan 6, 2025
1 parent 9c50410 commit 72975a2
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 49 deletions.
210 changes: 210 additions & 0 deletions Editor/DualGridRuleTilePreviewer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
using System.Linq;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Tilemaps;

namespace skner.DualGrid.Editor
{
public static class DualGridRuleTilePreviewer
{

private static Scene _previewScene;
private static Camera _previewCamera;
private static DualGridTilemapModule _previewDualGridTilemapModule;

private static RenderTexture _renderTexture;

/// <summary>
/// Offset used to spawn preview objects, so they are outside of the active scene view when rendered
/// <para></para>
/// This is used because cameras don't work properly in preview scenes.
/// The best workaround found was to move the preview objects into the active scene, render the camera and move them back into the preview scene.
/// <para></para>
/// Thanks Unity!
/// </summary>
private static Vector3 _previewObjectsPositionOffset = new(100000, 100000, 0);

/// <summary>
/// Loads the preview scene with a specific tile.
/// <para></para>
/// The preview scene and objects will be initialized if not already.
/// </summary>
/// <param name="tile"></param>
public static void LoadPreviewScene(DualGridRuleTile tile)
{
if (_previewScene == default)
{
_previewScene = EditorSceneManager.NewPreviewScene();
}

if (_previewDualGridTilemapModule == null)
{
_previewDualGridTilemapModule = CreateDualGridTilemapModule(tile);
EditorSceneManager.MoveGameObjectToScene(_previewDualGridTilemapModule.transform.parent.gameObject, _previewScene);
}

if (_previewDualGridTilemapModule.Tile != tile)
{
UpdateDualGridTile(_previewDualGridTilemapModule, tile);
}

if (_previewCamera == null)
{
_previewCamera = CreateCamera();
EditorSceneManager.MoveGameObjectToScene(_previewCamera.gameObject, _previewScene);
}

if (_renderTexture == null)
{
_renderTexture = new RenderTexture(1350, 420, 16, RenderTextureFormat.Default);
}
}

/// <summary>
/// Forcefully renders the preview dual grid tilemap, by temporarily moving the preview objects (camera and tilemap) into the active scene,
/// so that they are rendered, and then back into the preview scene, so that they are hidden.
/// <para></para>
/// This is only done because temporary preview scenes don't allow cameras to work properly.
/// </summary>
public static void UpdateRenderTexture()
{
MovePreviewObjectsToScene(EditorSceneManager.GetActiveScene());

_previewCamera.targetTexture = _renderTexture;
_previewCamera.Render();
_previewCamera.targetTexture = null;

MovePreviewObjectsToScene(_previewScene);

static void MovePreviewObjectsToScene(Scene scene)
{
EditorSceneManager.MoveGameObjectToScene(_previewDualGridTilemapModule.transform.parent.gameObject, scene);
EditorSceneManager.MoveGameObjectToScene(_previewCamera.gameObject, scene);
}
}

/// <summary>
/// Returns the current tilemap preview render texture.
/// </summary>
/// <returns></returns>
public static RenderTexture GetRenderTexture()
{
if (_renderTexture == null)
Debug.LogError("RenderTexture not initialized. Make sure the preview scene is loaded.");

return _renderTexture;
}

private static DualGridTilemapModule CreateDualGridTilemapModule(DualGridRuleTile dualGridRuleTile)
{
var dualGridTilemapModule = DualGridTilemapModuleEditor.CreateNewDualGridTilemap();

dualGridTilemapModule.transform.parent.position += _previewObjectsPositionOffset;
UpdateDualGridTile(dualGridTilemapModule, dualGridRuleTile);
PaintSampleTiles(dualGridTilemapModule);

return dualGridTilemapModule;
}

private static Camera CreateCamera()
{
var cameraObject = new GameObject("PreviewCamera");

Camera camera = cameraObject.AddComponent<Camera>();

camera.orthographic = true;
camera.transform.position = new Vector3(0, -5.5f, -10) + _previewObjectsPositionOffset;
camera.orthographicSize = 3;
camera.nearClipPlane = 0.3f;
camera.farClipPlane = 15f;
camera.backgroundColor = Color.gray;
camera.cullingMask = -1;

return camera;
}

private static void PaintSampleTiles(DualGridTilemapModule previewDualGridTilemapModule)
{
var tile = ScriptableObject.CreateInstance<Tile>();

previewDualGridTilemapModule.DataTilemap.ClearAllTiles();

// Two dots
SetTile(-9, -4);
SetTile(-7, -4);

// O shape
SetTile(-9, -6);
SetTile(-9, -7);
SetTile(-9, -8);
SetTile(-8, -6);
SetTile(-8, -8);
SetTile(-7, -6);
SetTile(-7, -7);
SetTile(-7, -8);

// Horizontal line
SetTile(-5, -4);
SetTile(-4, -4);
SetTile(-3, -4);

// 3x3 square
SetTile(-5, -6);
SetTile(-4, -6);
SetTile(-3, -6);
SetTile(-5, -7);
SetTile(-4, -7);
SetTile(-3, -7);
SetTile(-5, -8);
SetTile(-4, -8);
SetTile(-3, -8);

// Exclamation Point
SetTile(-1, -4);
SetTile(-1, -5);
SetTile(-1, -6);
SetTile(-1, -8);

// Plus Symbol
SetTile(2, -4);
SetTile(1, -5);
SetTile(2, -5);
SetTile(3, -5);
SetTile(2, -6);

// Another horizontal line
SetTile(1, -8);
SetTile(2, -8);
SetTile(3, -8);

// Top Shuriken thing
SetTile(5, -4);
SetTile(5, -5);
SetTile(6, -5);
SetTile(7, -4);
SetTile(8, -4);
SetTile(8, -5);

// Bottom Shuriken thing
SetTile(5, -7);
SetTile(5, -8);
SetTile(6, -7);
SetTile(8, -7);
SetTile(7, -8);
SetTile(8, -8);

void SetTile(int x, int y)
{
previewDualGridTilemapModule.DataTilemap.SetTile(new Vector3Int(x, y, 0), tile);
}
}

private static void UpdateDualGridTile(DualGridTilemapModule dualGridTilemapModule, DualGridRuleTile dualGridRuleTile)
{
dualGridTilemapModule.Tile = dualGridRuleTile;
dualGridTilemapModule.RefreshRenderTiles();
}

}
}
11 changes: 11 additions & 0 deletions Editor/DualGridRuleTilePreviewer.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 55 additions & 12 deletions Editor/Editors/DualGridRuleTileEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ public class DualGridRuleTileEditor : RuleTileEditor

private DualGridRuleTile _targetDualGridRuleTile;

private const string PreviewActiveStatusKey = "PreviewActiveStatusKey";
private bool _isPreviewActive;

public override void OnEnable()
{
_targetDualGridRuleTile = (DualGridRuleTile)target;

_isPreviewActive = EditorPrefs.GetBool(PreviewActiveStatusKey);

base.OnEnable();
}

Expand Down Expand Up @@ -55,16 +60,7 @@ public override void OnInspectorGUI()
_targetDualGridRuleTile.m_TilingRules.ForEach(tilingRule => tilingRule.m_ColliderType = _targetDualGridRuleTile.m_DefaultColliderType);
}

if (GUILayout.Button("Refresh Rule Tiles in use"))
{
var dualGridModules = Object.FindObjectsByType<DualGridTilemapModule>(FindObjectsSortMode.None);
foreach (var dualGridModule in dualGridModules)
{
if (dualGridModule.Tile == _targetDualGridRuleTile) dualGridModule.RefreshRenderTiles();
}
}

GUILayout.Space(10);
GUILayout.Space(5);

if (GUILayout.Button("Apply Automatic Rule Tiling"))
{
Expand All @@ -74,14 +70,16 @@ public override void OnInspectorGUI()

GUILayout.Space(5);

DrawRuleTilePreview();

EditorGUILayout.LabelField("Rule Tile Settings", EditorStyles.boldLabel);

base.OnInspectorGUI();

if (EditorGUI.EndChangeCheck())
{
EditorUtility.SetDirty(_targetDualGridRuleTile);
}

base.OnInspectorGUI();
}

private void DrawDragAndDropArea()
Expand Down Expand Up @@ -115,6 +113,51 @@ private void DrawDragAndDropArea()
}
}

private void DrawRuleTilePreview()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Tilemap Preview", EditorStyles.boldLabel);

if (!_isPreviewActive)
{
if (GUILayout.Button("Show Preview"))
{
_isPreviewActive = true;
EditorPrefs.SetBool(PreviewActiveStatusKey, _isPreviewActive);
}
}
else
{
if (GUILayout.Button("Hide Preview"))
{
_isPreviewActive = false;
EditorPrefs.SetBool(PreviewActiveStatusKey, _isPreviewActive);
}
}

if (_isPreviewActive)
{
DualGridRuleTilePreviewer.LoadPreviewScene(_targetDualGridRuleTile);

DualGridRuleTilePreviewer.UpdateRenderTexture(); // TODO: Slower than ideal, but can't find any better option to check for changes...
RenderTexture previewTexture = DualGridRuleTilePreviewer.GetRenderTexture();

if (previewTexture != null)
{
float aspectRatio = (float)previewTexture.width / previewTexture.height;

float desiredWidth = EditorGUIUtility.currentViewWidth;
float desiredHeight = desiredWidth / aspectRatio;

GUILayout.Box(new GUIContent(previewTexture), GUILayout.Width(desiredWidth - 22), GUILayout.Height(desiredHeight - 3));
}
else
{
EditorGUILayout.LabelField("Preview not available.", EditorStyles.centeredGreyMiniLabel);
}
}
}

public override void RuleMatrixOnGUI(RuleTile tile, Rect rect, BoundsInt bounds, RuleTile.TilingRule tilingRule)
{
// This code was copied from the base RuleTileEditor.RuleMatrixOnGUI, because there are no good ways to extend it.
Expand Down
Loading

0 comments on commit 72975a2

Please sign in to comment.