Skip to content

Instantly share code, notes, and snippets.

@rakkarage
Created May 24, 2015 19:15
Show Gist options
  • Save rakkarage/27da11ac8de7505f8e75 to your computer and use it in GitHub Desktop.
Save rakkarage/27da11ac8de7505f8e75 to your computer and use it in GitHub Desktop.
TileMap
using UnityEngine;
public class GridTexture
{
public enum Type
{
LightChecked,
MediumChecked,
DarkChecked,
BlackChecked,
LightSolid,
MediumSolid,
DarkSolid,
BlackSolid,
}
static GridTexture Instance = null;
private const int _textureSize = 16;
private Texture2D _texture = null;
public static void Done()
{
if (Instance != null)
{
Instance.DestroyTexture();
Instance = null;
}
}
public static void Draw(Rect rect, Type type)
{
Draw(rect, Vector2.zero, type);
}
public static void Draw(Rect rect, Vector2 offset, Type type)
{
if (Instance == null)
{
Instance = new GridTexture();
Instance.InitTexture(type);
}
GUI.DrawTextureWithTexCoords(rect, Instance._texture,
new Rect(-offset.x / _textureSize, (offset.y - rect.height) / _textureSize,
rect.width / _textureSize, rect.height / _textureSize), false);
}
void InitTexture(Type type)
{
if (_texture == null)
{
_texture = new Texture2D(_textureSize, _textureSize);
Color c0 = Color.white;
Color c1 = new Color(0.8f, 0.8f, 0.8f, 1.0f);
switch (type)
{
case Type.LightChecked: c0 = new Color32(255, 255, 255, 255); c1 = new Color32(217, 217, 217, 255); break;
case Type.MediumChecked: c0 = new Color32(178, 178, 178, 255); c1 = new Color32(151, 151, 151, 255); break;
case Type.DarkChecked: c0 = new Color32(37, 37, 37, 255); c1 = new Color32(31, 31, 31, 255); break;
case Type.BlackChecked: c0 = new Color32(14, 14, 14, 255); c1 = new Color32(0, 0, 0, 255); break;
case Type.LightSolid: c0 = new Color32(255, 255, 255, 255); c1 = c0; break;
case Type.MediumSolid: c0 = new Color32(178, 178, 178, 255); c1 = c0; break;
case Type.DarkSolid: c0 = new Color32(37, 37, 37, 255); c1 = c0; break;
case Type.BlackSolid: c0 = new Color32(0, 0, 0, 255); c1 = c0; break;
}
for (int y = 0; y < _texture.height; ++y)
{
for (int x = 0; x < _texture.width; ++x)
{
bool xx = (x < _texture.width / 2);
bool yy = (y < _texture.height / 2);
_texture.SetPixel(x, y, (xx == yy) ? c0 : c1);
}
}
_texture.Apply();
_texture.filterMode = FilterMode.Point;
_texture.hideFlags = HideFlags.HideAndDontSave;
}
}
void DestroyTexture()
{
if (_texture != null)
{
Object.DestroyImmediate(_texture);
_texture = null;
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Rendering;
namespace ca.HenrySoftware.Deko
{
[Flags]
public enum TileFlags
{
None = (1 << 0),
FlipX = (1 << 1),
FlipY = (1 << 2),
Rot90 = (1 << 3),
}
[ExecuteInEditMode]
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class TileMapLayer : MonoBehaviour
{
public int Width = 10;
public int Height = 10;
public bool Additive = false;
public List<int> _data;
public int TileSize = 16;
public Texture2D Texture;
private Mesh _mesh;
private MeshFilter _meshFilter;
private MeshRenderer _meshRenderer;
private static Material _material;
private Vector2[] _uv;
[ContextMenu("Build")]
public void Build()
{
_material = new Material(Shader.Find(Additive ? "Custom/Additive" : "Sprites/Default")) { color = Color.white };
_material.SetTexture("_MainTex", Texture);
_material.SetFloat("PixelSnap", 0f);
CreateMesh();
for (var x = 0; x < Width; x++)
{
for (var y = 0; y < Height; y++)
{
SetTile(TileIndex(x, y), -1, TileFlags.None, false);
}
}
Commit();
}
public int TileAt(int index)
{
return _data[index];
}
public int TileIndex(Vector2 p)
{
return TileIndex((int)p.x, (int)p.y);
}
public int TileIndex(int x, int y)
{
return y * Width + x;
}
public Vector2 TilePosition(int index)
{
float y = index / Width;
float x = index - Width * y;
return new Vector2(x, y);
}
public bool InsideMap(Vector2 p)
{
return InsideMap((int)p.x, (int)p.y);
}
public bool InsideMap(int x, int y)
{
return (x >= 0) && (y >= 0) && (x < Width) && (y < Height);
}
public bool InsideEdge(Vector2 p)
{
return InsideEdge((int)p.x, (int)p.y);
}
public bool InsideEdge(int x, int y)
{
return (x >= 1) && (y >= 1) && (x < Width - 1) && (y < Height - 1);
}
public void CreateMesh()
{
var quads = Width * Height;
_data = Enumerable.Repeat(-1, quads).ToList();
var vertices = new Vector3[quads * 4];
var triangles = new int[quads * 6];
var normals = new Vector3[vertices.Length];
_uv = new Vector2[vertices.Length];
for (var y = 0; y < Height; y++)
{
for (var x = 0; x < Width; x++)
{
var i = TileIndex(x, y);
var vi = i * 4;
var ti = i * 6;
vertices[vi] = new Vector2(x, y);
vertices[vi + 1] = new Vector2(x + 1, y);
vertices[vi + 2] = new Vector2(x, y + 1);
vertices[vi + 3] = new Vector2(x + 1, y + 1);
triangles[ti] = vi;
triangles[ti + 1] = vi + 2;
triangles[ti + 2] = vi + 3;
triangles[ti + 3] = vi;
triangles[ti + 4] = vi + 3;
triangles[ti + 5] = vi + 1;
}
}
for (var i = 0; i < vertices.Length; i++)
{
normals[i] = Vector3.forward;
_uv[i] = Vector2.zero;
}
_mesh = new Mesh
{
vertices = vertices,
triangles = triangles,
normals = normals,
uv = _uv,
name = "TileMapMesh"
};
_meshFilter = GetComponent<MeshFilter>();
_meshFilter.mesh = _mesh;
_meshRenderer = GetComponent<MeshRenderer>();
_meshRenderer.material = _material;
_meshRenderer.shadowCastingMode = ShadowCastingMode.Off;
_meshRenderer.receiveShadows = false;
_meshRenderer.useLightProbes = false;
_meshRenderer.reflectionProbeUsage = ReflectionProbeUsage.Off;
}
public int TextureIndex(Vector2 p)
{
return TextureIndex((int)p.x, (int)p.y);
}
public int TextureIndex(int x, int y)
{
float s = Texture.width / TileSize;
return (y * (int)s) + x;
}
public Vector2 TexturePosition(int index)
{
int s = Texture.width / TileSize;
int y = index / s;
int x = index - (s * y);
return new Vector2(x, y);
}
public int GetTile(int x, int y)
{
return _data[TileIndex(x, y)];
}
public void SetTileFlags(int x, int y, TileFlags flags)
{
SetTile(TileIndex(x, y), GetTile(x, y), flags);
}
public void SetTile(int index, int id, TileFlags flags = TileFlags.None, bool commit = true)
{
if (id == -1) id = (int)Tile.Level.Empty;
_data[index] = id;
var tp = TexturePosition(id);
tp = new Vector2(tp.x * TileSize, tp.y * TileSize);
tp.y = Texture.height - tp.y - TileSize;
var rect = new Rect(tp.x, tp.y, TileSize, TileSize);
index *= 4;
_uv[index + 0] = ToUv(new Vector2(rect.xMin, rect.yMin), Texture);
_uv[index + 1] = ToUv(new Vector2(rect.xMax, rect.yMin), Texture);
_uv[index + 2] = ToUv(new Vector2(rect.xMin, rect.yMax), Texture);
_uv[index + 3] = ToUv(new Vector2(rect.xMax, rect.yMax), Texture);
if ((flags & TileFlags.Rot90) == TileFlags.Rot90)
{
var temp0 = _uv[index + 0];
var temp1 = _uv[index + 1];
var temp2 = _uv[index + 2];
var temp3 = _uv[index + 3];
_uv[index + 0] = temp2;
_uv[index + 1] = temp0;
_uv[index + 2] = temp3;
_uv[index + 3] = temp1;
}
if ((flags & TileFlags.FlipX) == TileFlags.FlipX)
{
var temp0 = _uv[index + 0];
var temp1 = _uv[index + 1];
var temp2 = _uv[index + 2];
var temp3 = _uv[index + 3];
_uv[index + 0] = temp1;
_uv[index + 1] = temp0;
_uv[index + 2] = temp3;
_uv[index + 3] = temp2;
}
if ((flags & TileFlags.FlipY) == TileFlags.FlipY)
{
var temp0 = _uv[index + 0];
var temp1 = _uv[index + 1];
var temp2 = _uv[index + 2];
var temp3 = _uv[index + 3];
_uv[index + 0] = temp2;
_uv[index + 1] = temp3;
_uv[index + 2] = temp0;
_uv[index + 3] = temp1;
}
if (commit)
_dirty = true;
}
public void Commit()
{
_meshFilter.sharedMesh.uv = _uv;
}
private bool _dirty = false;
private void LateUpdate()
{
if (_dirty)
{
_dirty = false;
Commit();
}
}
public void SetAnimation(int index, Tile.Level[] frames, float fps)
{
StartCoroutine(Animation(index, new Tile.Level[][] { frames }, fps));
}
public void SetAnimation(int index, Tile.Level[][] frames, float fps)
{
StartCoroutine(Animation(index, frames, fps));
}
private IEnumerator Animation(int index, Tile.Level[][] frames, float fps)
{
var time = 1f / fps;
var animationIndex = 0;
var frameIndex = 0;
while (true)
{
if (animationIndex >= frames.Length) animationIndex = 0;
if (frameIndex >= frames[animationIndex].Length)
{
animationIndex = Utility.Random(frames.Length);
frameIndex = 0;
}
SetTile(index, (int)frames[animationIndex][frameIndex]);
frameIndex++;
yield return new WaitForSeconds(time);
}
}
public static float ToUv(float f, Texture2D t)
{
return f / t.width;
}
public static Vector2 ToUv(int x, int y, Texture2D t)
{
return ToUv(new Vector2(x, y), t);
}
public static Vector2 ToUv(Vector2 p, Texture2D t)
{
return new Vector2(p.x / t.width, p.y / t.height);
}
public Bounds Bounds
{
get { return _mesh.bounds; }
}
}
}
using System;
using UnityEditor;
using UnityEngine;
namespace ca.HenrySoftware.Deko
{
[CustomEditor(typeof(TileMapLayer))]
public class TileMapLayerEditor : Editor
{
private static Vector3 _mouse;
private static Vector3 _selection;
private int _tileX;
private int _tileY;
private static bool _tiles = false;
private static bool _edit = false;
public override void OnInspectorGUI()
{
var element = (TileMapLayer)target;
DrawDefaultInspector();
_edit = EditorGUILayout.Toggle(new GUIContent("Edit"), _edit);
_tileX = EditorGUILayout.IntField(new GUIContent("TileX"), _tileX);
_tileY = EditorGUILayout.IntField(new GUIContent("TileY"), _tileY);
_tiles = EditorGUILayout.Foldout(_tiles, new GUIContent("Tiles"));
if (_tiles)
{
var width = Mathf.Min(Screen.width - EditorGUIUtility.singleLineHeight * 2, element.Texture.width);
var scale = width / element.Texture.width;
var height = element.Texture.height * scale;
var columns = element.Texture.width / element.TileSize;
var rows = element.Texture.height / element.TileSize;
var sizex = width / columns;
var sizey = height / rows;
var r = EditorGUILayout.GetControlRect(false, height);
r.width = width;
GridTexture.Draw(r, GridTexture.Type.MediumChecked);
EditorGUI.DrawTextureTransparent(r, element.Texture);
if (Event.current.type == EventType.MouseUp && r.Contains(Event.current.mousePosition))
{
var p = Event.current.mousePosition - new Vector2(r.x, r.y);
_tileX = (int)(p.x / sizex);
_tileY = (int)(p.y / sizey);
}
var size = element.TileSize * scale;
var rect = new Rect(r.x + (_tileX * size), r.y + (_tileY * size), size, size);
EditorGUI.DrawRect(rect, Colors.YellowLight);
}
if (GUILayout.Button("Build"))
{
element.Build();
}
//if (GUILayout.Button("Load"))
//{
// element.Load();
//}
//if (GUILayout.Button("Edge"))
//{
// element.Edge();
//}
}
private void OnSceneGUI()
{
HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
if (UpdateMouse())
{
SceneView.RepaintAll();
}
_selection = GetTile();
if (MouseOver() && _edit)
{
var current = Event.current;
if (current.type == EventType.MouseDown || current.type == EventType.MouseDrag)
{
current.Use();
if (current.button == 1)
{
Erase();
}
else if (current.button == 0)
{
Draw();
}
}
}
}
private void Draw()
{
var element = (TileMapLayer)target;
element.SetTile(element.TileIndex((int)_selection.x, (int)_selection.y), element.TileIndex(new Vector2(_tileX, _tileY)));
}
private void Erase()
{
var element = (TileMapLayer)target;
element.SetTile(element.TileIndex((int)_selection.x, (int)_selection.y), -1);
}
private bool UpdateMouse()
{
var element = (TileMapLayer)target;
var p = new Plane(element.transform.TransformDirection(Vector3.forward), element.transform.position);
var ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
var hit = Vector3.zero;
float dist;
if (p.Raycast(ray, out dist))
{
hit = ray.origin + (ray.direction.normalized * dist);
}
var changed = false;
var value = element.transform.InverseTransformPoint(hit);
if (value != _mouse)
{
_mouse = value;
changed = true;
}
return changed;
}
private Vector3 GetTile()
{
var element = (TileMapLayer)target;
var p = new Vector3(
(int)Math.Round(_mouse.x, 5, MidpointRounding.ToEven),
(int)Math.Round(_mouse.y, 5, MidpointRounding.ToEven),
element.transform.position.z);
if (p.x < 0f) p.x = 0f;
if (p.x > element.Width - 1) p.x = element.Width - 1;
if (p.y < 0f) p.y = 0f;
if (p.y > element.Height - 1) p.y = element.Height - 1;
return p;
}
private bool MouseOver()
{
var element = (TileMapLayer)target;
return (_mouse.x > 0) && (_mouse.x < element.Width) && (_mouse.y > 0) && (_mouse.y < element.Height);
}
[DrawGizmo(GizmoType.Selected | GizmoType.Active)]
static void RenderMapGizmo(TileMapLayer layer, GizmoType gizmoType)
{
var p = layer.transform.position;
var width = layer.Width;
var height = layer.Height;
Gizmos.color = Color.grey;
for (float i = 1; i < width; i++)
{
Gizmos.DrawLine(p + new Vector3(i, 0), p + new Vector3(i, height));
}
for (float i = 1; i < height; i++)
{
Gizmos.DrawLine(p + new Vector3(0, i), p + new Vector3(width, i));
}
Gizmos.color = Color.white;
Gizmos.DrawLine(p, p + new Vector3(width, 0));
Gizmos.DrawLine(p, p + new Vector3(0, height));
Gizmos.DrawLine(p + new Vector3(width, 0), p + new Vector3(width, height));
Gizmos.DrawLine(p + new Vector3(0, height), p + new Vector3(width, height));
Gizmos.color = Color.red;
Gizmos.DrawWireCube(_selection + layer.transform.position + new Vector3(.5f, .5f), new Vector3(1.1f, 1.1f, .2f));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment