/// <summary> /// Create split from the source and the split up rectangles, spreading the neighbours /// of the split over its children /// </summary> /// <param name="source"></param> /// <param name="rectangles"></param> /// <returns></returns> public SplitRect[] CreateSplits(SplitRect source, RectInt[] rectangles) { var result = new SplitRect[rectangles.Length]; for (var i = 0; i < rectangles.Length; ++i) { result[i] = new SplitRect() { _rect = rectangles[i], Neighbours = new HashSet <SplitRect>(source.Neighbours.Where( (neighbour) => !RectUtil.AreDisconnected(rectangles[i], neighbour._rect))) }; foreach (var neighbour in result[i].Neighbours) { neighbour.Neighbours.Add(result[i]); } } for (var i = 0; i < result.Length; ++i) { for (var j = i + 1; j < result.Length; ++j) { result[i].Neighbours.Add(result[j]); result[j].Neighbours.Add(result[i]); } } return(result); }
private void SubdivideSplitRect(SplitRect split, List <SplitRect> result, int depth, Axis divisionAxis) { var subdivisionRectangles = SplitRectRandomly(split, divisionAxis); var splitRects = CreateSplits(split, subdivisionRectangles); split.DisconnectFromNeighbours(); var nextAxis = divisionAxis == Axis.X ? Axis.Y : Axis.X; for (int i = 0; i < splitRects.Length; ++i) { TrySubdivideSplitRect(splitRects[i], result, depth + 1, nextAxis); } }
/// <summary> /// Subdivides the given root node in binary pieces according to the settings in this class /// </summary> /// <param name="root"></param> /// <returns></returns> public List <SplitRect> Subdivide(SplitRect root) { var initiailRandomState = UnityEngine.Random.state; if (_seed >= 0) { UnityEngine.Random.InitState(_seed); } List <SplitRect> result = new List <SplitRect>(); TrySubdivideSplitRect(root, result, 0, UnityEngine.Random.value > 0.5f ? Axis.Y : Axis.X); if (_seed >= 0) { UnityEngine.Random.state = initiailRandomState; } return(result); }
private void TrySubdivideSplitRect(SplitRect split, List <SplitRect> result, int depth, Axis divisionAxis) { var decision = ShouldStopSubdividing(split, depth, divisionAxis); switch (decision) { case ContinueSubdivisionDecision.ContinueSubdivision: SubdivideSplitRect(split, result, depth, divisionAxis); break; case ContinueSubdivisionDecision.AttemptOtherAxis: SubdivideSplitRect(split, result, depth, divisionAxis == Axis.X ? Axis.Y : Axis.X); break; default: split._reasonForSplitStoppedAtThisPoint = decision; result.Add(split); break; } }
/// <summary> /// Split the given rect along the given axis /// </summary> /// <param name="split"></param> /// <param name="divisionAxis"></param> /// <returns></returns> public RectInt[] SplitRectRandomly(SplitRect split, Axis divisionAxis) { var result = new RectInt[2]; if (divisionAxis == Axis.Y) { var leftWidth = UnityEngine.Random.Range(_minRectWidth, split._rect.width - _minRectWidth); result[0] = new RectInt(split._rect.position, new Vector2Int(leftWidth, split._rect.height)); result[1] = new RectInt(split._rect.position + Vector2Int.right * leftWidth, new Vector2Int(split._rect.width - leftWidth, split._rect.height)); } else if (divisionAxis == Axis.X) { var bottomHeight = UnityEngine.Random.Range(_minRectHeight, split._rect.height - _minRectHeight); result[0] = new RectInt(split._rect.position, new Vector2Int(split._rect.width, bottomHeight)); result[1] = new RectInt(split._rect.position + Vector2Int.up * bottomHeight, new Vector2Int(split._rect.width, split._rect.height - bottomHeight)); } return(result); }
/// <summary> /// Build a path with the given split as a root, returns the root node of the path /// </summary> /// <param name="split"></param> /// <param name="closedList"></param> /// <returns></returns> private TraversalNode BuildPath(SplitRect split, HashSet <SplitRect> closedList) { var root = CreateNode(null, split, closedList); var currentNode = root; for (int i = 0; CanContinueIteration(i, currentNode); ++i) { var childSplit = SelectRandomNeighbour(currentNode._split, closedList); if (childSplit == null) { // backtrack - the loop will break if there are no more children currentNode = currentNode._parent; } else { currentNode = CreateNode(currentNode, childSplit, closedList);; } } // did we find a solution if (currentNode == null) { // if not take the best alternative currentNode = _longestDistanceNode; } // traverse up again marking the primary path through the level while (currentNode != null && currentNode._parent != null) { currentNode._isPrimaryPath = true; currentNode = currentNode._parent; } return(root); }
private SplitRect SelectRandomNeighbour(SplitRect source, HashSet <SplitRect> closedList) { var neighbours = source.Neighbours.ToList(); var count = neighbours.Count; var startIndex = UnityEngine.Random.Range(0, count); for (int i = 0; i < neighbours.Count; ++i) { var subject = neighbours[(i + startIndex) % count]; if (!closedList.Contains(subject)) { var intersection = RectUtil.GetIntersection(source._rect, subject._rect); // need at least 3 units to build a door without artifacts (such as unreachable doors) if (Vector2Int.Distance(intersection[0], intersection[1]) >= 3) { return(subject); } } } return(null); }
private TraversalNode CreateNode(TraversalNode parent, SplitRect split, HashSet <SplitRect> closedList) { var result = new TraversalNode() { _split = split, _parent = parent, _children = new List <TraversalNode>(), _parentIntersection = parent == null ? null : RectUtil.GetIntersection(parent._split._rect, split._rect), _pathLength = parent == null ? 0 : parent._pathLength + Vector2.Distance(parent._split._rect.center, split._rect.center) }; if (_longestDistanceNode == null || result._pathLength > _longestDistanceNode._pathLength) { _longestDistanceNode = result; } closedList.Add(split); if (parent != null) { parent._children.Add(result); } return(result); }
/// <summary> /// Heuristic testing if the subdivision can continue /// </summary> /// <param name="split"></param> /// <param name="depth"></param> /// <param name="divisionAxis"></param> /// <returns></returns> public ContinueSubdivisionDecision ShouldStopSubdividing(SplitRect split, int depth, Axis divisionAxis) { // check we if can divide over the x axis if (divisionAxis == Axis.X && split._rect.height < _minRectHeight * 2) { // if we can still continue to divide over the y axis advice to do so, return((split._rect.width < _minRectWidth * 2) ? ContinueSubdivisionDecision.RectTooSmall : ContinueSubdivisionDecision.AttemptOtherAxis); } // check we if can divide over the y axis if (divisionAxis == Axis.Y && split._rect.width < _minRectWidth * 2) { // if we can still continue to divide over the x axis advice to do so, return((split._rect.height < _minRectHeight * 2) ? ContinueSubdivisionDecision.RectTooSmall : ContinueSubdivisionDecision.AttemptOtherAxis); } if (depth > _minDepth) { // check all conditons to stop dividing if (_maxDepth > 0 && depth >= _maxDepth) { return(ContinueSubdivisionDecision.MaxDepthReached); } // perform a random roll to see if we should continue return((UnityEngine.Random.value < _stopChanceBase + _stopChanceBase * depth) ? ContinueSubdivisionDecision.RandomRollFailed : ContinueSubdivisionDecision.ContinueSubdivision); } return(ContinueSubdivisionDecision.ContinueSubdivision); }