Beispiel #1
0
        void TraverseTree(BSPNodeObject node, System.Action <BSPNodeObject> visit)
        {
            // traverse the children
            foreach (var child in node.children)
            {
                TraverseTree(child, visit);
            }

            visit(node);
        }
Beispiel #2
0
        /// <summary>
        /// This function assumes that the cell is big enough to split.
        /// Make sure to call CanSplit function before calling this
        /// </summary>
        public void Split(float splitRatio, System.Random random)
        {
            if (bounds.Width == bounds.Length)
            {
                horizontalSplit = random.NextFloat() < 0.5f;
            }
            else
            {
                horizontalSplit = (bounds.Width > bounds.Length);
            }

            int totalSize = horizontalSplit ? bounds.Width : bounds.Length;

            int left  = Mathf.RoundToInt(totalSize * splitRatio);
            int right = totalSize - left;

            var child0 = new BSPNodeObject();

            child0.parent        = this;
            child0.padding       = padding;
            child0.depthFromRoot = depthFromRoot + 1;

            var child1 = new BSPNodeObject();

            child1.parent        = this;
            child1.padding       = padding;
            child1.depthFromRoot = depthFromRoot + 1;

            var loc0  = bounds.Location;
            var size0 = bounds.Size;

            var loc1  = bounds.Location;
            var size1 = bounds.Size;

            if (horizontalSplit)
            {
                size0.x = left;

                loc1.x += left;
                size1.x = right;
            }
            else
            {
                size0.z = left;

                loc1.z += left;
                size1.z = right;
            }

            child0.bounds = new Rectangle(loc0, size0);
            child1.bounds = new Rectangle(loc1, size1);

            children = new BSPNodeObject[] { child0, child1 };
        }
Beispiel #3
0
        void TraverseParentBranch(BSPNodeObject node, System.Action <BSPNodeObject> visit)
        {
            if (node == null)
            {
                return;
            }

            visit(node);

            TraverseParentBranch(node.parent, visit);
        }
Beispiel #4
0
        BSPNodeObject GetCornerSubtreeNode(BSPNodeObject node, bool left)
        {
            if (node.children == null || node.children.Length == 0)
            {
                return(node);
            }

            var child = left ? node.children[0] : node.children[1];

            return(GetCornerSubtreeNode(child, left));
        }
Beispiel #5
0
        void DebugRoomLayout(BSPNodeObject rootNode)
        {
            var edgeRooms = new List <BSPNodeObject>();

            FindBoundaryEdgeRooms(rootNode.children[1], BSPNodeDirection.Left, edgeRooms);

            foreach (var room in edgeRooms)
            {
                room.debugColor = Color.red;
            }
        }
Beispiel #6
0
        void BuildDungeonGraph(BSPNodeObject node)
        {
            int targetMinRoomSize = bspConfig.minRoomSize + bspConfig.roomPadding * 2;
            int targetMaxRoomSize = bspConfig.maxRoomSize + bspConfig.roomPadding * 2;

            if (!node.CanSplit(targetMinRoomSize))
            {
                // Node is too small to split further
                return;
            }

            bool shouldSplit;

            if (node.MustSplit(targetMaxRoomSize))
            {
                shouldSplit = true;
            }
            else
            {
                // Check if the aspect ratio would be correct after a split

                // Use a probability to decide if we split
                shouldSplit = random.NextFloat() < bspConfig.smallerRoomProbability;
            }

            if (shouldSplit)
            {
                float splitRatio  = 0.5f;
                bool  unevenSplit = random.NextFloat() < bspConfig.unevenSplitProbability;
                if (unevenSplit)
                {
                    int sizeToSplit = Mathf.Max(node.bounds.Width, node.bounds.Length);

                    int allowedSplitDistance = sizeToSplit - 2 * targetMinRoomSize;
                    if (allowedSplitDistance > 0)
                    {
                        float allowedSplitRatio = allowedSplitDistance / (float)sizeToSplit;
                        var   randomValue       = random.NextFloat(); // get a random value between 0..1
                        randomValue = randomValue * 2 - 1;            // transform to -1..1

                        // From 0.5, we are going to move the split either to the left or right by half of the allowed ratio (-1..1 * halfAllowedSplitRatio)
                        splitRatio = 0.5f + randomValue * allowedSplitRatio / 2.0f;
                    }
                }
                node.Split(splitRatio, random);
            }

            // Process the children
            foreach (var child in node.children)
            {
                BuildDungeonGraph(child);
            }
        }
Beispiel #7
0
        void SerializeGraph(BSPNodeObject rootNode)
        {
            var serializedNodes       = new List <BSPNode>();
            var serializedConnections = new List <BSPNodeConnection>();

            SerializeGraph(rootNode, serializedNodes, serializedConnections);

            bspModel.nodes       = serializedNodes.ToArray();
            bspModel.connections = serializedConnections.ToArray();

            bspModel.rootNode = rootNode.id;
        }
Beispiel #8
0
        void FindBoundaryEdgeRooms(BSPNodeObject node, BSPNodeDirection direction, List <BSPNodeObject> result)
        {
            if (node.discarded)
            {
                return;
            }

            bool hasChildren = (node.children != null && node.children.Length > 0);

            if (!hasChildren)
            {
                result.Add(node);
                return;
            }

            if (node.horizontalSplit)
            {
                if (direction == BSPNodeDirection.Left)
                {
                    FindBoundaryEdgeRooms(node.children[0], direction, result);
                }
                else if (direction == BSPNodeDirection.Right)
                {
                    FindBoundaryEdgeRooms(node.children[1], direction, result);
                }
                else
                {
                    FindBoundaryEdgeRooms(node.children[0], direction, result);
                    FindBoundaryEdgeRooms(node.children[1], direction, result);
                }
            }
            else              // Vertical split
            {
                if (direction == BSPNodeDirection.Bottom)
                {
                    FindBoundaryEdgeRooms(node.children[0], direction, result);
                }
                else if (direction == BSPNodeDirection.Top)
                {
                    FindBoundaryEdgeRooms(node.children[1], direction, result);
                }
                else
                {
                    FindBoundaryEdgeRooms(node.children[0], direction, result);
                    FindBoundaryEdgeRooms(node.children[1], direction, result);
                }
            }
        }
Beispiel #9
0
        void FlagConnectedLeafNodes(BSPNodeObject node)
        {
            if (node.depthFromRoot >= bspConfig.randomKillDepthStart)
            {
                return;
            }

            foreach (var connection in node.subtreeLeafConnections)
            {
                TraverseParentBranch(connection.Room0, n => n.discarded = false);
                TraverseParentBranch(connection.Room1, n => n.discarded = false);
            }

            foreach (var child in node.children)
            {
                FlagConnectedLeafNodes(child);
            }
        }
Beispiel #10
0
        void DiscardExtraRooms(BSPNodeObject rootNode)
        {
            TraverseTree(rootNode, n => n.discarded = true);

            FlagConnectedLeafNodes(rootNode);

            int numNodes = 0;

            TraverseTree(rootNode, n => numNodes++);

            int maxTries = numNodes;
            int numTries = 0;

            while (ConnectActiveSubtrees(rootNode) && numTries <= maxTries)
            {
                numTries++;
            }
        }
Beispiel #11
0
        void ConnectDoors(BSPNodeObject node)
        {
            if (node.discarded || node.children == null)
            {
                return;
            }

            // Connect the children
            foreach (var child in node.children)
            {
                ConnectDoors(child);
            }

            // Connect the siblings
            if (node.children.Length == 2)
            {
                node.subtreeLeafConnections = ConnectPartitions(node.children [0], node.children [1], node.horizontalSplit);
            }
        }
Beispiel #12
0
        void GenerateLevelLayout()
        {
            var rootNode = new BSPNodeObject();

            rootNode.bounds        = new Rectangle(0, 0, bspConfig.dungeonWidth, bspConfig.dungeonLength);
            rootNode.padding       = bspConfig.roomPadding;
            rootNode.depthFromRoot = 0;

            BuildDungeonGraph(rootNode);

            ConnectDoors(rootNode);


            GenerateCustomRooms(rootNode);

            DiscardExtraRooms(rootNode);

            SerializeGraph(rootNode);
        }
Beispiel #13
0
        bool ConnectActiveSubtrees(BSPNodeObject node)
        {
            bool stateModified = false;

            foreach (var child in node.children)
            {
                stateModified |= ConnectActiveSubtrees(child);
            }

            if (node.discarded)
            {
                return(stateModified);
            }

            bool bothChildrenActive = (node.children.Length == 2 && !node.children[0].discarded && !node.children[1].discarded);

            if (bothChildrenActive)
            {
                foreach (var connection in node.subtreeLeafConnections)
                {
                    //connection.Room0.discarded = false;
                    //connection.Room1.discarded = false;
                    TraverseParentBranch(connection.Room0, n => {
                        if (n.discarded)
                        {
                            n.discarded   = false;
                            stateModified = true;
                        }
                    });
                    TraverseParentBranch(connection.Room1, n => {
                        if (n.discarded)
                        {
                            n.discarded   = false;
                            stateModified = true;
                        }
                    });
                }
            }

            return(stateModified);
        }
Beispiel #14
0
        public NodeConnection(BSPNodeObject room0, BSPNodeObject room1, int padding)
        {
            this.room0 = room0;
            this.room1 = room1;

            var intersection = Rectangle.Intersect(room0.bounds, room1.bounds);
            var center       = intersection.Center();

            if (intersection.Size.x > 0)
            {
                doorPosition0 = center + new IntVector(0, 0, padding);
                doorPosition1 = center - new IntVector(0, 0, padding);
                doorFacingX   = false;
            }
            else
            {
                doorPosition0 = center + new IntVector(padding, 0, 0);
                doorPosition1 = center - new IntVector(padding, 0, 0);
                doorFacingX   = true;
            }
        }
Beispiel #15
0
        void SerializeGraph(BSPNodeObject node, List <BSPNode> serializedNodes, List <BSPNodeConnection> serializedConnections)
        {
            if (node == null)
            {
                return;
            }

            var serializedNode = new BSPNode();

            serializedNode.id            = node.id;
            serializedNode.bounds        = node.bounds;
            serializedNode.paddedBounds  = node.PaddedBounds;
            serializedNode.depthFromRoot = node.depthFromRoot;
            serializedNode.debugColor    = node.debugColor;
            serializedNode.discarded     = node.discarded;

            if (node.parent != null)
            {
                serializedNode.parent = node.parent.id;
            }

            var childIds = new List <System.Guid>();

            foreach (var child in node.children)
            {
                if (child != null)
                {
                    childIds.Add(child.id);
                }
            }
            serializedNode.children = childIds.ToArray();

            var connectedIds = new List <System.Guid>();

            foreach (var connectedRoom in node.connectedRooms)
            {
                connectedIds.Add(connectedRoom.id);
            }
            serializedNode.connectedRooms = connectedIds.ToArray();

            var subtreeLeafConnections = new List <BSPNodeConnection>();

            foreach (var connection in node.subtreeLeafConnections)
            {
                var serializedConnection = new BSPNodeConnection();
                serializedConnection.room0         = connection.Room0.id;
                serializedConnection.room1         = connection.Room1.id;
                serializedConnection.doorPosition0 = connection.DoorPosition0;
                serializedConnection.doorPosition1 = connection.DoorPosition1;
                serializedConnection.doorFacingX   = connection.DoorFacingX;
                subtreeLeafConnections.Add(serializedConnection);

                if (!connection.Room0.discarded && !connection.Room1.discarded)
                {
                    serializedConnections.Add(serializedConnection);
                }
            }
            serializedNode.subtreeLeafConnections = subtreeLeafConnections.ToArray();

            serializedNodes.Add(serializedNode);

            // Serialize the children
            foreach (var child in node.children)
            {
                SerializeGraph(child, serializedNodes, serializedConnections);
            }
        }
Beispiel #16
0
 void DiscardSubtree(BSPNodeObject node)
 {
     TraverseTree(node, n => n.discarded = true);
 }
Beispiel #17
0
 void GenerateCustomRooms(BSPNodeObject rootNode)
 {
 }
Beispiel #18
0
        NodeConnection[] ConnectPartitions(BSPNodeObject leftPartition, BSPNodeObject rightPartition, bool horizontalSplit)
        {
            var connections = new List <NodeConnection>();

            if (leftPartition.discarded || rightPartition.discarded)
            {
                return(connections.ToArray());
            }

            var leftRooms  = new List <BSPNodeObject>();
            var rightRooms = new List <BSPNodeObject>();

            if (horizontalSplit)
            {
                FindBoundaryEdgeRooms(leftPartition, BSPNodeDirection.Right, leftRooms);
                FindBoundaryEdgeRooms(rightPartition, BSPNodeDirection.Left, rightRooms);
            }
            else               // Vertical split
            {
                // Left = bottom partition
                // Right = top partition
                FindBoundaryEdgeRooms(leftPartition, BSPNodeDirection.Top, leftRooms);
                FindBoundaryEdgeRooms(rightPartition, BSPNodeDirection.Bottom, rightRooms);
            }

            // Connect the two rooms together
            if (leftRooms.Count == 0 || rightRooms.Count == 0)
            {
                return(connections.ToArray());
            }

            Shuffle(leftRooms);
            Shuffle(rightRooms);

            bool roomsConnected = false;

            foreach (var leftRoom in leftRooms)
            {
                // First check if any of the right rooms are connected
                foreach (var rightRoom in rightRooms)
                {
                    if (leftRoom.connectedRooms.Contains(rightRoom))
                    {
                        roomsConnected = true;
                        break;
                    }
                }

                foreach (var rightRoom in rightRooms)
                {
                    if (leftRoom.connectedRooms.Contains(rightRoom))
                    {
                        // Already connected
                        continue;
                    }

                    bool shouldConnectRooms = true;
                    if (roomsConnected)
                    {
                        // rooms are already connected along this edge.  Check if can loop
                        shouldConnectRooms = random.NextFloat() < bspConfig.loopingProbability;
                    }

                    if (shouldConnectRooms)
                    {
                        // Connect left and right together
                        var intersection    = Rectangle.Intersect(leftRoom.bounds, rightRoom.bounds);
                        var minIntersection = bspConfig.roomPadding * 2;
                        if (intersection.Size.x > minIntersection || intersection.Size.z > minIntersection)
                        {
                            // These two rooms can connect
                            leftRoom.connectedRooms.Add(rightRoom);
                            rightRoom.connectedRooms.Add(leftRoom);
                            roomsConnected = true;

                            var connection = new NodeConnection(leftRoom, rightRoom, bspConfig.roomPadding);
                            connections.Add(connection);
                        }
                    }
                }
            }

            return(connections.ToArray());
        }