├── Helpers ├── HashCodeCalculator.cs └── MyCollectionExtension.cs ├── README.md ├── WFC_1_Preset └── WFC_1_Preset.unitypackage ├── WFC_2_InputReader_Step ├── Input │ ├── IInputReader.cs │ ├── IValue.cs │ ├── InputImageParameters.cs │ ├── InputReader.cs │ ├── TileBaseValue.cs │ └── TileContainer.cs ├── TEst.cs └── WFC_2_InputReaderStep.unitypackage ├── WFC_3_ValueManager ├── ValuesManager.cs └── WFC_3_ValueManager.unitypackage ├── WFC_4_PatternManager ├── Pattern.cs ├── PatternData.cs ├── PatternDataResults.cs ├── PatternFinder.cs ├── PatternManager.cs ├── PatternNeighbours.cs ├── Strategies │ ├── IFindNeighbourStrategy.cs │ ├── NeighbourStrategyFactory.cs │ ├── NeighboursStrategySize1Default.cs │ └── NeighboursStrategySize2andMore.cs └── WFC_4_PatternManager.unitypackage ├── WFC_5_CoreSolver ├── CoreHelper.cs ├── CoreSolver.cs ├── LowEntropyCell.cs ├── OutputGrid.cs ├── PropragationHelper.cs ├── VectorPair.cs ├── WFCCore.cs └── WFC_5_CoreSolver.unitypackage ├── WFC_6_OutputGenerator ├── Editor │ └── WfcInspector.cs ├── IOutputCreator.cs ├── TileMapOutput.cs └── WFC_6_OutputGenerator.unitypackage ├── WFC_7_Bug_fixEntropyCalculation └── CoreHelper.cs └── WFC_8_Final_Version.unitypackage /Helpers/HashCodeCalculator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | 6 | public static class HashCodeCalculator 7 | { 8 | //https://support.microsoft.com/da-dk/help/307020/how-to-compute-and-compare-hash-values-by-using-visual-c 9 | public static string CalculateHashCode(int[][] grid) 10 | { 11 | byte[] tmpSource = grid.SelectMany(x => GetByteArrayFromIntArray(x)).ToArray(); 12 | 13 | byte[] tmpHash = new MD5CryptoServiceProvider().ComputeHash(tmpSource); 14 | return ByteArrayToString(tmpHash); 15 | 16 | } 17 | 18 | private static byte[] GetByteArrayFromIntArray(int[] intArray) 19 | 20 | { 21 | 22 | byte[] data = new byte[intArray.Length * 4]; 23 | 24 | for (int i = 0; i < intArray.Length; i++) 25 | 26 | Array.Copy(BitConverter.GetBytes(intArray[i]), 0, data, i * 4, 4); 27 | 28 | return data; 29 | 30 | } 31 | 32 | private static string ByteArrayToString(byte[] arrInput) 33 | { 34 | int i; 35 | StringBuilder sOutput = new StringBuilder(arrInput.Length); 36 | for (i = 0; i < arrInput.Length; i++) 37 | { 38 | sOutput.Append(arrInput[i].ToString("X2")); 39 | } 40 | return sOutput.ToString(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Helpers/MyCollectionExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | namespace Helpers 8 | { 9 | public static class MyCollectionExtension 10 | { 11 | 12 | //https://stackoverflow.com/questions/1738990/initializing-jagged-arrays 13 | public static T CreateJaggedArray(params int[] lengths) 14 | { 15 | return (T)InitializeJaggedArray(typeof(T).GetElementType(), 0, lengths); 16 | } 17 | 18 | static object InitializeJaggedArray(Type type, int index, int[] lengths) 19 | { 20 | Array array = Array.CreateInstance(type, lengths[index]); 21 | Type elementType = type.GetElementType(); 22 | 23 | if (elementType != null) 24 | { 25 | for (int i = 0; i < lengths[index]; i++) 26 | { 27 | array.SetValue( 28 | InitializeJaggedArray(elementType, index + 1, lengths), i); 29 | } 30 | } 31 | 32 | return array; 33 | } 34 | 35 | public static bool CheckJaggedArray2IfIndexIsValid(this T[][] array, int x, int y) 36 | { 37 | if (array == null) //|| x >= array.Length || array[0] == null || y >= array[0].Length || x < 0 || y < 0 38 | { 39 | return false; 40 | } 41 | return ValidateCoordinates(x, y, array[0].Length, array.Length); 42 | 43 | } 44 | 45 | public static bool ValidateCoordinates(int x, int y, int width, int height) 46 | { 47 | if (x < 0 || x >= width || y < 0 || y >= height) 48 | return false; 49 | return true; 50 | } 51 | } 52 | 53 | 54 | } 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WaveFunctionCollapseUnityTilemapTutorial 2 | Tutorial files for implementing Wave Function Collapse using tilemaps in unity. 3 | 4 | This is an implementation of wave function collapse algorithm using Unity and its Tilemap system. 5 | 6 | [![Tutorial](https://img.youtube.com/vi/ws4r3wLPNSE/0.jpg)](https://youtu.be/ws4r3wLPNSE) 7 | MIT Licence 8 |

Attribution: 9 | Made by Sunny Valley Studio 10 | 11 | -------------------------------------------------------------------------------- /WFC_1_Preset/WFC_1_Preset.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyValleyStudio/WaveFunctionCollapseUnityTilemapTutorial/cc54ff3b648eeaf06601d48254fb1190c79a4dd6/WFC_1_Preset/WFC_1_Preset.unitypackage -------------------------------------------------------------------------------- /WFC_2_InputReader_Step/Input/IInputReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace WaveFunctionCollapse 6 | { 7 | public interface IInputReader 8 | { 9 | IValue[][] ReadInputToGrid(); 10 | } 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /WFC_2_InputReader_Step/Input/IValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | namespace WaveFunctionCollapse 7 | { 8 | public interface IValue : IEqualityComparer>, IEquatable> 9 | { 10 | T Value { get; } 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /WFC_2_InputReader_Step/Input/InputImageParameters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | using UnityEngine.Tilemaps; 7 | 8 | namespace WaveFunctionCollapse 9 | { 10 | public class InputImageParameters 11 | { 12 | Vector2Int? bottomRightTileCoords = null; 13 | Vector2Int? topLeftTileCoords = null; 14 | BoundsInt inputTileMapBounds; 15 | TileBase[] inputTilemapTilesArray; 16 | Queue stackOftiles = new Queue(); 17 | private int width = 0, height = 0; 18 | private Tilemap inputTilemap; 19 | 20 | public Queue StackOftiles { get => stackOftiles; set => stackOftiles = value; } 21 | public int Height { get => height;} 22 | public int Width { get => width;} 23 | 24 | 25 | public InputImageParameters(Tilemap inputTilemap) 26 | { 27 | this.inputTilemap = inputTilemap; 28 | this.inputTileMapBounds = this.inputTilemap.cellBounds; 29 | this.inputTilemapTilesArray = this.inputTilemap.GetTilesBlock(this.inputTileMapBounds); 30 | ExtractNonEmptyTiles(); 31 | VeryfyInputTiles(); 32 | } 33 | 34 | private void VeryfyInputTiles() 35 | { 36 | if(topLeftTileCoords==null || bottomRightTileCoords == null) 37 | { 38 | throw new System.Exception("WFC: Input tIlemap is empty"); 39 | } 40 | int minX = bottomRightTileCoords.Value.x; 41 | int maxX = topLeftTileCoords.Value.x; 42 | int minY = bottomRightTileCoords.Value.y; 43 | int maxY = topLeftTileCoords.Value.y; 44 | 45 | width = Math.Abs(maxX - minX) + 1; 46 | height = Math.Abs(maxY - minY) + 1; 47 | 48 | int tileCount = width * height; 49 | if (stackOftiles.Count != tileCount) 50 | { 51 | throw new System.Exception("WFC: Tilemap has empty fields"); 52 | } 53 | if(stackOftiles.Any(tile => tile.X > maxX || tile.X < minX || tile.Y > maxY || tile.Y < minY)) 54 | { 55 | throw new System.Exception("WFC: Tilemap image should be a filled rectangle"); 56 | } 57 | } 58 | 59 | private void ExtractNonEmptyTiles() 60 | { 61 | for (int row = 0; row < inputTileMapBounds.size.y; row++) 62 | { 63 | for (int col = 0; col < inputTileMapBounds.size.x; col++) 64 | { 65 | int index = col + (row * inputTileMapBounds.size.x); 66 | 67 | TileBase tile = inputTilemapTilesArray[index]; 68 | if(bottomRightTileCoords==null && tile!= null) 69 | { 70 | bottomRightTileCoords = new Vector2Int(col, row); 71 | } 72 | if (tile != null) 73 | { 74 | stackOftiles.Enqueue(new TileContainer(tile, col, row)); 75 | topLeftTileCoords = new Vector2Int(col, row); 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /WFC_2_InputReader_Step/Input/InputReader.cs: -------------------------------------------------------------------------------- 1 | using Helpers; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using UnityEngine; 6 | using UnityEngine.Tilemaps; 7 | 8 | namespace WaveFunctionCollapse 9 | { 10 | public class InputReader : IInputReader 11 | { 12 | 13 | private Tilemap _inputTilemap; 14 | 15 | public InputReader(Tilemap input) 16 | { 17 | _inputTilemap = input; 18 | } 19 | public IValue[][] ReadInputToGrid() 20 | { 21 | var grid = ReadInputTileMap(); 22 | 23 | TileBaseValue[][] gridOfValues = null; 24 | if(grid != null) 25 | { 26 | gridOfValues = MyCollectionExtension.CreateJaggedArray(grid.Length, grid[0].Length); 27 | for (int row = 0; row < grid.Length; row++) 28 | { 29 | for (int col = 0; col < grid[0].Length; col++) 30 | { 31 | gridOfValues[row][col] = new TileBaseValue(grid[row][col]); 32 | } 33 | } 34 | } 35 | 36 | return gridOfValues; 37 | } 38 | 39 | private TileBase[][] ReadInputTileMap() 40 | { 41 | InputImageParameters imagerParameters = new InputImageParameters(_inputTilemap); 42 | return CreteTileBaseGrid(imagerParameters); 43 | } 44 | 45 | private TileBase[][] CreteTileBaseGrid(InputImageParameters imagerParameters) 46 | { 47 | TileBase[][] gridOfInputTiles = null; 48 | gridOfInputTiles = MyCollectionExtension.CreateJaggedArray(imagerParameters.Height, imagerParameters.Width); 49 | for (int row = 0; row < imagerParameters.Height; row++) 50 | { 51 | for (int col = 0; col < imagerParameters.Width; col++) 52 | { 53 | gridOfInputTiles[row][col] = imagerParameters.StackOftiles.Dequeue().Tile; 54 | } 55 | } 56 | 57 | return gridOfInputTiles; 58 | } 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /WFC_2_InputReader_Step/Input/TileBaseValue.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.Tilemaps; 5 | 6 | namespace WaveFunctionCollapse 7 | { 8 | public class TileBaseValue : IValue 9 | { 10 | private TileBase tileBase; 11 | 12 | public TileBaseValue(TileBase tileBase) 13 | { 14 | this.tileBase = tileBase; 15 | } 16 | 17 | public TileBase Value => this.tileBase; 18 | 19 | public bool Equals(IValue x, IValue y) 20 | { 21 | return x == y; 22 | } 23 | 24 | public bool Equals(IValue other) 25 | { 26 | return other.Value == this.Value; 27 | } 28 | 29 | public int GetHashCode(IValue obj) 30 | { 31 | return obj.GetHashCode(); 32 | } 33 | public override int GetHashCode() 34 | { 35 | return this.tileBase.GetHashCode(); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /WFC_2_InputReader_Step/Input/TileContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.Tilemaps; 5 | 6 | namespace WaveFunctionCollapse 7 | { 8 | public class TileContainer 9 | { 10 | public TileBase Tile { get; set; } 11 | public int X { get; set; } 12 | public int Y { get; set; } 13 | 14 | public TileContainer(TileBase tile, int X, int Y) 15 | { 16 | this.Tile = tile; 17 | this.X = X; 18 | this.Y = Y; 19 | } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /WFC_2_InputReader_Step/TEst.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.Tilemaps; 5 | using WaveFunctionCollapse; 6 | 7 | public class TEst : MonoBehaviour 8 | { 9 | public Tilemap input; 10 | // Start is called before the first frame update 11 | void Start() 12 | { 13 | InputReader reader = new InputReader(input); 14 | var grid = reader.ReadInputToGrid(); 15 | for (int row = 0; row < grid.Length; row++) 16 | { 17 | for (int col = 0; col < grid[0].Length; col++) 18 | { 19 | Debug.Log("Row: " + row + " Col: " + col + " tile name " + grid[row][col].Value.name); 20 | } 21 | } 22 | } 23 | 24 | // Update is called once per frame 25 | void Update() 26 | { 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /WFC_2_InputReader_Step/WFC_2_InputReaderStep.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyValleyStudio/WaveFunctionCollapseUnityTilemapTutorial/cc54ff3b648eeaf06601d48254fb1190c79a4dd6/WFC_2_InputReader_Step/WFC_2_InputReaderStep.unitypackage -------------------------------------------------------------------------------- /WFC_3_ValueManager/ValuesManager.cs: -------------------------------------------------------------------------------- 1 | using Helpers; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using UnityEngine; 7 | 8 | namespace WaveFunctionCollapse 9 | { 10 | public class ValuesManager 11 | { 12 | int[][] _grid; 13 | Dictionary> valueIndexDictionary = new Dictionary>(); 14 | int index = 0; 15 | 16 | public ValuesManager(IValue[][] gridOfValues) 17 | { 18 | CreateGridOfIndices(gridOfValues); 19 | } 20 | 21 | private void CreateGridOfIndices(IValue[][] gridOfValues) 22 | { 23 | _grid = MyCollectionExtension.CreateJaggedArray(gridOfValues.Length, gridOfValues[0].Length); 24 | for (int row = 0; row < gridOfValues.Length; row++) 25 | { 26 | for (int col = 0; col < gridOfValues[0].Length; col++) 27 | { 28 | SetIndexToGridPosition(gridOfValues, row, col); 29 | } 30 | } 31 | } 32 | 33 | private void SetIndexToGridPosition(IValue[][] gridOfValues, int row, int col) 34 | { 35 | if (valueIndexDictionary.ContainsValue(gridOfValues[row][col])) 36 | { 37 | var key = valueIndexDictionary.FirstOrDefault(x => x.Value.Equals(gridOfValues[row][col])); 38 | _grid[row][col] = key.Key; 39 | } 40 | else 41 | { 42 | _grid[row][col] = index; 43 | valueIndexDictionary.Add(_grid[row][col], gridOfValues[row][col]); 44 | index++; 45 | } 46 | } 47 | 48 | public int GetGridValue(int x, int y) 49 | { 50 | if (x >= _grid[0].Length || y >= _grid.Length || x < 0 || y < 0) 51 | { 52 | throw new System.IndexOutOfRangeException("Grid doeant contain x: " + x + " y: " + y + " value"); 53 | } 54 | return _grid[y][x]; 55 | } 56 | 57 | public IValue GetValueFromIndex(int index) 58 | { 59 | if (valueIndexDictionary.ContainsKey(index)) 60 | { 61 | return valueIndexDictionary[index]; 62 | } 63 | throw new System.Exception("No index " + index + " in valueDictionary"); 64 | } 65 | 66 | public int GetGridValuesIncludingOffset(int x, int y) 67 | { 68 | int yMax = _grid.Length; 69 | int xMax = _grid[0].Length; 70 | if(x<0 && y < 0) 71 | { 72 | return GetGridValue(xMax + x, yMax + y); 73 | } 74 | if(x<0 && y >= yMax) 75 | { 76 | return GetGridValue(xMax + x, y - yMax); 77 | } 78 | if(x>=xMax && y < 0) 79 | { 80 | return GetGridValue(x - xMax, yMax + y); 81 | } 82 | if(x >=xMax && y >= yMax) 83 | { 84 | return GetGridValue(x - xMax, y - yMax); 85 | } 86 | 87 | if (x < 0) 88 | { 89 | return GetGridValue(xMax + x, y); 90 | } 91 | if (x >= xMax) 92 | { 93 | return GetGridValue(x - xMax, y); 94 | } 95 | if (y < 0) 96 | { 97 | return GetGridValue(x, yMax + y); 98 | } 99 | 100 | if (y >= yMax) 101 | { 102 | return GetGridValue(x, y - yMax); 103 | } 104 | return GetGridValue(x, y); 105 | 106 | } 107 | 108 | public int[][] GetPatternValuesFromGridAt(int x, int y, int patternSize) 109 | { 110 | int[][] arrayToReturn = MyCollectionExtension.CreateJaggedArray(patternSize, patternSize); 111 | for (int row = 0; row < patternSize; row++) 112 | { 113 | for (int col = 0; col < patternSize; col++) 114 | { 115 | arrayToReturn[row][col] = GetGridValuesIncludingOffset(x + col, y + row); 116 | } 117 | } 118 | 119 | return arrayToReturn; 120 | } 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /WFC_3_ValueManager/WFC_3_ValueManager.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyValleyStudio/WaveFunctionCollapseUnityTilemapTutorial/cc54ff3b648eeaf06601d48254fb1190c79a4dd6/WFC_3_ValueManager/WFC_3_ValueManager.unitypackage -------------------------------------------------------------------------------- /WFC_4_PatternManager/Pattern.cs: -------------------------------------------------------------------------------- 1 | using Helpers; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using UnityEngine; 6 | 7 | namespace WaveFunctionCollapse 8 | { 9 | public class Pattern 10 | { 11 | private int _index; 12 | private int[][] _grid; 13 | 14 | public string HashIndex { get; set; } 15 | public int Index { get => _index; } 16 | 17 | public Pattern(int[][] grid, string hashCode, int index) 18 | { 19 | _grid = grid; 20 | HashIndex = hashCode; 21 | _index = index; 22 | } 23 | 24 | public void SetGridVAlue(int x, int y, int value) 25 | { 26 | _grid[y][x] = value; 27 | } 28 | 29 | public int GetGridValue(int x, int y) 30 | { 31 | return _grid[y][x]; 32 | } 33 | 34 | public bool CheckValueAtPosition(int x, int y, int value) 35 | { 36 | return value.Equals(GetGridValue(x, y)); 37 | } 38 | 39 | internal bool ComparePatternToAnotherPattern(Direction dir, Pattern pattern) 40 | { 41 | int[][] myGrid = GetGridValuesInDirection(dir); 42 | int[][] otherGrid = pattern.GetGridValuesInDirection(dir.GetOppositeDirectionTo()); 43 | 44 | for (int row = 0; row < myGrid.Length; row++) 45 | { 46 | for (int col = 0; col < myGrid[0].Length; col++) 47 | { 48 | if (myGrid[row][col] != otherGrid[row][col]) 49 | { 50 | return false; 51 | } 52 | } 53 | } 54 | return true; 55 | } 56 | 57 | private int[][] GetGridValuesInDirection(Direction dir) 58 | { 59 | int[][] gridPartToCompare; 60 | switch (dir) 61 | { 62 | case Direction.Up: 63 | gridPartToCompare = MyCollectionExtension.CreateJaggedArray(_grid.Length - 1, _grid.Length); 64 | CreatePartOfGrid(0, _grid.Length, 1, _grid.Length, gridPartToCompare); 65 | break; 66 | case Direction.Down: 67 | gridPartToCompare = MyCollectionExtension.CreateJaggedArray(_grid.Length - 1, _grid.Length); 68 | CreatePartOfGrid(0, _grid.Length, 0, _grid.Length-1, gridPartToCompare); 69 | break; 70 | case Direction.Left: 71 | gridPartToCompare = MyCollectionExtension.CreateJaggedArray(_grid.Length, _grid.Length - 1); 72 | CreatePartOfGrid(0, _grid.Length-1, 0, _grid.Length, gridPartToCompare); 73 | break; 74 | case Direction.Right: 75 | gridPartToCompare = MyCollectionExtension.CreateJaggedArray(_grid.Length, _grid.Length - 1); 76 | CreatePartOfGrid(1, _grid.Length, 0, _grid.Length, gridPartToCompare); 77 | break; 78 | default: 79 | return _grid; 80 | } 81 | 82 | return gridPartToCompare; 83 | } 84 | 85 | private void CreatePartOfGrid(int xmin, int xmax, int ymin, int ymax, int[][] gridPartToCompare) 86 | { 87 | List tempList = new List(); 88 | for (int row = ymin; row < ymax; row++) 89 | { 90 | for (int col = xmin; col < xmax; col++) 91 | { 92 | tempList.Add(_grid[row][col]); 93 | } 94 | } 95 | 96 | for (int i = 0; i < tempList.Count; i++) 97 | { 98 | int x = i % gridPartToCompare.Length; 99 | int y = i / gridPartToCompare.Length; 100 | gridPartToCompare[x][y] = tempList[i]; 101 | } 102 | } 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /WFC_4_PatternManager/PatternData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace WaveFunctionCollapse 6 | { 7 | public class PatternData 8 | { 9 | private Pattern _pattern; 10 | private int _frequency; 11 | private float _frequencyRelative; 12 | private float _frequencyRelativeLog2; 13 | 14 | public float FrequencyRelative { get => _frequencyRelative;} 15 | public Pattern Pattern { get => _pattern;} 16 | public float FrequencyRelativeLog2 { get => _frequencyRelativeLog2;} 17 | 18 | public PatternData(Pattern pattern) 19 | { 20 | _pattern = pattern; 21 | } 22 | 23 | public void AddToFrequency() 24 | { 25 | _frequency++; 26 | } 27 | 28 | public void CalculateRelativeFrequency(int total) 29 | { 30 | _frequencyRelative = (float)_frequency / total; 31 | _frequencyRelativeLog2 = Mathf.Log(_frequencyRelative, 2); 32 | } 33 | 34 | public bool CompareGrid(Direction dir, PatternData data) 35 | { 36 | return _pattern.ComparePatternToAnotherPattern(dir, data.Pattern); 37 | } 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /WFC_4_PatternManager/PatternDataResults.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Helpers; 5 | 6 | namespace WaveFunctionCollapse 7 | { 8 | public class PatternDataResults 9 | { 10 | private int[][] patternIndicesGrid; 11 | public Dictionary PatternIndexDictionary { get; private set; } 12 | 13 | public PatternDataResults(int[][] patternIndicesGrid, Dictionary patternIndexDictionary) 14 | { 15 | this.patternIndicesGrid = patternIndicesGrid; 16 | PatternIndexDictionary = patternIndexDictionary; 17 | } 18 | 19 | public int GetGridLengthX() 20 | { 21 | return patternIndicesGrid[0].Length; 22 | } 23 | 24 | public int GetGridLengthY() 25 | { 26 | return patternIndicesGrid.Length; 27 | } 28 | 29 | public int GetIndexAt(int x, int y) 30 | { 31 | return patternIndicesGrid[y][x]; 32 | } 33 | 34 | public int GetNeighbourInDirection(int x, int y, Direction dir) 35 | { 36 | if (patternIndicesGrid.CheckJaggedArray2IfIndexIsValid(x, y) == false) 37 | { 38 | return -1; 39 | } 40 | switch (dir) 41 | { 42 | case Direction.Up: 43 | if (patternIndicesGrid.CheckJaggedArray2IfIndexIsValid(x, y + 1)) 44 | { 45 | return GetIndexAt(x, y + 1); 46 | } 47 | return -1; 48 | case Direction.Down: 49 | if (patternIndicesGrid.CheckJaggedArray2IfIndexIsValid(x, y - 1)) 50 | { 51 | return GetIndexAt(x, y- 1); 52 | } 53 | return -1; 54 | case Direction.Left: 55 | if (patternIndicesGrid.CheckJaggedArray2IfIndexIsValid(x-1, y)) 56 | { 57 | return GetIndexAt(x-1, y); 58 | } 59 | return -1; 60 | case Direction.Right: 61 | if (patternIndicesGrid.CheckJaggedArray2IfIndexIsValid(x+1, y)) 62 | { 63 | return GetIndexAt(x+1, y); 64 | } 65 | return -1; 66 | default: 67 | return -1; 68 | } 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /WFC_4_PatternManager/PatternFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | using UnityEngine; 8 | using Helpers; 9 | 10 | namespace WaveFunctionCollaps 11 | { 12 | public static class PatternFinder 13 | { 14 | public static PatternDataResults GetPatternDataFromGrid(ValuesManager valuesManager, int patternSize, bool equalWeights) 15 | { 16 | Dictionary patternHashcodeDictionary = new Dictionary(); 17 | Dictionary patternIndexDictionary = new Dictionary(); 18 | Vector2 sizeOfGrid = valuesManager.GetGridSize(); 19 | int patternGridSizeX = 0; 20 | int patternGridSizeY = 0; 21 | int rowMin = -1, colMin = -1, rowMax =-1, colMax =-1; 22 | if (patternSize < 3) 23 | { 24 | patternGridSizeX = (int)sizeOfGrid.x + 3 - patternSize; 25 | patternGridSizeY = (int)sizeOfGrid.y + 3 - patternSize; 26 | rowMax = patternGridSizeY - 1; 27 | colMax = patternGridSizeX - 1; 28 | } 29 | else 30 | { 31 | patternGridSizeX = (int)sizeOfGrid.x + patternSize - 1; 32 | patternGridSizeY = (int)sizeOfGrid.y + patternSize - 1; 33 | rowMin = 1 - patternSize; 34 | colMin = 1 - patternSize; 35 | rowMax = (int)sizeOfGrid.y; 36 | colMax = (int)sizeOfGrid.x; 37 | } 38 | 39 | int[][] patternIndicesGrid = MyCollectionExtension.CreateJaggedArray(patternGridSizeY, patternGridSizeX); 40 | int totalFrequency = 0; 41 | 42 | //we loop with offset -1 / +1 to get patterns. At the same time we have to account for patten size. 43 | //If pattern is of size 2 we will be reaching x+1 and y+1 to check all 4 values. Need visual aid. 44 | 45 | int patternIndex = 0; 46 | for (int row = rowMin; row < rowMax; row++) 47 | { 48 | for (int col = colMin; col < colMax; col++) 49 | { 50 | int[][] gridValues = valuesManager.GetPatternValuesFromGridAt(col, row, patternSize); 51 | string hashValue = HashCodeCalculator.CalculateHashCode(gridValues); 52 | 53 | if (patternHashcodeDictionary.ContainsKey(hashValue) == false) 54 | { 55 | Pattern pattern = new Pattern(gridValues, hashValue, patternIndex); 56 | patternIndex++; 57 | AddNewPattern(patternHashcodeDictionary, patternIndexDictionary, hashValue, pattern); 58 | } 59 | else 60 | { 61 | 62 | if (equalWeights == false) 63 | patternIndexDictionary[patternHashcodeDictionary[hashValue].Pattern.Index].AddToFrequency(); 64 | 65 | 66 | } 67 | //if (patternSize > colMin || row >= rowMin && row < rowMax-1 && col >= colMin && col < colMax-1) 68 | //{ 69 | 70 | // totalFrequency++; 71 | 72 | //} 73 | totalFrequency++; 74 | if (patternSize<3) 75 | patternIndicesGrid[row + 1][col + 1] = patternHashcodeDictionary[hashValue].Pattern.Index; 76 | else 77 | patternIndicesGrid[row + patternSize - 1][col + patternSize - 1] = patternHashcodeDictionary[hashValue].Pattern.Index; 78 | } 79 | } 80 | 81 | CalculateRelativeFrequency(patternIndexDictionary, totalFrequency); 82 | 83 | return new PatternDataResults(patternIndicesGrid, patternIndexDictionary); 84 | } 85 | 86 | private static void CalculateRelativeFrequency(Dictionary patternIndexDictionary, int totalFrequency) 87 | { 88 | foreach (var item in patternIndexDictionary.Values) 89 | { 90 | item.CalculateRelativeFrequency(totalFrequency); 91 | } 92 | } 93 | 94 | public static Dictionary FindPossibleNeighbursForAllPatterns(IFindNeighboutStrategy patternFinder, PatternDataResults patterndataResults) 95 | { 96 | 97 | return patternFinder.FIndNeighbours(patterndataResults); 98 | } 99 | 100 | public static PatternNeighbours CheckNeighboursInEachDirection(int x, int y, PatternDataResults patterndataResults) 101 | { 102 | PatternNeighbours neighbours = new PatternNeighbours(); 103 | foreach (Direction dir in Enum.GetValues(typeof(Direction))) 104 | { 105 | int possiblePatternIndex = patterndataResults.GetNeighbourInDirection(x, y, dir); 106 | if (possiblePatternIndex >= 0) 107 | { 108 | neighbours.AddPatternToDirection(dir, possiblePatternIndex); 109 | } 110 | } 111 | return neighbours; 112 | } 113 | 114 | public static void AddNeighboursToDictionary(Dictionary dictionary, int patternIndex, PatternNeighbours neighbours) 115 | { 116 | if (dictionary.ContainsKey(patternIndex) == false) 117 | { 118 | 119 | dictionary.Add(patternIndex, neighbours); 120 | 121 | } 122 | dictionary[patternIndex].AddNeighbours(neighbours); 123 | 124 | } 125 | 126 | private static void AddNewPattern(Dictionary patternHashcodeDictionary, Dictionary patternIndexDictionary, string hashValue, Pattern pattern) 127 | { 128 | 129 | PatternData patternData = new PatternData(pattern); 130 | patternHashcodeDictionary.Add(hashValue, patternData); 131 | patternIndexDictionary.Add(pattern.Index, patternData); 132 | } 133 | 134 | public static bool AreArraysTheSame(int[][] arr1, int[][] arr2) 135 | { 136 | string arr1hash = HashCodeCalculator.CalculateHashCode(arr1); 137 | string arr2hash = HashCodeCalculator.CalculateHashCode(arr2); 138 | return arr1hash.Equals(arr2hash); 139 | 140 | } 141 | 142 | 143 | } 144 | 145 | 146 | 147 | 148 | 149 | } 150 | 151 | -------------------------------------------------------------------------------- /WFC_4_PatternManager/PatternManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | namespace WaveFunctionCollapse 8 | { 9 | public class PatternManager 10 | { 11 | Dictionary patternDataIndexDictionary; 12 | Dictionary patternPossibleNeighboursDictionary; 13 | int _patternSize = -1; 14 | IFindNeighbourStrategy strategy; 15 | 16 | public PatternManager(int patternSize) 17 | { 18 | _patternSize = patternSize; 19 | } 20 | 21 | public void ProcessGrid(ValuesManager valueManager, bool equalWeights, string strategyName = null) 22 | { 23 | NeighbourStrategyFactory strategyFactory = new NeighbourStrategyFactory(); 24 | strategy = strategyFactory.CreteInstance(strategyName == null ? _patternSize + "" : strategyName); 25 | CreatePatterns(valueManager, strategy, equalWeights); 26 | } 27 | 28 | private void CreatePatterns(ValuesManager valueManager, IFindNeighbourStrategy strategy, bool equalWeights) 29 | { 30 | var patternFinderResult = PatternFinder.GetPatternDataFromGrid(valueManager, _patternSize, equalWeights); 31 | //StringBuilder builder = null; 32 | //List list = new List(); 33 | //for (int row = 0; row < patternFinderResult.GetGridLengthY(); row++) 34 | //{ 35 | // builder = new StringBuilder(); 36 | // for (int col = 0; col < patternFinderResult.GetGridLengthX(); col++) 37 | // { 38 | // builder.Append(patternFinderResult.GetIndexAt(col,row) + " "); 39 | // } 40 | // list.Add(builder.ToString()); 41 | //} 42 | //list.Reverse(); 43 | //foreach (var item in list) 44 | //{ 45 | // Debug.Log(item); 46 | //} 47 | patternDataIndexDictionary = patternFinderResult.PatternIndexDictionary; 48 | GetPatternNeighbours(patternFinderResult, strategy); 49 | } 50 | 51 | private void GetPatternNeighbours(PatternDataResults patternFinderResult, IFindNeighbourStrategy strategy) 52 | { 53 | patternPossibleNeighboursDictionary = PatternFinder.FindPossibleNeighboursForAllPatterns(strategy, patternFinderResult); 54 | } 55 | 56 | public PatternData GetPatternDataFromIndex(int index) 57 | { 58 | return patternDataIndexDictionary[index]; 59 | } 60 | 61 | public HashSet GetPossibleNeighboursForPatternInDirection(int patternIndex, Direction dir) 62 | { 63 | return patternPossibleNeighboursDictionary[patternIndex].GetNeighboursInDirection(dir); 64 | } 65 | 66 | public float GetPatternFrequency(int index) 67 | { 68 | return GetPatternDataFromIndex(index).FrequencyRelative; 69 | } 70 | 71 | public float GetPatternFrequencyLog2(int index) 72 | { 73 | return GetPatternDataFromIndex(index).FrequencyRelativeLog2; 74 | } 75 | 76 | public int GetNuberOfPatterns() 77 | { 78 | return patternDataIndexDictionary.Count; 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /WFC_4_PatternManager/PatternNeighbours.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using WaveFunctionCollapse; 6 | 7 | namespace WaveFunctionCollapse 8 | { 9 | public class PatternNeighbours 10 | { 11 | public Dictionary> directionPatternNeighbourDictionary = new Dictionary>(); 12 | 13 | public void AddPatternToDictionary(Direction dir, int patternIndex) 14 | { 15 | if (directionPatternNeighbourDictionary.ContainsKey(dir)) 16 | { 17 | directionPatternNeighbourDictionary[dir].Add(patternIndex); 18 | } 19 | else 20 | { 21 | directionPatternNeighbourDictionary.Add(dir, new HashSet() { patternIndex }); 22 | } 23 | } 24 | 25 | internal HashSet GetNeighboursInDirection(Direction dir) 26 | { 27 | if (directionPatternNeighbourDictionary.ContainsKey(dir)) 28 | { 29 | return directionPatternNeighbourDictionary[dir]; 30 | } 31 | return new HashSet(); 32 | } 33 | 34 | public void AddNeighbour(PatternNeighbours neighbours) 35 | { 36 | foreach (var item in neighbours.directionPatternNeighbourDictionary) 37 | { 38 | if (directionPatternNeighbourDictionary.ContainsKey(item.Key) == false) 39 | { 40 | directionPatternNeighbourDictionary.Add(item.Key, new HashSet()); 41 | } 42 | directionPatternNeighbourDictionary[item.Key].UnionWith(item.Value); 43 | } 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /WFC_4_PatternManager/Strategies/IFindNeighbourStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | namespace WaveFunctionCollapse 7 | { 8 | public interface IFindNeighbourStrategy 9 | { 10 | 11 | Dictionary FindNeighbours(PatternDataResults patternFinderResult); 12 | } 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /WFC_4_PatternManager/Strategies/NeighbourStrategyFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using UnityEngine; 6 | 7 | namespace WaveFunctionCollapse 8 | { 9 | public class NeighbourStrategyFactory 10 | { 11 | Dictionary strategies; 12 | 13 | public NeighbourStrategyFactory() 14 | { 15 | LoadTypesIFindNeighbourStrategy(); 16 | } 17 | 18 | private void LoadTypesIFindNeighbourStrategy() 19 | { 20 | strategies = new Dictionary(); 21 | Type[] typesInThisAssembly = Assembly.GetExecutingAssembly().GetTypes(); 22 | 23 | foreach(var type in typesInThisAssembly) 24 | { 25 | if (type.GetInterface(typeof(IFindNeighbourStrategy).ToString()) != null) 26 | { 27 | strategies.Add(type.Name.ToLower(), type); 28 | } 29 | } 30 | } 31 | 32 | internal IFindNeighbourStrategy CreteInstance(string nameOfStrategy) 33 | { 34 | Type t = GetTypeToCreate(nameOfStrategy); 35 | if (t == null) 36 | { 37 | t = GetTypeToCreate("more"); 38 | } 39 | return Activator.CreateInstance(t) as IFindNeighbourStrategy; 40 | } 41 | 42 | private Type GetTypeToCreate(string nameOfStrategy) 43 | { 44 | foreach (var possibleStrategy in strategies) 45 | { 46 | if (possibleStrategy.Key.Contains(nameOfStrategy)){ 47 | return possibleStrategy.Value; 48 | } 49 | } 50 | return null; 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /WFC_4_PatternManager/Strategies/NeighboursStrategySize1Default.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | namespace WaveFunctionCollapse 7 | { 8 | 9 | public class NeighboursStrategySize1Default : IFindNeighbourStrategy 10 | { 11 | public Dictionary FindNeighbours(PatternDataResults patternFinderResult) 12 | { 13 | Dictionary result = new Dictionary(); 14 | FindNeighboursForEachPattern(patternFinderResult, result); 15 | return result; 16 | } 17 | 18 | private void FindNeighboursForEachPattern(PatternDataResults patternFinderResult, Dictionary result) 19 | { 20 | for (int row = 0; row < patternFinderResult.GetGridLengthY(); row++) 21 | { 22 | for (int col = 0; col < patternFinderResult.GetGridLengthX(); col++) 23 | { 24 | PatternNeighbours neighbours = PatternFinder.CheckNeighboursInEachDirection(col, row, patternFinderResult); 25 | PatternFinder.AddNeighboursToDictionary(result, patternFinderResult.GetIndexAt(col, row), neighbours); 26 | } 27 | } 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /WFC_4_PatternManager/Strategies/NeighboursStrategySize2andMore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | namespace WaveFunctionCollapse 7 | { 8 | public class NeighboursStrategySize2andMore : IFindNeighbourStrategy 9 | { 10 | public Dictionary FindNeighbours(PatternDataResults patternFinderResult) 11 | { 12 | Dictionary result = new Dictionary(); 13 | foreach (var patternDataToCheck in patternFinderResult.PatternIndexDictionary) 14 | { 15 | foreach (var possibleNeighbourForPattern in patternFinderResult.PatternIndexDictionary) 16 | { 17 | FindNeighboursInAllDirections(result, patternDataToCheck, possibleNeighbourForPattern); 18 | } 19 | } 20 | return result; 21 | } 22 | 23 | private void FindNeighboursInAllDirections(Dictionary result, KeyValuePair patternDataToCheck, KeyValuePair possibleNeighbourForPattern) 24 | { 25 | foreach (Direction dir in Enum.GetValues(typeof(Direction))) 26 | { 27 | if (patternDataToCheck.Value.CompareGrid(dir, possibleNeighbourForPattern.Value)){ 28 | if (result.ContainsKey(patternDataToCheck.Key) == false) 29 | { 30 | result.Add(patternDataToCheck.Key, new PatternNeighbours()); 31 | } 32 | result[patternDataToCheck.Key].AddPatternToDictionary(dir, possibleNeighbourForPattern.Key); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /WFC_4_PatternManager/WFC_4_PatternManager.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyValleyStudio/WaveFunctionCollapseUnityTilemapTutorial/cc54ff3b648eeaf06601d48254fb1190c79a4dd6/WFC_4_PatternManager/WFC_4_PatternManager.unitypackage -------------------------------------------------------------------------------- /WFC_5_CoreSolver/CoreHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | namespace WaveFunctionCollapse 8 | { 9 | public class CoreHelper 10 | { 11 | float totalFrequency = 0; 12 | float totalFrequencyLog = 0; 13 | PatternManager patternManager; 14 | 15 | public CoreHelper(PatternManager manager) 16 | { 17 | patternManager = manager; 18 | 19 | for (int i = 0; i < patternManager.GetNuberOfPatterns(); i++) 20 | { 21 | totalFrequency += patternManager.GetPatternFrequency(i); 22 | } 23 | totalFrequencyLog = Mathf.Log(totalFrequency, 2); 24 | } 25 | 26 | public int SelectSolutionPatternFromFrequency(List possibleValues) 27 | { 28 | List valueFrequenciesFractions = GetListOfWeightsFromIndices(possibleValues); 29 | float randomValue = UnityEngine.Random.Range(0, valueFrequenciesFractions.Sum()); 30 | float sum = 0; 31 | int index = 0; 32 | foreach (var item in valueFrequenciesFractions) 33 | { 34 | sum += item; 35 | if(randomValue <= sum) 36 | { 37 | return index; 38 | } 39 | index++; 40 | } 41 | return index; 42 | } 43 | 44 | 45 | 46 | private List GetListOfWeightsFromIndices(List possibleValues) 47 | { 48 | var valueFrequencies = possibleValues.Aggregate(new List(), (acc, val) => 49 | { 50 | acc.Add(patternManager.GetPatternFrequency(val)); 51 | return acc; 52 | }, 53 | acc => acc).ToList(); 54 | return valueFrequencies; 55 | 56 | } 57 | 58 | public List Create4DirectionNeighbours(Vector2Int cellCoordinates, Vector2Int previousCell) 59 | { 60 | List list = new List() 61 | { 62 | new VectorPair(cellCoordinates, cellCoordinates+new Vector2Int(1,0), Direction.Right,previousCell), 63 | new VectorPair(cellCoordinates, cellCoordinates+new Vector2Int(-1,0), Direction.Left,previousCell), 64 | new VectorPair(cellCoordinates, cellCoordinates+new Vector2Int(0,1), Direction.Up,previousCell), 65 | new VectorPair(cellCoordinates, cellCoordinates+new Vector2Int(0,-1), Direction.Down,previousCell), 66 | }; 67 | return list; 68 | } 69 | 70 | public List Create4DirectionNeighbours(Vector2Int cellCoordinate) 71 | { 72 | return Create4DirectionNeighbours(cellCoordinate, cellCoordinate); 73 | } 74 | 75 | public float CalculateEntropy(Vector2Int position, OutputGrid outputGrid) 76 | { 77 | float sum = 0; 78 | foreach (var possibleIndex in outputGrid.GetPossibleValueForPossition(position)) 79 | { 80 | sum += patternManager.GetPatternFrequencyLog2(possibleIndex); 81 | } 82 | return totalFrequencyLog - (sum / totalFrequency); 83 | } 84 | 85 | public List CheckIfNeighboursAreCollapsed(VectorPair pairToCheck, OutputGrid outputgrid) 86 | { 87 | return Create4DirectionNeighbours(pairToCheck.CellToPropagatePosition, pairToCheck.BaseCellPosition) 88 | .Where(x => outputgrid.CheckIfValidPosition(x.CellToPropagatePosition) && outputgrid.CheckIfCellIsCollapsed(x.CellToPropagatePosition) == false) 89 | .ToList(); 90 | } 91 | 92 | public bool CheckCellSolutionForCollision(Vector2Int cellCoordinates, OutputGrid outputGrid) 93 | { 94 | foreach (var neighbour in Create4DirectionNeighbours(cellCoordinates)) 95 | { 96 | if (outputGrid.CheckIfValidPosition(neighbour.CellToPropagatePosition) == false) 97 | { 98 | continue; 99 | } 100 | HashSet possibleIndices = new HashSet(); 101 | foreach (var patternIndexAtNeighbour in outputGrid.GetPossibleValueForPossition(neighbour.CellToPropagatePosition)) 102 | { 103 | var possibleNeighboursForBase = patternManager.GetPossibleNeighboursForPatternInDirection(patternIndexAtNeighbour, neighbour.DirectionFromBase.GetOppositeDirectionTo()); 104 | possibleIndices.UnionWith(possibleNeighboursForBase); 105 | } 106 | if (possibleIndices.Contains(outputGrid.GetPossibleValueForPossition(cellCoordinates).First()) == false) 107 | { 108 | return true; 109 | } 110 | } 111 | 112 | return false; 113 | } 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /WFC_5_CoreSolver/CoreSolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | namespace WaveFunctionCollapse 8 | { 9 | public class CoreSolver 10 | { 11 | PatternManager patternManager; 12 | OutputGrid outputGrid; 13 | CoreHelper coreHelper; 14 | PropragationHelper propagationHelper; 15 | 16 | public CoreSolver(OutputGrid outputGrid, PatternManager patternManager) 17 | { 18 | this.outputGrid = outputGrid; 19 | this.patternManager = patternManager; 20 | this.coreHelper = new CoreHelper(this.patternManager); 21 | this.propagationHelper = new PropragationHelper(this.outputGrid, this.coreHelper); 22 | } 23 | 24 | public void Propagate() 25 | { 26 | while (propagationHelper.PairsToPropagate.Count >0) 27 | { 28 | var propagatePair = propagationHelper.PairsToPropagate.Dequeue(); 29 | if (propagationHelper.CheckIfPairShouldBeProcessed(propagatePair)) 30 | { 31 | ProcessCell(propagatePair); 32 | } 33 | if(propagationHelper.CheckForConflicts() || outputGrid.CheckIfGridIsSolved()) 34 | { 35 | return; 36 | } 37 | } 38 | if(propagationHelper.CheckForConflicts() && propagationHelper.PairsToPropagate.Count==0 && propagationHelper.LowEntropySet.Count == 0) 39 | { 40 | propagationHelper.SetConflictFlag(); 41 | } 42 | } 43 | 44 | private void ProcessCell(VectorPair propagatePair) 45 | { 46 | if (outputGrid.CheckIfCellIsCollapsed(propagatePair.CellToPropagatePosition)){ 47 | propagationHelper.EnqueueUncollapseNeighbours(propagatePair); 48 | } 49 | else 50 | { 51 | PropagateNeighbour(propagatePair); 52 | } 53 | } 54 | 55 | private void PropagateNeighbour(VectorPair propagatePair) 56 | { 57 | var possibleValuesAtNeighbour = outputGrid.GetPossibleValueForPossition(propagatePair.CellToPropagatePosition); 58 | int startCount = possibleValuesAtNeighbour.Count; 59 | 60 | RemoveImpossibleNeighbours(propagatePair, possibleValuesAtNeighbour); 61 | 62 | int newPossiblePatternCount = possibleValuesAtNeighbour.Count; 63 | propagationHelper.AnalyzePropagationResults(propagatePair, startCount, newPossiblePatternCount); 64 | } 65 | 66 | private void RemoveImpossibleNeighbours(VectorPair propagatePair, HashSet possibleValuesAtNeighbour) 67 | { 68 | HashSet possibleIndices = new HashSet(); 69 | 70 | foreach (var patternIndexAtBase in outputGrid.GetPossibleValueForPossition(propagatePair.BaseCellPosition)) 71 | { 72 | var possibleNeighboursForBase = patternManager.GetPossibleNeighboursForPatternInDirection(patternIndexAtBase, propagatePair.DirectionFromBase); 73 | possibleIndices.UnionWith(possibleNeighboursForBase); 74 | } 75 | 76 | possibleValuesAtNeighbour.IntersectWith(possibleIndices); 77 | } 78 | 79 | public Vector2Int GetLowestEntropyCell() 80 | { 81 | if(propagationHelper.LowEntropySet.Count <= 0) 82 | { 83 | return outputGrid.GetRandomCell(); 84 | } 85 | else 86 | { 87 | var lowestEntropyElement = propagationHelper.LowEntropySet.First(); 88 | Vector2Int returnVector = lowestEntropyElement.Position; 89 | propagationHelper.LowEntropySet.Remove(lowestEntropyElement); 90 | return returnVector; 91 | } 92 | } 93 | 94 | public void CollapseCell(Vector2Int cellCoordinates) 95 | { 96 | var possibleValue = outputGrid.GetPossibleValueForPossition(cellCoordinates).ToList(); 97 | 98 | if(possibleValue.Count==0 || possibleValue.Count == 1) 99 | { 100 | return; 101 | } 102 | else 103 | { 104 | int index = coreHelper.SelectSolutionPatternFromFrequency(possibleValue); 105 | outputGrid.SetPatternOnPosition(cellCoordinates.x, cellCoordinates.y, possibleValue[index]); 106 | } 107 | 108 | if (coreHelper.CheckCellSolutionForCollision(cellCoordinates, outputGrid) == false) 109 | { 110 | propagationHelper.AddNewPairsToPropagateQueue(cellCoordinates, cellCoordinates); 111 | } 112 | else 113 | { 114 | propagationHelper.SetConflictFlag(); 115 | } 116 | 117 | } 118 | 119 | public bool CheckIfSolved() 120 | { 121 | return outputGrid.CheckIfGridIsSolved(); 122 | } 123 | 124 | public bool CheckForConflicts() 125 | { 126 | return propagationHelper.CheckForConflicts(); 127 | } 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /WFC_5_CoreSolver/LowEntropyCell.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | namespace WaveFunctionCollapse 7 | { 8 | public class LowEntropyCell : IComparable, IEqualityComparer 9 | { 10 | public Vector2Int Position { get; set; } 11 | public float Entropy { get; set; } 12 | private float smallEntropyNoise; 13 | 14 | public LowEntropyCell(Vector2Int position, float entropy) 15 | { 16 | smallEntropyNoise = UnityEngine.Random.Range(0.001f, 0.005f); 17 | this.Entropy = entropy+smallEntropyNoise; 18 | this.Position = position; 19 | } 20 | 21 | public int CompareTo(LowEntropyCell other) 22 | { 23 | if (Entropy > other.Entropy) return 1; 24 | else if (Entropy < other.Entropy) return -1; 25 | else return 0; 26 | } 27 | 28 | public bool Equals(LowEntropyCell cell1, LowEntropyCell cell2) 29 | { 30 | return cell1.Position.x == cell2.Position.x && cell1.Position.y == cell2.Position.y; 31 | } 32 | 33 | public int GetHashCode(LowEntropyCell obj) 34 | { 35 | return obj.GetHashCode(); 36 | } 37 | 38 | public override int GetHashCode() 39 | { 40 | return Position.GetHashCode(); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /WFC_5_CoreSolver/OutputGrid.cs: -------------------------------------------------------------------------------- 1 | using Helpers; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using UnityEngine; 8 | 9 | namespace WaveFunctionCollapse 10 | { 11 | public class OutputGrid 12 | { 13 | Dictionary> indexPossiblePatternDictionary = new Dictionary>(); 14 | public int width { get; } 15 | public int height { get; } 16 | private int maxNumberOfPatterns = 0; 17 | 18 | public OutputGrid(int width, int height, int numberOfPatterns) 19 | { 20 | this.width = width; 21 | this.height = height; 22 | this.maxNumberOfPatterns = numberOfPatterns; 23 | ResetAllPossibilities(); 24 | } 25 | 26 | public void ResetAllPossibilities() 27 | { 28 | HashSet allPossiblePatternList = new HashSet(); 29 | allPossiblePatternList.UnionWith(Enumerable.Range(0, this.maxNumberOfPatterns).ToList()); 30 | 31 | indexPossiblePatternDictionary.Clear(); 32 | for (int i = 0; i < height * width; i++) 33 | { 34 | indexPossiblePatternDictionary.Add(i, new HashSet(allPossiblePatternList)); 35 | } 36 | 37 | } 38 | 39 | public bool CheckCellExists(Vector2Int position) 40 | { 41 | int index = GetIndexFromCoordinates(position); 42 | return indexPossiblePatternDictionary.ContainsKey(index); 43 | } 44 | 45 | private int GetIndexFromCoordinates(Vector2Int position) 46 | { 47 | return position.x + width * position.y; 48 | } 49 | 50 | public bool CheckIfCellIsCollapsed(Vector2Int position) 51 | { 52 | return GetPossibleValueForPossition(position).Count <= 1; 53 | } 54 | 55 | public HashSet GetPossibleValueForPossition(Vector2Int position) 56 | { 57 | int index = GetIndexFromCoordinates(position); 58 | if (indexPossiblePatternDictionary.ContainsKey(index)) 59 | { 60 | return indexPossiblePatternDictionary[index]; 61 | } 62 | return new HashSet(); 63 | } 64 | 65 | internal void PrintResultsToConsol() 66 | { 67 | StringBuilder builder = null; 68 | List list = new List(); 69 | for (int row = 0; row < this.height; row++) 70 | { 71 | builder = new StringBuilder(); 72 | for (int col = 0; col < this.width; col++) 73 | { 74 | var result = GetPossibleValueForPossition(new Vector2Int(col, row)); 75 | if (result.Count == 1) 76 | { 77 | builder.Append(result.First() + " "); 78 | } 79 | else 80 | { 81 | string newString = ""; 82 | foreach (var item in result) 83 | { 84 | newString += item + ","; 85 | } 86 | builder.Append(newString); 87 | } 88 | } 89 | list.Add(builder.ToString()); 90 | } 91 | list.Reverse(); 92 | foreach (var item in list) 93 | { 94 | Debug.Log(item); 95 | } 96 | Debug.Log("---"); 97 | } 98 | 99 | public bool CheckIfGridIsSolved() 100 | { 101 | return !indexPossiblePatternDictionary.Any(x => x.Value.Count > 1); 102 | } 103 | 104 | internal bool CheckIfValidPosition(Vector2Int position) 105 | { 106 | return MyCollectionExtension.ValidateCoordinates(position.x, position.y, width, height); 107 | } 108 | 109 | public Vector2Int GetRandomCell() 110 | { 111 | int randmIndex = UnityEngine.Random.Range(0, indexPossiblePatternDictionary.Count); 112 | return GetCoordsFromIndex(randmIndex); 113 | } 114 | 115 | private Vector2Int GetCoordsFromIndex(int randmIndex) 116 | { 117 | Vector2Int coords = Vector2Int.zero; 118 | coords.x = randmIndex / this.width; 119 | coords.y = randmIndex % this.height; 120 | return coords; 121 | } 122 | 123 | public void SetPatternOnPosition(int x, int y, int patternIndex) 124 | { 125 | int index = GetIndexFromCoordinates(new Vector2Int(x, y)); 126 | indexPossiblePatternDictionary[index] = new HashSet() { patternIndex }; 127 | } 128 | 129 | public int[][] GetSolvedOutputGrid() 130 | { 131 | int[][] returnGrid = MyCollectionExtension.CreateJaggedArray(this.height, this.width); 132 | if (CheckIfGridIsSolved() == false) 133 | { 134 | return MyCollectionExtension.CreateJaggedArray(0, 0); 135 | } 136 | for (int row = 0; row < this.height; row++) 137 | { 138 | for (int col = 0; col < this.width; col++) 139 | { 140 | int index = GetIndexFromCoordinates(new Vector2Int(col, row)); 141 | returnGrid[row][col] = indexPossiblePatternDictionary[index].First(); 142 | } 143 | } 144 | return returnGrid; 145 | } 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /WFC_5_CoreSolver/PropragationHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | namespace WaveFunctionCollapse 8 | { 9 | public class PropragationHelper 10 | { 11 | OutputGrid outputGrid; 12 | CoreHelper coreHelper; 13 | bool cellWithNoSolutionPresent; 14 | SortedSet lowEntropySet = new SortedSet(); 15 | Queue pairsToPropagate = new Queue(); 16 | 17 | public SortedSet LowEntropySet { get => lowEntropySet;} 18 | public Queue PairsToPropagate { get => pairsToPropagate;} 19 | 20 | public PropragationHelper(OutputGrid outputGrid, CoreHelper coreHelper) 21 | { 22 | this.outputGrid = outputGrid; 23 | this.coreHelper = coreHelper; 24 | } 25 | 26 | public bool CheckIfPairShouldBeProcessed(VectorPair propagationPair) 27 | { 28 | return outputGrid.CheckIfValidPosition(propagationPair.CellToPropagatePosition) && propagationPair.AreWeCheckingPreviousCellAgain() == false; 29 | } 30 | 31 | public void AnalyzePropagationResults(VectorPair propagatePair, int startCount, int newPossiblePatternCount) 32 | { 33 | if(newPossiblePatternCount >1 && startCount > newPossiblePatternCount) 34 | { 35 | AddNewPairsToPropagateQueue(propagatePair.CellToPropagatePosition, propagatePair.BaseCellPosition); 36 | AddToLowEntropySet(propagatePair.CellToPropagatePosition); 37 | } 38 | if (newPossiblePatternCount == 0) 39 | { 40 | cellWithNoSolutionPresent = true; 41 | Debug.Log("cell with no solution after base propagation."); 42 | } 43 | if (newPossiblePatternCount == 1) 44 | { 45 | cellWithNoSolutionPresent = coreHelper.CheckCellSolutionForCollision(propagatePair.CellToPropagatePosition, outputGrid); 46 | } 47 | } 48 | 49 | internal void EnqueueUncollapseNeighbours(VectorPair propagatePair) 50 | { 51 | var uncollapsedNeighbours = coreHelper.CheckIfNeighboursAreCollapsed(propagatePair, outputGrid); 52 | foreach (var uncollapsed in uncollapsedNeighbours) 53 | { 54 | pairsToPropagate.Enqueue(uncollapsed); 55 | } 56 | } 57 | 58 | private void AddToLowEntropySet(Vector2Int cellToPropagatePosition) 59 | { 60 | var elementOfLowEntropySet = lowEntropySet.Where(x => x.Position == cellToPropagatePosition).FirstOrDefault(); 61 | if(elementOfLowEntropySet == null && outputGrid.CheckIfCellIsCollapsed(cellToPropagatePosition) == false) 62 | { 63 | float entropy = coreHelper.CalculateEntropy(cellToPropagatePosition, outputGrid); 64 | lowEntropySet.Add(new LowEntropyCell(cellToPropagatePosition,entropy)); 65 | } 66 | else 67 | { 68 | lowEntropySet.Remove(elementOfLowEntropySet); 69 | elementOfLowEntropySet.Entropy = coreHelper.CalculateEntropy(cellToPropagatePosition, outputGrid); 70 | lowEntropySet.Add(elementOfLowEntropySet); 71 | } 72 | } 73 | 74 | public void AddNewPairsToPropagateQueue(Vector2Int cellToPropagatePosition, Vector2Int baseCellPosition) 75 | { 76 | var list = coreHelper.Create4DirectionNeighbours(cellToPropagatePosition,baseCellPosition); 77 | foreach (var item in list) 78 | { 79 | pairsToPropagate.Enqueue(item); 80 | } 81 | } 82 | 83 | public bool CheckForConflicts() 84 | { 85 | return cellWithNoSolutionPresent; 86 | } 87 | 88 | public void SetConflictFlag() 89 | { 90 | cellWithNoSolutionPresent = true; 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /WFC_5_CoreSolver/VectorPair.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace WaveFunctionCollapse 6 | { 7 | public class VectorPair 8 | { 9 | public Vector2Int BaseCellPosition { get; set; } 10 | public Vector2Int CellToPropagatePosition { get; set; } 11 | 12 | public Vector2Int PreviousCellPosition { get; set; } 13 | 14 | public Direction DirectionFromBase { get; set; } 15 | 16 | public VectorPair(Vector2Int baseCellPosition, Vector2Int cellToPropagatePosition, Direction directionFromBase, Vector2Int previousCellPosition) 17 | { 18 | BaseCellPosition = baseCellPosition; 19 | CellToPropagatePosition = cellToPropagatePosition; 20 | DirectionFromBase = directionFromBase; 21 | PreviousCellPosition = previousCellPosition; 22 | } 23 | 24 | public bool AreWeCheckingPreviousCellAgain() 25 | { 26 | return PreviousCellPosition == CellToPropagatePosition; 27 | } 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /WFC_5_CoreSolver/WFCCore.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace WaveFunctionCollapse{ 6 | public class WFCCore 7 | { 8 | OutputGrid outputGrid; 9 | PatternManager patternManager; 10 | 11 | private int maxIterations = 0; 12 | 13 | public WFCCore(int outputWidth, int outputHeight, int maxIterations, PatternManager patternManage) 14 | { 15 | this.outputGrid = new OutputGrid(outputWidth, outputHeight, patternManage.GetNuberOfPatterns()); 16 | this.patternManager = patternManage; 17 | this.maxIterations = maxIterations; 18 | } 19 | 20 | public int[][] CreateOputputGrid() 21 | { 22 | int iteration = 0; 23 | while(iteration < this.maxIterations) 24 | { 25 | CoreSolver solver = new CoreSolver(this.outputGrid, this.patternManager); 26 | int innerIteration = 100; 27 | while(!solver.CheckForConflicts() && !solver.CheckIfSolved()) 28 | { 29 | Vector2Int position = solver.GetLowestEntropyCell(); 30 | solver.CollapseCell(position); 31 | solver.Propagate(); 32 | innerIteration--; 33 | if(innerIteration <= 0) 34 | { 35 | Debug.Log("Propagation is taking too long"); 36 | return new int[0][]; 37 | } 38 | } 39 | if (solver.CheckForConflicts()) 40 | { 41 | Debug.Log("\n Conflict occured. Iteration: " + iteration); 42 | iteration++; 43 | outputGrid.ResetAllPossibilities(); 44 | solver = new CoreSolver(this.outputGrid, this.patternManager); 45 | } 46 | else 47 | { 48 | Debug.Log("Solved on: " + iteration); 49 | this.outputGrid.PrintResultsToConsol(); 50 | break; 51 | } 52 | } 53 | if(iteration>= this.maxIterations) 54 | { 55 | Debug.Log("Coulnd solve the tilemap"); 56 | } 57 | return outputGrid.GetSolvedOutputGrid(); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /WFC_5_CoreSolver/WFC_5_CoreSolver.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyValleyStudio/WaveFunctionCollapseUnityTilemapTutorial/cc54ff3b648eeaf06601d48254fb1190c79a4dd6/WFC_5_CoreSolver/WFC_5_CoreSolver.unitypackage -------------------------------------------------------------------------------- /WFC_6_OutputGenerator/Editor/WfcInspector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | [CustomEditor(typeof(TEst))] 7 | public class WfcInspector : Editor 8 | { 9 | public override void OnInspectorGUI() 10 | { 11 | DrawDefaultInspector(); 12 | TEst myScript = (TEst)target; 13 | if(GUILayout.Button("Create tilemap")) 14 | { 15 | myScript.CreateWFC(); 16 | myScript.CreateTilemap(); 17 | } 18 | if(GUILayout.Button("Save tilmep")) 19 | { 20 | myScript.SaveTilemap(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /WFC_6_OutputGenerator/IOutputCreator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace WaveFunctionCollaps 6 | { 7 | public interface IOutputCreator 8 | { 9 | T OutputImage { get; } 10 | void CreateOutput(PatternManager manager, int[][] outputvalues, int width, int height); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /WFC_6_OutputGenerator/TileMapOutput.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.Tilemaps; 5 | 6 | namespace WaveFunctionCollaps 7 | { 8 | public class TileMapOutput : IOutputCreator 9 | { 10 | private Tilemap outputImage; 11 | private ValuesManager valueManager; 12 | public Tilemap OutputImage => outputImage; 13 | 14 | public TileMapOutput(ValuesManager valueManager, Tilemap outputImage) 15 | { 16 | this.outputImage = outputImage; 17 | this.valueManager = valueManager; 18 | } 19 | 20 | public void CreateOutput(PatternManager manager, int[][] outputvalues, int width, int height) 21 | { 22 | if(outputvalues.Length == 0) 23 | { 24 | return; 25 | } 26 | this.outputImage.ClearAllTiles(); 27 | 28 | int[][] valueGrid; 29 | valueGrid = manager.ConvertPatternToValues(outputvalues); 30 | 31 | for (int row = 0; row < height; row++) 32 | { 33 | for (int col = 0; col < width; col++) 34 | { 35 | TileBase tile = (TileBase)this.valueManager.GetValueFromIndex(valueGrid[row][col]).Value; 36 | this.outputImage.SetTile(new Vector3Int(col, row, 0), tile); 37 | } 38 | } 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /WFC_6_OutputGenerator/WFC_6_OutputGenerator.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyValleyStudio/WaveFunctionCollapseUnityTilemapTutorial/cc54ff3b648eeaf06601d48254fb1190c79a4dd6/WFC_6_OutputGenerator/WFC_6_OutputGenerator.unitypackage -------------------------------------------------------------------------------- /WFC_7_Bug_fixEntropyCalculation/CoreHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | 7 | namespace WaveFunctionCollaps 8 | { 9 | public class CoreHelper 10 | { 11 | float totalFrequency = 0; 12 | float totalFrequencyLog = 0; 13 | PatternManager patternManager; 14 | 15 | public CoreHelper(PatternManager manager) 16 | { 17 | patternManager = manager; 18 | 19 | //for (int i = 0; i < patternManager.GetNuberOfPatterns(); i++) 20 | //{ 21 | // totalFrequency += patternManager.GetPatternFrequency(i); 22 | //} 23 | //totalFrequencyLog = Mathf.Log(totalFrequency, 2); 24 | } 25 | 26 | public int SelectSolutionPatternFromFrequency(List possibleValues) 27 | { 28 | List valueFrequenciesFractions = GetListOfWeightsFromIndices(possibleValues); 29 | float randomValue = UnityEngine.Random.Range(0, valueFrequenciesFractions.Sum()); 30 | float sum = 0; 31 | int index = 0; 32 | foreach (var item in valueFrequenciesFractions) 33 | { 34 | sum += item; 35 | if(randomValue <= sum) 36 | { 37 | return index; 38 | } 39 | index++; 40 | } 41 | return index; 42 | } 43 | 44 | 45 | 46 | private List GetListOfWeightsFromIndices(List possibleValues) 47 | { 48 | var valueFrequencies = possibleValues.Aggregate(new List(), (acc, val) => 49 | { 50 | acc.Add(patternManager.GetPatternFrequency(val)); 51 | return acc; 52 | }, 53 | acc => acc).ToList(); 54 | return valueFrequencies; 55 | 56 | } 57 | 58 | public List Create4DirectionNeighbours(Vector2Int cellCoordinates, Vector2Int previousCell) 59 | { 60 | List list = new List() 61 | { 62 | new VectorPair(cellCoordinates, cellCoordinates+new Vector2Int(1,0), Direction.Right,previousCell), 63 | new VectorPair(cellCoordinates, cellCoordinates+new Vector2Int(-1,0), Direction.Left,previousCell), 64 | new VectorPair(cellCoordinates, cellCoordinates+new Vector2Int(0,1), Direction.Up,previousCell), 65 | new VectorPair(cellCoordinates, cellCoordinates+new Vector2Int(0,-1), Direction.Down,previousCell), 66 | }; 67 | return list; 68 | } 69 | 70 | public List Create4DirectionNeighbours(Vector2Int cellCoordinate) 71 | { 72 | return Create4DirectionNeighbours(cellCoordinate, cellCoordinate); 73 | } 74 | 75 | public float CalculateEntropy(Vector2Int position, OutputGrid outputGrid) 76 | { 77 | float sum = 0; 78 | foreach (var possibleIndex in outputGrid.GetPossibleValueForPossition(position)) 79 | { 80 | totalFrequency += patternManager.GetPatternFrequency(possibleIndex); 81 | sum += patternManager.GetPatternFrequencyLog2(possibleIndex); 82 | } 83 | totalFrequencyLog = Mathf.Log(totalFrequency, 2); 84 | return totalFrequencyLog - (sum / totalFrequency); 85 | } 86 | 87 | public List CheckIfNeighboursAreCollapsed(VectorPair pairToCheck, OutputGrid outputgrid) 88 | { 89 | return Create4DirectionNeighbours(pairToCheck.CellToPropagatePosition, pairToCheck.BaseCellPosition) 90 | .Where(x => outputgrid.CheckIfValidPosition(x.CellToPropagatePosition) && outputgrid.CheckIfCellIsCollapsed(x.CellToPropagatePosition) == false) 91 | .ToList(); 92 | } 93 | 94 | public bool CheckCellSolutionForCollision(Vector2Int cellCoordinates, OutputGrid outputGrid) 95 | { 96 | foreach (var neighbour in Create4DirectionNeighbours(cellCoordinates)) 97 | { 98 | if (outputGrid.CheckIfValidPosition(neighbour.CellToPropagatePosition) == false) 99 | { 100 | continue; 101 | } 102 | HashSet possibleIndices = new HashSet(); 103 | foreach (var patternIndexAtNeighbour in outputGrid.GetPossibleValueForPossition(neighbour.CellToPropagatePosition)) 104 | { 105 | var possibleNeighboursForBase = patternManager.GetPossibleNeighboursForPatternInDirection(patternIndexAtNeighbour, neighbour.DirectionFromBase.GetOppositeDirectionTo()); 106 | possibleIndices.UnionWith(possibleNeighboursForBase); 107 | } 108 | if (possibleIndices.Contains(outputGrid.GetPossibleValueForPossition(cellCoordinates).First()) == false) 109 | { 110 | return true; 111 | } 112 | } 113 | 114 | return false; 115 | } 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /WFC_8_Final_Version.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyValleyStudio/WaveFunctionCollapseUnityTilemapTutorial/cc54ff3b648eeaf06601d48254fb1190c79a4dd6/WFC_8_Final_Version.unitypackage --------------------------------------------------------------------------------