Created
May 24, 2015 19:15
-
-
Save rakkarage/27da11ac8de7505f8e75 to your computer and use it in GitHub Desktop.
TileMap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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