237 lines
7.2 KiB
C#
237 lines
7.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine.Tilemaps;
|
|
|
|
namespace UnityEngine
|
|
{
|
|
[Serializable]
|
|
[CreateAssetMenu]
|
|
public class RuleTile : TileBase
|
|
{
|
|
public Sprite m_DefaultSprite;
|
|
public Tile.ColliderType m_DefaultColliderType = Tile.ColliderType.Sprite;
|
|
|
|
[Serializable]
|
|
public class TilingRule
|
|
{
|
|
public Neighbor[] m_Neighbors;
|
|
public Sprite[] m_Sprites;
|
|
public float m_AnimationSpeed;
|
|
public float m_PerlinScale;
|
|
public Transform m_RuleTransform;
|
|
public OutputSprite m_Output;
|
|
public Tile.ColliderType m_ColliderType;
|
|
public Transform m_RandomTransform;
|
|
|
|
public TilingRule()
|
|
{
|
|
m_Output = OutputSprite.Single;
|
|
m_Neighbors = new Neighbor[8];
|
|
m_Sprites = new Sprite[1];
|
|
m_AnimationSpeed = 1f;
|
|
m_PerlinScale = 0.5f;
|
|
m_ColliderType = Tile.ColliderType.Sprite;
|
|
|
|
for(int i=0; i<m_Neighbors.Length; i++)
|
|
m_Neighbors[i] = Neighbor.DontCare;
|
|
}
|
|
|
|
public enum Transform { Fixed, Rotated, MirrorX, MirrorY }
|
|
public enum Neighbor { DontCare, This, NotThis }
|
|
public enum OutputSprite { Single, Random, Animation }
|
|
}
|
|
|
|
[HideInInspector] public List<TilingRule> m_TilingRules;
|
|
|
|
public override void GetTileData(Vector3Int position, ITilemap tileMap, ref TileData tileData)
|
|
{
|
|
tileData.sprite = m_DefaultSprite;
|
|
tileData.colliderType = m_DefaultColliderType;
|
|
tileData.flags = TileFlags.LockTransform;
|
|
tileData.transform = Matrix4x4.identity;
|
|
|
|
foreach (TilingRule rule in m_TilingRules)
|
|
{
|
|
Matrix4x4 transform = Matrix4x4.identity;
|
|
if (RuleMatches(rule, position, tileMap, ref transform))
|
|
{
|
|
switch (rule.m_Output)
|
|
{
|
|
case TilingRule.OutputSprite.Single:
|
|
case TilingRule.OutputSprite.Animation:
|
|
tileData.sprite = rule.m_Sprites[0];
|
|
break;
|
|
case TilingRule.OutputSprite.Random:
|
|
int index = Mathf.Clamp(Mathf.FloorToInt(GetPerlinValue(position, rule.m_PerlinScale, 100000f) * rule.m_Sprites.Length), 0, rule.m_Sprites.Length - 1);
|
|
tileData.sprite = rule.m_Sprites[index];
|
|
if (rule.m_RandomTransform != TilingRule.Transform.Fixed)
|
|
transform = ApplyRandomTransform(rule.m_RandomTransform, transform, rule.m_PerlinScale, position);
|
|
break;
|
|
}
|
|
tileData.transform = transform;
|
|
tileData.colliderType = rule.m_ColliderType;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static float GetPerlinValue(Vector3Int position, float scale, float offset)
|
|
{
|
|
return Mathf.PerlinNoise((position.x + offset) * scale, (position.y + offset) * scale);
|
|
}
|
|
|
|
public override bool GetTileAnimationData(Vector3Int position, ITilemap tilemap, ref TileAnimationData tileAnimationData)
|
|
{
|
|
foreach (TilingRule rule in m_TilingRules)
|
|
{
|
|
Matrix4x4 transform = Matrix4x4.identity;
|
|
if (RuleMatches(rule, position, tilemap, ref transform) && rule.m_Output == TilingRule.OutputSprite.Animation)
|
|
{
|
|
tileAnimationData.animatedSprites = rule.m_Sprites;
|
|
tileAnimationData.animationSpeed = rule.m_AnimationSpeed;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public override void RefreshTile(Vector3Int location, ITilemap tileMap)
|
|
{
|
|
if (m_TilingRules != null && m_TilingRules.Count > 0)
|
|
{
|
|
for (int y = -1; y <= 1; y++)
|
|
{
|
|
for (int x = -1; x <= 1; x++)
|
|
{
|
|
base.RefreshTile(location + new Vector3Int(x, y, 0), tileMap);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
base.RefreshTile(location, tileMap);
|
|
}
|
|
}
|
|
|
|
public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, ref Matrix4x4 transform)
|
|
{
|
|
// Check rule against rotations of 0, 90, 180, 270
|
|
for (int angle = 0; angle <= (rule.m_RuleTransform == TilingRule.Transform.Rotated ? 270 : 0); angle += 90)
|
|
{
|
|
if (RuleMatches(rule, position, tilemap, angle))
|
|
{
|
|
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), Vector3.one);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Check rule against x-axis mirror
|
|
if ((rule.m_RuleTransform == TilingRule.Transform.MirrorX) && RuleMatches(rule, position, tilemap, true, false))
|
|
{
|
|
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, 1f, 1f));
|
|
return true;
|
|
}
|
|
|
|
// Check rule against y-axis mirror
|
|
if ((rule.m_RuleTransform == TilingRule.Transform.MirrorY) && RuleMatches(rule, position, tilemap, false, true))
|
|
{
|
|
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, -1f, 1f));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static Matrix4x4 ApplyRandomTransform(TilingRule.Transform type, Matrix4x4 original, float perlinScale, Vector3Int position)
|
|
{
|
|
float perlin = GetPerlinValue(position, perlinScale, 200000f);
|
|
switch (type)
|
|
{
|
|
case TilingRule.Transform.MirrorX:
|
|
return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(perlin < 0.5 ? 1f : -1f, 1f, 1f));
|
|
case TilingRule.Transform.MirrorY:
|
|
return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, perlin < 0.5 ? 1f : -1f, 1f));
|
|
case TilingRule.Transform.Rotated:
|
|
int angle = Mathf.Clamp(Mathf.FloorToInt(perlin * 4), 0, 3) * 90;
|
|
return Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), Vector3.one);
|
|
}
|
|
return original;
|
|
}
|
|
|
|
public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, int angle)
|
|
{
|
|
for (int y = -1; y <= 1; y++)
|
|
{
|
|
for (int x = -1; x <= 1; x++)
|
|
{
|
|
if (x != 0 || y != 0)
|
|
{
|
|
Vector3Int offset = new Vector3Int(x, y, 0);
|
|
Vector3Int rotated = GetRotatedPos(offset, angle);
|
|
int index = GetIndexOfOffset(rotated);
|
|
TileBase tile = tilemap.GetTile(position + offset);
|
|
if (rule.m_Neighbors[index] == TilingRule.Neighbor.This && tile != this || rule.m_Neighbors[index] == TilingRule.Neighbor.NotThis && tile == this)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, bool mirrorX, bool mirrorY)
|
|
{
|
|
for (int y = -1; y <= 1; y++)
|
|
{
|
|
for (int x = -1; x <= 1; x++)
|
|
{
|
|
if (x != 0 || y != 0)
|
|
{
|
|
Vector3Int offset = new Vector3Int(x, y, 0);
|
|
Vector3Int mirrored = GetMirroredPos(offset, mirrorX, mirrorY);
|
|
int index = GetIndexOfOffset(mirrored);
|
|
TileBase tile = tilemap.GetTile(position + offset);
|
|
if (rule.m_Neighbors[index] == TilingRule.Neighbor.This && tile != this || rule.m_Neighbors[index] == TilingRule.Neighbor.NotThis && tile == this)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private int GetIndexOfOffset(Vector3Int offset)
|
|
{
|
|
int result = offset.x + 1 + (-offset.y + 1) * 3;
|
|
if (result >= 4)
|
|
result--;
|
|
return result;
|
|
}
|
|
|
|
public Vector3Int GetRotatedPos(Vector3Int original, int rotation)
|
|
{
|
|
switch (rotation)
|
|
{
|
|
case 0:
|
|
return original;
|
|
case 90:
|
|
return new Vector3Int(-original.y, original.x, original.z);
|
|
case 180:
|
|
return new Vector3Int(-original.x, -original.y, original.z);
|
|
case 270:
|
|
return new Vector3Int(original.y, -original.x, original.z);
|
|
}
|
|
return original;
|
|
}
|
|
|
|
public Vector3Int GetMirroredPos(Vector3Int original, bool mirrorX, bool mirrorY)
|
|
{
|
|
return new Vector3Int(original.x * (mirrorX ? -1 : 1), original.y * (mirrorY ? -1 : 1), original.z);
|
|
}
|
|
}
|
|
}
|