Singularity/Library/PackageCache/com.unity.2d.pixel-perfect@.../Samples~/Scenes and Extras/Scenes/Rule Tiles/Scripts/RuleTile.cs
2024-05-06 11:45:45 -07:00

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);
}
}
}