Created
December 29, 2025 03:36
-
-
Save baba-s/dc692e3f15462f9a823b60cf54321431 to your computer and use it in GitHub Desktop.
This file contains hidden or 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.Collections.Generic; | |
| using UnityEngine; | |
| using UnityEngine.Rendering; | |
| namespace Kogane | |
| { | |
| [RequireComponent( typeof( MeshFilter ), typeof( MeshRenderer ) )] | |
| public sealed class ColoredTilemap : MonoBehaviour | |
| { | |
| private const int VERTICES_PER_TILE = 4; | |
| private const int INDICES_PER_TILE = 6; | |
| [SerializeField] private MeshFilter m_meshFilter; | |
| [SerializeField] private MeshRenderer m_meshRenderer; | |
| [SerializeField] private int m_width = 64; | |
| [SerializeField] private int m_height = 64; | |
| [SerializeField] private float m_cellSize = 1; | |
| private bool m_isInitialized; | |
| private int m_tileCount; | |
| private int m_halfWidth; | |
| private int m_halfHeight; | |
| private Mesh m_mesh; | |
| private Color32[] m_tileColors; | |
| private Color32[] m_meshColors32; | |
| private bool m_isDirty; | |
| private void OnDestroy() | |
| { | |
| if ( m_mesh == null ) return; | |
| Destroy( m_mesh ); | |
| m_mesh = null; | |
| } | |
| private void Awake() | |
| { | |
| Initialize(); | |
| } | |
| private void Initialize() | |
| { | |
| if ( m_isInitialized ) return; | |
| m_isInitialized = true; | |
| m_tileCount = m_width * m_height; | |
| m_halfWidth = m_width / 2; | |
| m_halfHeight = m_height / 2; | |
| var vertexCount = m_tileCount * VERTICES_PER_TILE; | |
| var indexCount = m_tileCount * INDICES_PER_TILE; | |
| m_tileColors = new Color32[ m_tileCount ]; | |
| m_meshColors32 = new Color32[ vertexCount ]; | |
| var vertices = new Vector3[ vertexCount ]; | |
| var uvs = new Vector2[ vertexCount ]; | |
| var triangles = new int[ indexCount ]; | |
| var vi = 0; | |
| var ti = 0; | |
| for ( var y = 0; y < m_height; y++ ) | |
| { | |
| var gy = y - m_halfHeight; // グリッドy | |
| for ( var x = 0; x < m_width; x++ ) | |
| { | |
| var gx = x - m_halfWidth; // グリッドx | |
| // タイルの左下座標(ローカル) | |
| var lx = gx * m_cellSize; | |
| var ly = gy * m_cellSize; | |
| // 4頂点(左下, 右下, 右上, 左上) | |
| vertices[ vi + 0 ] = new( lx, ly ); | |
| vertices[ vi + 1 ] = new( lx + m_cellSize, ly ); | |
| vertices[ vi + 2 ] = new( lx + m_cellSize, ly + m_cellSize ); | |
| vertices[ vi + 3 ] = new( lx, ly + m_cellSize ); | |
| // UV(適当でOK) | |
| uvs[ vi + 0 ] = new( 0, 0 ); | |
| uvs[ vi + 1 ] = new( 1, 0 ); | |
| uvs[ vi + 2 ] = new( 1, 1 ); | |
| uvs[ vi + 3 ] = new( 0, 1 ); | |
| // 2三角形 | |
| triangles[ ti + 0 ] = vi + 0; | |
| triangles[ ti + 1 ] = vi + 2; | |
| triangles[ ti + 2 ] = vi + 1; | |
| triangles[ ti + 3 ] = vi + 0; | |
| triangles[ ti + 4 ] = vi + 3; | |
| triangles[ ti + 5 ] = vi + 2; | |
| vi += VERTICES_PER_TILE; | |
| ti += INDICES_PER_TILE; | |
| } | |
| } | |
| m_mesh = new(); | |
| m_mesh.MarkDynamic(); | |
| m_mesh.indexFormat = IndexFormat.UInt32; | |
| m_mesh.vertices = vertices; | |
| m_mesh.uv = uvs; | |
| m_mesh.triangles = triangles; | |
| m_mesh.RecalculateBounds(); | |
| m_mesh.RecalculateNormals(); | |
| m_meshFilter.sharedMesh = m_mesh; | |
| } | |
| public void Setup | |
| ( | |
| in Vector2Int position, | |
| in Color color | |
| ) | |
| { | |
| Initialize(); | |
| SetTileColor | |
| ( | |
| position: position, | |
| color: color | |
| ); | |
| ApplyColorToMesh( position ); | |
| } | |
| public void Setup | |
| ( | |
| IReadOnlyList<Vector2Int> positions, | |
| in Color color | |
| ) | |
| { | |
| Initialize(); | |
| for ( var i = 0; i < positions.Count; i++ ) | |
| { | |
| SetTileColor | |
| ( | |
| position: positions[ i ], | |
| color: color | |
| ); | |
| } | |
| ApplyAllColorsToMesh(); | |
| } | |
| private void SetTileColor | |
| ( | |
| in Vector2Int position, | |
| in Color color | |
| ) | |
| { | |
| if ( !TryToIndex( position, out var index ) ) return; | |
| m_tileColors[ index ] = color; | |
| } | |
| private void ApplyAllColorsToMesh() | |
| { | |
| for ( var i = 0; i < m_tileCount; i++ ) | |
| { | |
| var color = m_tileColors[ i ]; | |
| var startIndex = i * VERTICES_PER_TILE; | |
| m_meshColors32[ startIndex + 0 ] = color; | |
| m_meshColors32[ startIndex + 1 ] = color; | |
| m_meshColors32[ startIndex + 2 ] = color; | |
| m_meshColors32[ startIndex + 3 ] = color; | |
| } | |
| m_isDirty = true; | |
| } | |
| private void ApplyColorToMesh( in Vector2Int position ) | |
| { | |
| if ( !TryToIndex( position, out var index ) ) return; | |
| var color = m_tileColors[ index ]; | |
| var startIndex = index * VERTICES_PER_TILE; | |
| m_meshColors32[ startIndex + 0 ] = color; | |
| m_meshColors32[ startIndex + 1 ] = color; | |
| m_meshColors32[ startIndex + 2 ] = color; | |
| m_meshColors32[ startIndex + 3 ] = color; | |
| m_isDirty = true; | |
| } | |
| private bool TryToIndex | |
| ( | |
| in Vector2Int position, | |
| out int index | |
| ) | |
| { | |
| var x = position.x + m_halfWidth; | |
| var y = position.y + m_halfHeight; | |
| if ( x < 0 || m_width <= x || y < 0 || m_height <= y ) | |
| { | |
| index = -1; | |
| return false; | |
| } | |
| index = x + y * m_width; | |
| return true; | |
| } | |
| private void LateUpdate() | |
| { | |
| if ( !m_isDirty ) return; | |
| m_isDirty = false; | |
| m_mesh.colors32 = m_meshColors32; | |
| } | |
| public Vector3Int WorldToCell( in Vector3 worldPosition ) | |
| { | |
| Initialize(); | |
| var local = transform.InverseTransformPoint( worldPosition ); | |
| var gridX = Mathf.FloorToInt( local.x / m_cellSize + 0.5f ); | |
| var gridY = Mathf.FloorToInt( local.y / m_cellSize + 0.5f ); | |
| return new( gridX, gridY ); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment