暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

RuleTile.cs 38KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. using System;
  2. using System.Linq;
  3. using System.Reflection;
  4. using System.Collections.Generic;
  5. using UnityEngine.Tilemaps;
  6. using UnityEngine.Serialization;
  7. namespace UnityEngine
  8. {
  9. /// <summary>
  10. /// Generic visual tile for creating different tilesets like terrain, pipeline, random or animated tiles.
  11. /// This is templated to accept a Neighbor Rule Class for Custom Rules.
  12. /// </summary>
  13. /// <typeparam name="T">Neighbor Rule Class for Custom Rules</typeparam>
  14. public class RuleTile<T> : RuleTile
  15. {
  16. /// <summary>
  17. /// Returns the Neighbor Rule Class type for this Rule Tile.
  18. /// </summary>
  19. public sealed override Type m_NeighborType => typeof(T);
  20. }
  21. /// <summary>
  22. /// Generic visual tile for creating different tilesets like terrain, pipeline, random or animated tiles.
  23. /// </summary>
  24. [Serializable]
  25. [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/RuleTile.html")]
  26. public class RuleTile : TileBase
  27. {
  28. /// <summary>
  29. /// Returns the default Neighbor Rule Class type.
  30. /// </summary>
  31. public virtual Type m_NeighborType => typeof(TilingRuleOutput.Neighbor);
  32. /// <summary>
  33. /// The Default Sprite set when creating a new Rule.
  34. /// </summary>
  35. public Sprite m_DefaultSprite;
  36. /// <summary>
  37. /// The Default GameObject set when creating a new Rule.
  38. /// </summary>
  39. public GameObject m_DefaultGameObject;
  40. /// <summary>
  41. /// The Default Collider Type set when creating a new Rule.
  42. /// </summary>
  43. public Tile.ColliderType m_DefaultColliderType = Tile.ColliderType.Sprite;
  44. /// <summary>
  45. /// Angle in which the RuleTile is rotated by for matching in Degrees.
  46. /// </summary>
  47. public virtual int m_RotationAngle => 90;
  48. /// <summary>
  49. /// Number of rotations the RuleTile can be rotated by for matching.
  50. /// </summary>
  51. public int m_RotationCount => 360 / m_RotationAngle;
  52. /// <summary>
  53. /// The data structure holding the Rule information for matching Rule Tiles with
  54. /// its neighbors.
  55. /// </summary>
  56. [Serializable]
  57. public class TilingRuleOutput
  58. {
  59. /// <summary>
  60. /// Id for this Rule.
  61. /// </summary>
  62. public int m_Id;
  63. /// <summary>
  64. /// The output Sprites for this Rule.
  65. /// </summary>
  66. public Sprite[] m_Sprites = new Sprite[1];
  67. /// <summary>
  68. /// The output GameObject for this Rule.
  69. /// </summary>
  70. public GameObject m_GameObject;
  71. /// <summary>
  72. /// The output minimum Animation Speed for this Rule.
  73. /// </summary>
  74. [FormerlySerializedAs("m_AnimationSpeed")]
  75. public float m_MinAnimationSpeed = 1f;
  76. /// <summary>
  77. /// The output maximum Animation Speed for this Rule.
  78. /// </summary>
  79. [FormerlySerializedAs("m_AnimationSpeed")]
  80. public float m_MaxAnimationSpeed = 1f;
  81. /// <summary>
  82. /// The perlin scale factor for this Rule.
  83. /// </summary>
  84. public float m_PerlinScale = 0.5f;
  85. /// <summary>
  86. /// The output type for this Rule.
  87. /// </summary>
  88. public OutputSprite m_Output = OutputSprite.Single;
  89. /// <summary>
  90. /// The output Collider Type for this Rule.
  91. /// </summary>
  92. public Tile.ColliderType m_ColliderType = Tile.ColliderType.Sprite;
  93. /// <summary>
  94. /// The randomized transform output for this Rule.
  95. /// </summary>
  96. public Transform m_RandomTransform;
  97. /// <summary>
  98. /// The enumeration for matching Neighbors when matching Rule Tiles
  99. /// </summary>
  100. public class Neighbor
  101. {
  102. /// <summary>
  103. /// The Rule Tile will check if the contents of the cell in that direction is an instance of this Rule Tile.
  104. /// If not, the rule will fail.
  105. /// </summary>
  106. public const int This = 1;
  107. /// <summary>
  108. /// The Rule Tile will check if the contents of the cell in that direction is not an instance of this Rule Tile.
  109. /// If it is, the rule will fail.
  110. /// </summary>
  111. public const int NotThis = 2;
  112. }
  113. /// <summary>
  114. /// The enumeration for the transform rule used when matching Rule Tiles.
  115. /// </summary>
  116. public enum Transform
  117. {
  118. /// <summary>
  119. /// The Rule Tile will match Tiles exactly as laid out in its neighbors.
  120. /// </summary>
  121. Fixed,
  122. /// <summary>
  123. /// The Rule Tile will rotate and match its neighbors.
  124. /// </summary>
  125. Rotated,
  126. /// <summary>
  127. /// The Rule Tile will mirror in the X axis and match its neighbors.
  128. /// </summary>
  129. MirrorX,
  130. /// <summary>
  131. /// The Rule Tile will mirror in the Y axis and match its neighbors.
  132. /// </summary>
  133. MirrorY,
  134. /// <summary>
  135. /// The Rule Tile will mirror in the X or Y axis and match its neighbors.
  136. /// </summary>
  137. MirrorXY,
  138. /// <summary>
  139. /// The Rule Tile will rotate and mirror in the X and match its neighbors.
  140. /// </summary>
  141. RotatedMirror
  142. }
  143. /// <summary>
  144. /// The Output for the Tile which fits this Rule.
  145. /// </summary>
  146. public enum OutputSprite
  147. {
  148. /// <summary>
  149. /// A Single Sprite will be output.
  150. /// </summary>
  151. Single,
  152. /// <summary>
  153. /// A Random Sprite will be output.
  154. /// </summary>
  155. Random,
  156. /// <summary>
  157. /// A Sprite Animation will be output.
  158. /// </summary>
  159. Animation
  160. }
  161. }
  162. /// <summary>
  163. /// The data structure holding the Rule information for matching Rule Tiles with
  164. /// its neighbors.
  165. /// </summary>
  166. [Serializable]
  167. public class TilingRule : TilingRuleOutput
  168. {
  169. /// <summary>
  170. /// The matching Rule conditions for each of its neighboring Tiles.
  171. /// </summary>
  172. public List<int> m_Neighbors = new List<int>();
  173. /// <summary>
  174. /// * Preset this list to RuleTile backward compatible, but not support for HexagonalRuleTile backward compatible.
  175. /// </summary>
  176. public List<Vector3Int> m_NeighborPositions = new List<Vector3Int>()
  177. {
  178. new Vector3Int(-1, 1, 0),
  179. new Vector3Int(0, 1, 0),
  180. new Vector3Int(1, 1, 0),
  181. new Vector3Int(-1, 0, 0),
  182. new Vector3Int(1, 0, 0),
  183. new Vector3Int(-1, -1, 0),
  184. new Vector3Int(0, -1, 0),
  185. new Vector3Int(1, -1, 0),
  186. };
  187. /// <summary>
  188. /// The transform matching Rule for this Rule.
  189. /// </summary>
  190. public Transform m_RuleTransform;
  191. /// <summary>
  192. /// This clones a copy of the TilingRule.
  193. /// </summary>
  194. /// <returns>A copy of the TilingRule.</returns>
  195. public TilingRule Clone()
  196. {
  197. TilingRule rule = new TilingRule
  198. {
  199. m_Neighbors = new List<int>(m_Neighbors),
  200. m_NeighborPositions = new List<Vector3Int>(m_NeighborPositions),
  201. m_RuleTransform = m_RuleTransform,
  202. m_Sprites = new Sprite[m_Sprites.Length],
  203. m_GameObject = m_GameObject,
  204. m_MinAnimationSpeed = m_MinAnimationSpeed,
  205. m_MaxAnimationSpeed = m_MaxAnimationSpeed,
  206. m_PerlinScale = m_PerlinScale,
  207. m_Output = m_Output,
  208. m_ColliderType = m_ColliderType,
  209. m_RandomTransform = m_RandomTransform,
  210. };
  211. Array.Copy(m_Sprites, rule.m_Sprites, m_Sprites.Length);
  212. return rule;
  213. }
  214. /// <summary>
  215. /// Returns all neighbors of this Tile as a dictionary
  216. /// </summary>
  217. /// <returns>A dictionary of neighbors for this Tile</returns>
  218. public Dictionary<Vector3Int, int> GetNeighbors()
  219. {
  220. Dictionary<Vector3Int, int> dict = new Dictionary<Vector3Int, int>();
  221. for (int i = 0; i < m_Neighbors.Count && i < m_NeighborPositions.Count; i++)
  222. dict.Add(m_NeighborPositions[i], m_Neighbors[i]);
  223. return dict;
  224. }
  225. /// <summary>
  226. /// Applies the values from the given dictionary as this Tile's neighbors
  227. /// </summary>
  228. /// <param name="dict">Dictionary to apply values from</param>
  229. public void ApplyNeighbors(Dictionary<Vector3Int, int> dict)
  230. {
  231. m_NeighborPositions = dict.Keys.ToList();
  232. m_Neighbors = dict.Values.ToList();
  233. }
  234. /// <summary>
  235. /// Gets the cell bounds of the TilingRule.
  236. /// </summary>
  237. /// <returns>Returns the cell bounds of the TilingRule.</returns>
  238. public BoundsInt GetBounds()
  239. {
  240. BoundsInt bounds = new BoundsInt(Vector3Int.zero, Vector3Int.one);
  241. foreach (var neighbor in GetNeighbors())
  242. {
  243. bounds.xMin = Mathf.Min(bounds.xMin, neighbor.Key.x);
  244. bounds.yMin = Mathf.Min(bounds.yMin, neighbor.Key.y);
  245. bounds.xMax = Mathf.Max(bounds.xMax, neighbor.Key.x + 1);
  246. bounds.yMax = Mathf.Max(bounds.yMax, neighbor.Key.y + 1);
  247. }
  248. return bounds;
  249. }
  250. }
  251. /// <summary>
  252. /// Attribute which marks a property which cannot be overridden by a RuleOverrideTile
  253. /// </summary>
  254. public class DontOverride : Attribute { }
  255. /// <summary>
  256. /// A list of Tiling Rules for the Rule Tile.
  257. /// </summary>
  258. [HideInInspector] public List<TilingRule> m_TilingRules = new List<RuleTile.TilingRule>();
  259. /// <summary>
  260. /// Returns a set of neighboring positions for this RuleTile
  261. /// </summary>
  262. public HashSet<Vector3Int> neighborPositions
  263. {
  264. get
  265. {
  266. if (m_NeighborPositions.Count == 0)
  267. UpdateNeighborPositions();
  268. return m_NeighborPositions;
  269. }
  270. }
  271. private HashSet<Vector3Int> m_NeighborPositions = new HashSet<Vector3Int>();
  272. /// <summary>
  273. /// Updates the neighboring positions of this RuleTile
  274. /// </summary>
  275. public void UpdateNeighborPositions()
  276. {
  277. m_CacheTilemapsNeighborPositions.Clear();
  278. HashSet<Vector3Int> positions = m_NeighborPositions;
  279. positions.Clear();
  280. foreach (TilingRule rule in m_TilingRules)
  281. {
  282. foreach (var neighbor in rule.GetNeighbors())
  283. {
  284. Vector3Int position = neighbor.Key;
  285. positions.Add(position);
  286. // Check rule against rotations of 0, 90, 180, 270
  287. if (rule.m_RuleTransform == TilingRuleOutput.Transform.Rotated)
  288. {
  289. for (int angle = m_RotationAngle; angle < 360; angle += m_RotationAngle)
  290. {
  291. positions.Add(GetRotatedPosition(position, angle));
  292. }
  293. }
  294. // Check rule against x-axis, y-axis mirror
  295. else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorXY)
  296. {
  297. positions.Add(GetMirroredPosition(position, true, true));
  298. positions.Add(GetMirroredPosition(position, true, false));
  299. positions.Add(GetMirroredPosition(position, false, true));
  300. }
  301. // Check rule against x-axis mirror
  302. else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorX)
  303. {
  304. positions.Add(GetMirroredPosition(position, true, false));
  305. }
  306. // Check rule against y-axis mirror
  307. else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorY)
  308. {
  309. positions.Add(GetMirroredPosition(position, false, true));
  310. }
  311. else if (rule.m_RuleTransform == TilingRuleOutput.Transform.RotatedMirror)
  312. {
  313. var mirroredPosition = GetMirroredPosition(position, true, false);
  314. for (int angle = m_RotationAngle; angle < 360; angle += m_RotationAngle)
  315. {
  316. positions.Add(GetRotatedPosition(position, angle));
  317. positions.Add(GetRotatedPosition(mirroredPosition, angle));
  318. }
  319. }
  320. }
  321. }
  322. }
  323. /// <summary>
  324. /// StartUp is called on the first frame of the running Scene.
  325. /// </summary>
  326. /// <param name="position">Position of the Tile on the Tilemap.</param>
  327. /// <param name="tilemap">The Tilemap the tile is present on.</param>
  328. /// <param name="instantiatedGameObject">The GameObject instantiated for the Tile.</param>
  329. /// <returns>Whether StartUp was successful</returns>
  330. public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject instantiatedGameObject)
  331. {
  332. if (instantiatedGameObject != null)
  333. {
  334. Tilemap tmpMap = tilemap.GetComponent<Tilemap>();
  335. Matrix4x4 orientMatrix = tmpMap.orientationMatrix;
  336. var iden = Matrix4x4.identity;
  337. Vector3 gameObjectTranslation = new Vector3();
  338. Quaternion gameObjectRotation = new Quaternion();
  339. Vector3 gameObjectScale = new Vector3();
  340. bool ruleMatched = false;
  341. Matrix4x4 transform = iden;
  342. foreach (TilingRule rule in m_TilingRules)
  343. {
  344. if (RuleMatches(rule, position, tilemap, ref transform))
  345. {
  346. transform = orientMatrix * transform;
  347. // Converts the tile's translation, rotation, & scale matrix to values to be used by the instantiated GameObject
  348. gameObjectTranslation = new Vector3(transform.m03, transform.m13, transform.m23);
  349. gameObjectRotation = Quaternion.LookRotation(new Vector3(transform.m02, transform.m12, transform.m22), new Vector3(transform.m01, transform.m11, transform.m21));
  350. gameObjectScale = transform.lossyScale;
  351. ruleMatched = true;
  352. break;
  353. }
  354. }
  355. if (!ruleMatched)
  356. {
  357. // Fallback to just using the orientMatrix for the translation, rotation, & scale values.
  358. gameObjectTranslation = new Vector3(orientMatrix.m03, orientMatrix.m13, orientMatrix.m23);
  359. gameObjectRotation = Quaternion.LookRotation(new Vector3(orientMatrix.m02, orientMatrix.m12, orientMatrix.m22), new Vector3(orientMatrix.m01, orientMatrix.m11, orientMatrix.m21));
  360. gameObjectScale = orientMatrix.lossyScale;
  361. }
  362. instantiatedGameObject.transform.localPosition = gameObjectTranslation + tmpMap.CellToLocalInterpolated(position + tmpMap.tileAnchor);
  363. instantiatedGameObject.transform.localRotation = gameObjectRotation;
  364. instantiatedGameObject.transform.localScale = gameObjectScale;
  365. }
  366. return true;
  367. }
  368. /// <summary>
  369. /// Retrieves any tile rendering data from the scripted tile.
  370. /// </summary>
  371. /// <param name="position">Position of the Tile on the Tilemap.</param>
  372. /// <param name="tilemap">The Tilemap the tile is present on.</param>
  373. /// <param name="tileData">Data to render the tile.</param>
  374. public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
  375. {
  376. var iden = Matrix4x4.identity;
  377. tileData.sprite = m_DefaultSprite;
  378. tileData.gameObject = m_DefaultGameObject;
  379. tileData.colliderType = m_DefaultColliderType;
  380. tileData.flags = TileFlags.LockTransform;
  381. tileData.transform = iden;
  382. Matrix4x4 transform = iden;
  383. foreach (TilingRule rule in m_TilingRules)
  384. {
  385. if (RuleMatches(rule, position, tilemap, ref transform))
  386. {
  387. switch (rule.m_Output)
  388. {
  389. case TilingRuleOutput.OutputSprite.Single:
  390. case TilingRuleOutput.OutputSprite.Animation:
  391. tileData.sprite = rule.m_Sprites[0];
  392. break;
  393. case TilingRuleOutput.OutputSprite.Random:
  394. int index = Mathf.Clamp(Mathf.FloorToInt(GetPerlinValue(position, rule.m_PerlinScale, 100000f) * rule.m_Sprites.Length), 0, rule.m_Sprites.Length - 1);
  395. tileData.sprite = rule.m_Sprites[index];
  396. if (rule.m_RandomTransform != TilingRuleOutput.Transform.Fixed)
  397. transform = ApplyRandomTransform(rule.m_RandomTransform, transform, rule.m_PerlinScale, position);
  398. break;
  399. }
  400. tileData.transform = transform;
  401. tileData.gameObject = rule.m_GameObject;
  402. tileData.colliderType = rule.m_ColliderType;
  403. break;
  404. }
  405. }
  406. }
  407. /// <summary>
  408. /// Returns a Perlin Noise value based on the given inputs.
  409. /// </summary>
  410. /// <param name="position">Position of the Tile on the Tilemap.</param>
  411. /// <param name="scale">The Perlin Scale factor of the Tile.</param>
  412. /// <param name="offset">Offset of the Tile on the Tilemap.</param>
  413. /// <returns>A Perlin Noise value based on the given inputs.</returns>
  414. public static float GetPerlinValue(Vector3Int position, float scale, float offset)
  415. {
  416. return Mathf.PerlinNoise((position.x + offset) * scale, (position.y + offset) * scale);
  417. }
  418. static Dictionary<Tilemap, KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>>> m_CacheTilemapsNeighborPositions = new Dictionary<Tilemap, KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>>>();
  419. static TileBase[] m_AllocatedUsedTileArr = Array.Empty<TileBase>();
  420. static bool IsTilemapUsedTilesChange(Tilemap tilemap, out KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>> hashSet)
  421. {
  422. if (!m_CacheTilemapsNeighborPositions.TryGetValue(tilemap, out hashSet))
  423. return true;
  424. var oldUsedTiles = hashSet.Key;
  425. int newUsedTilesCount = tilemap.GetUsedTilesCount();
  426. if (newUsedTilesCount != oldUsedTiles.Count)
  427. return true;
  428. if (m_AllocatedUsedTileArr.Length < newUsedTilesCount)
  429. Array.Resize(ref m_AllocatedUsedTileArr, newUsedTilesCount);
  430. tilemap.GetUsedTilesNonAlloc(m_AllocatedUsedTileArr);
  431. for (int i = 0; i < newUsedTilesCount; i++)
  432. {
  433. TileBase newUsedTile = m_AllocatedUsedTileArr[i];
  434. if (!oldUsedTiles.Contains(newUsedTile))
  435. return true;
  436. }
  437. return false;
  438. }
  439. static KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>> CachingTilemapNeighborPositions(Tilemap tilemap)
  440. {
  441. int usedTileCount = tilemap.GetUsedTilesCount();
  442. HashSet<TileBase> usedTiles = new HashSet<TileBase>();
  443. HashSet<Vector3Int> neighborPositions = new HashSet<Vector3Int>();
  444. if (m_AllocatedUsedTileArr.Length < usedTileCount)
  445. Array.Resize(ref m_AllocatedUsedTileArr, usedTileCount);
  446. tilemap.GetUsedTilesNonAlloc(m_AllocatedUsedTileArr);
  447. for (int i = 0; i < usedTileCount; i++)
  448. {
  449. TileBase tile = m_AllocatedUsedTileArr[i];
  450. usedTiles.Add(tile);
  451. RuleTile ruleTile = null;
  452. if (tile is RuleTile rt)
  453. ruleTile = rt;
  454. else if (tile is RuleOverrideTile ot)
  455. ruleTile = ot.m_Tile;
  456. if (ruleTile)
  457. foreach (Vector3Int neighborPosition in ruleTile.neighborPositions)
  458. neighborPositions.Add(neighborPosition);
  459. }
  460. var value = new KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>>(usedTiles, neighborPositions);
  461. m_CacheTilemapsNeighborPositions[tilemap] = value;
  462. return value;
  463. }
  464. static bool NeedRelease()
  465. {
  466. foreach (var keypair in m_CacheTilemapsNeighborPositions)
  467. {
  468. if (keypair.Key == null)
  469. {
  470. return true;
  471. }
  472. }
  473. return false;
  474. }
  475. static void ReleaseDestroyedTilemapCacheData()
  476. {
  477. if (!NeedRelease())
  478. return;
  479. var hasCleared = false;
  480. var keys = m_CacheTilemapsNeighborPositions.Keys.ToArray();
  481. foreach (var key in keys)
  482. {
  483. if (key == null && m_CacheTilemapsNeighborPositions.Remove(key))
  484. hasCleared = true;
  485. }
  486. if (hasCleared)
  487. {
  488. // TrimExcess
  489. m_CacheTilemapsNeighborPositions = new Dictionary<Tilemap, KeyValuePair<HashSet<TileBase>, HashSet<Vector3Int>>>(m_CacheTilemapsNeighborPositions);
  490. }
  491. }
  492. /// <summary>
  493. /// Retrieves any tile animation data from the scripted tile.
  494. /// </summary>
  495. /// <param name="position">Position of the Tile on the Tilemap.</param>
  496. /// <param name="tilemap">The Tilemap the tile is present on.</param>
  497. /// <param name="tileAnimationData">Data to run an animation on the tile.</param>
  498. /// <returns>Whether the call was successful.</returns>
  499. public override bool GetTileAnimationData(Vector3Int position, ITilemap tilemap, ref TileAnimationData tileAnimationData)
  500. {
  501. Matrix4x4 transform = Matrix4x4.identity;
  502. foreach (TilingRule rule in m_TilingRules)
  503. {
  504. if (rule.m_Output == TilingRuleOutput.OutputSprite.Animation)
  505. {
  506. if (RuleMatches(rule, position, tilemap, ref transform))
  507. {
  508. tileAnimationData.animatedSprites = rule.m_Sprites;
  509. tileAnimationData.animationSpeed = Random.Range( rule.m_MinAnimationSpeed, rule.m_MaxAnimationSpeed);
  510. return true;
  511. }
  512. }
  513. }
  514. return false;
  515. }
  516. /// <summary>
  517. /// This method is called when the tile is refreshed.
  518. /// </summary>
  519. /// <param name="position">Position of the Tile on the Tilemap.</param>
  520. /// <param name="tilemap">The Tilemap the tile is present on.</param>
  521. public override void RefreshTile(Vector3Int position, ITilemap tilemap)
  522. {
  523. base.RefreshTile(position, tilemap);
  524. Tilemap baseTilemap = tilemap.GetComponent<Tilemap>();
  525. ReleaseDestroyedTilemapCacheData(); // Prevent memory leak
  526. if (IsTilemapUsedTilesChange(baseTilemap, out var neighborPositionsSet))
  527. neighborPositionsSet = CachingTilemapNeighborPositions(baseTilemap);
  528. var neighborPositionsRuleTile = neighborPositionsSet.Value;
  529. foreach (Vector3Int offset in neighborPositionsRuleTile)
  530. {
  531. Vector3Int offsetPosition = GetOffsetPositionReverse(position, offset);
  532. TileBase tile = tilemap.GetTile(offsetPosition);
  533. RuleTile ruleTile = null;
  534. if (tile is RuleTile rt)
  535. ruleTile = rt;
  536. else if (tile is RuleOverrideTile ot)
  537. ruleTile = ot.m_Tile;
  538. if (ruleTile != null)
  539. if (ruleTile == this || ruleTile.neighborPositions.Contains(offset))
  540. base.RefreshTile(offsetPosition, tilemap);
  541. }
  542. }
  543. /// <summary>
  544. /// Does a Rule Match given a Tiling Rule and neighboring Tiles.
  545. /// </summary>
  546. /// <param name="rule">The Tiling Rule to match with.</param>
  547. /// <param name="position">Position of the Tile on the Tilemap.</param>
  548. /// <param name="tilemap">The tilemap to match with.</param>
  549. /// <param name="transform">A transform matrix which will match the Rule.</param>
  550. /// <returns>True if there is a match, False if not.</returns>
  551. public virtual bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, ref Matrix4x4 transform)
  552. {
  553. if (RuleMatches(rule, position, tilemap, 0))
  554. {
  555. transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, 0f), Vector3.one);
  556. return true;
  557. }
  558. // Check rule against rotations of 0, 90, 180, 270
  559. if (rule.m_RuleTransform == TilingRuleOutput.Transform.Rotated)
  560. {
  561. for (int angle = m_RotationAngle; angle < 360; angle += m_RotationAngle)
  562. {
  563. if (RuleMatches(rule, position, tilemap, angle))
  564. {
  565. transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), Vector3.one);
  566. return true;
  567. }
  568. }
  569. }
  570. // Check rule against x-axis, y-axis mirror
  571. else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorXY)
  572. {
  573. if (RuleMatches(rule, position, tilemap, true, true))
  574. {
  575. transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, -1f, 1f));
  576. return true;
  577. }
  578. if (RuleMatches(rule, position, tilemap, true, false))
  579. {
  580. transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, 1f, 1f));
  581. return true;
  582. }
  583. if (RuleMatches(rule, position, tilemap, false, true))
  584. {
  585. transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, -1f, 1f));
  586. return true;
  587. }
  588. }
  589. // Check rule against x-axis mirror
  590. else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorX)
  591. {
  592. if (RuleMatches(rule, position, tilemap, true, false))
  593. {
  594. transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1f, 1f, 1f));
  595. return true;
  596. }
  597. }
  598. // Check rule against y-axis mirror
  599. else if (rule.m_RuleTransform == TilingRuleOutput.Transform.MirrorY)
  600. {
  601. if (RuleMatches(rule, position, tilemap, false, true))
  602. {
  603. transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, -1f, 1f));
  604. return true;
  605. }
  606. }
  607. // Check rule against x-axis mirror with rotations of 0, 90, 180, 270
  608. else if (rule.m_RuleTransform == TilingRuleOutput.Transform.RotatedMirror)
  609. {
  610. for (int angle = 0; angle < 360; angle += m_RotationAngle)
  611. {
  612. if (angle != 0 && RuleMatches(rule, position, tilemap, angle))
  613. {
  614. transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), Vector3.one);
  615. return true;
  616. }
  617. if (RuleMatches(rule, position, tilemap, angle, true))
  618. {
  619. transform = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), new Vector3(-1f, 1f, 1f));
  620. return true;
  621. }
  622. }
  623. }
  624. return false;
  625. }
  626. /// <summary>
  627. /// Returns a random transform matrix given the random transform rule.
  628. /// </summary>
  629. /// <param name="type">Random transform rule.</param>
  630. /// <param name="original">The original transform matrix.</param>
  631. /// <param name="perlinScale">The Perlin Scale factor of the Tile.</param>
  632. /// <param name="position">Position of the Tile on the Tilemap.</param>
  633. /// <returns>A random transform matrix.</returns>
  634. public virtual Matrix4x4 ApplyRandomTransform(TilingRuleOutput.Transform type, Matrix4x4 original, float perlinScale, Vector3Int position)
  635. {
  636. float perlin = GetPerlinValue(position, perlinScale, 200000f);
  637. switch (type)
  638. {
  639. case TilingRuleOutput.Transform.MirrorXY:
  640. return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(Math.Abs(perlin - 0.5) > 0.25 ? 1f : -1f, perlin < 0.5 ? 1f : -1f, 1f));
  641. case TilingRuleOutput.Transform.MirrorX:
  642. return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(perlin < 0.5 ? 1f : -1f, 1f, 1f));
  643. case TilingRuleOutput.Transform.MirrorY:
  644. return original * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, perlin < 0.5 ? 1f : -1f, 1f));
  645. case TilingRuleOutput.Transform.Rotated:
  646. {
  647. var angle = Mathf.Clamp(Mathf.FloorToInt(perlin * m_RotationCount), 0, m_RotationCount - 1) * m_RotationAngle;
  648. return Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), Vector3.one);
  649. }
  650. case TilingRuleOutput.Transform.RotatedMirror:
  651. {
  652. var angle = Mathf.Clamp(Mathf.FloorToInt(perlin * m_RotationCount), 0, m_RotationCount - 1) * m_RotationAngle;
  653. return Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0f, 0f, -angle), new Vector3(perlin < 0.5 ? 1f : -1f, 1f, 1f));
  654. }
  655. }
  656. return original;
  657. }
  658. /// <summary>
  659. /// Returns custom fields for this RuleTile
  660. /// </summary>
  661. /// <param name="isOverrideInstance">Whether override fields are returned</param>
  662. /// <returns>Custom fields for this RuleTile</returns>
  663. public FieldInfo[] GetCustomFields(bool isOverrideInstance)
  664. {
  665. return this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
  666. .Where(field => typeof(RuleTile).GetField(field.Name) == null)
  667. .Where(field => field.IsPublic || field.IsDefined(typeof(SerializeField)))
  668. .Where(field => !field.IsDefined(typeof(HideInInspector)))
  669. .Where(field => !isOverrideInstance || !field.IsDefined(typeof(DontOverride)))
  670. .ToArray();
  671. }
  672. /// <summary>
  673. /// Checks if there is a match given the neighbor matching rule and a Tile.
  674. /// </summary>
  675. /// <param name="neighbor">Neighbor matching rule.</param>
  676. /// <param name="other">Tile to match.</param>
  677. /// <returns>True if there is a match, False if not.</returns>
  678. public virtual bool RuleMatch(int neighbor, TileBase other)
  679. {
  680. if (other is RuleOverrideTile ot)
  681. other = ot.m_InstanceTile;
  682. switch (neighbor)
  683. {
  684. case TilingRuleOutput.Neighbor.This: return other == this;
  685. case TilingRuleOutput.Neighbor.NotThis: return other != this;
  686. }
  687. return true;
  688. }
  689. /// <summary>
  690. /// Checks if there is a match given the neighbor matching rule and a Tile with a rotation angle.
  691. /// </summary>
  692. /// <param name="rule">Neighbor matching rule.</param>
  693. /// <param name="position">Position of the Tile on the Tilemap.</param>
  694. /// <param name="tilemap">Tilemap to match.</param>
  695. /// <param name="angle">Rotation angle for matching.</param>
  696. /// <returns>True if there is a match, False if not.</returns>
  697. public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, int angle, bool mirrorX = false)
  698. {
  699. var minCount = Math.Min(rule.m_Neighbors.Count, rule.m_NeighborPositions.Count);
  700. for (int i = 0; i < minCount ; i++)
  701. {
  702. var neighbor = rule.m_Neighbors[i];
  703. var neighborPosition = rule.m_NeighborPositions[i];
  704. if (mirrorX)
  705. neighborPosition = GetMirroredPosition(neighborPosition, true, false);
  706. var positionOffset = GetRotatedPosition(neighborPosition, angle);
  707. var other = tilemap.GetTile(GetOffsetPosition(position, positionOffset));
  708. if (!RuleMatch(neighbor, other))
  709. {
  710. return false;
  711. }
  712. }
  713. return true;
  714. }
  715. /// <summary>
  716. /// Checks if there is a match given the neighbor matching rule and a Tile with mirrored axii.
  717. /// </summary>
  718. /// <param name="rule">Neighbor matching rule.</param>
  719. /// <param name="position">Position of the Tile on the Tilemap.</param>
  720. /// <param name="tilemap">Tilemap to match.</param>
  721. /// <param name="mirrorX">Mirror X Axis for matching.</param>
  722. /// <param name="mirrorY">Mirror Y Axis for matching.</param>
  723. /// <returns>True if there is a match, False if not.</returns>
  724. public bool RuleMatches(TilingRule rule, Vector3Int position, ITilemap tilemap, bool mirrorX, bool mirrorY)
  725. {
  726. var minCount = Math.Min(rule.m_Neighbors.Count, rule.m_NeighborPositions.Count);
  727. for (int i = 0; i < minCount; i++)
  728. {
  729. int neighbor = rule.m_Neighbors[i];
  730. Vector3Int positionOffset = GetMirroredPosition(rule.m_NeighborPositions[i], mirrorX, mirrorY);
  731. TileBase other = tilemap.GetTile(GetOffsetPosition(position, positionOffset));
  732. if (!RuleMatch(neighbor, other))
  733. {
  734. return false;
  735. }
  736. }
  737. return true;
  738. }
  739. /// <summary>
  740. /// Gets a rotated position given its original position and the rotation in degrees.
  741. /// </summary>
  742. /// <param name="position">Original position of Tile.</param>
  743. /// <param name="rotation">Rotation in degrees.</param>
  744. /// <returns>Rotated position of Tile.</returns>
  745. public virtual Vector3Int GetRotatedPosition(Vector3Int position, int rotation)
  746. {
  747. switch (rotation)
  748. {
  749. case 0:
  750. return position;
  751. case 90:
  752. return new Vector3Int(position.y, -position.x, 0);
  753. case 180:
  754. return new Vector3Int(-position.x, -position.y, 0);
  755. case 270:
  756. return new Vector3Int(-position.y, position.x, 0);
  757. }
  758. return position;
  759. }
  760. /// <summary>
  761. /// Gets a mirrored position given its original position and the mirroring axii.
  762. /// </summary>
  763. /// <param name="position">Original position of Tile.</param>
  764. /// <param name="mirrorX">Mirror in the X Axis.</param>
  765. /// <param name="mirrorY">Mirror in the Y Axis.</param>
  766. /// <returns>Mirrored position of Tile.</returns>
  767. public virtual Vector3Int GetMirroredPosition(Vector3Int position, bool mirrorX, bool mirrorY)
  768. {
  769. if (mirrorX)
  770. position.x *= -1;
  771. if (mirrorY)
  772. position.y *= -1;
  773. return position;
  774. }
  775. /// <summary>
  776. /// Get the offset for the given position with the given offset.
  777. /// </summary>
  778. /// <param name="position">Position to offset.</param>
  779. /// <param name="offset">Offset for the position.</param>
  780. /// <returns>The offset position.</returns>
  781. public virtual Vector3Int GetOffsetPosition(Vector3Int position, Vector3Int offset)
  782. {
  783. return position + offset;
  784. }
  785. /// <summary>
  786. /// Get the reversed offset for the given position with the given offset.
  787. /// </summary>
  788. /// <param name="position">Position to offset.</param>
  789. /// <param name="offset">Offset for the position.</param>
  790. /// <returns>The reversed offset position.</returns>
  791. public virtual Vector3Int GetOffsetPositionReverse(Vector3Int position, Vector3Int offset)
  792. {
  793. return position - offset;
  794. }
  795. }
  796. }