示例#1
0
        /// <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);
        }
示例#2
0
        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);
            }
        }
示例#3
0
        /// <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);
        }
示例#4
0
        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;
            }
        }
示例#5
0
        /// <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);
        }
示例#9
0
        /// <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);
        }