/// <summary> /// Clears the current dungeon and Destroy all pieces. A new dungeon cannot be generated /// until after the next physics update clears colliders for these destroyed objects /// </summary> public void Clear() { CurrentDungeonBounds = new Bounds(transform.position, Vector3.zero); for (var i = 0; i < CurrentDungeonPartInstances.Count; i++) { Destroy(CurrentDungeonPartInstances[i].gameObject); } CurrentDungeonPartInstances.Clear(); Generating = false; currentPhaseStatus = null; }
/// <summary> /// Triggers generation of a new dungeon. If synchronous is true, the generation will be /// completed synchronously. If false, generation will need to be advanced by repeated /// calls to StepGeneration (until StepGeneration returns false) /// </summary> public void Generate(bool synchronous = true) { if (StartParts.Count == 0) { Debug.LogError("Generation failed: no Start Parts configured!"); return; } else if (Phases.Length == 0) { Debug.LogError("Generation failed: no Phases configured!"); return; } Generating = true; if (UseRandomSeed) { Seed = Random.Range(int.MinValue, int.MaxValue); } var oldRandomState = Random.state; Random.InitState(Seed); DungeonPart startPart = StartParts.RandomElement(); var startPartInstance = Instantiate(startPart.gameObject, transform).GetComponent <DungeonPart>(); CurrentDungeonPartInstances.Add(startPartInstance); currentPhaseStatus = new DungeonGenerationPhaseStatus(Phases[0]); currentPhaseStatus.OpenOutbound.AddRange(startPartInstance.OutboundConnectors()); randomState = Random.state; Random.state = oldRandomState; if (synchronous) { while (Generating) { StepGeneration(); } } }
/// <summary> /// Attempts to attach the specified part to the specified outbound connector, performing bounds /// checks as appropriate. Will attempt placement with all valid matching inbound connectors /// Returns true if the part was placed successfully, false if not /// </summary> private bool TryPlacePart(DungeonPart partPrefab, DungeonConnector outboundConnector) { Quaternion targetOrientation = new Quaternion(); Vector3 targetPosition = Vector3.zero; int? inboundConnectorId = null; var prefabICs = partPrefab.InboundConnectorsFor(outboundConnector); prefabICs.Shuffle(); foreach (var prefabIC in prefabICs) { // calculate prospective orientation and position for part instance targetOrientation = Quaternion.AngleAxis(180, outboundConnector.transform.up) * outboundConnector.transform.rotation * Quaternion.Inverse(prefabIC.transform.localRotation); targetPosition = outboundConnector.transform.position - targetOrientation * prefabIC.transform.localPosition; bool canPlace = partPrefab.SkipBoundsCheck || partPrefab.BoundsCheck(outboundConnector.transform, prefabIC.ConnectorId); if (canPlace) { inboundConnectorId = prefabIC.ConnectorId; break; } else { //Debug.Log($"Bounds check failed! Can't connect {outboundConnector.gameObject.name} (outbound) to {partPrefab.PartName} {prefabIC.gameObject.name} (inbound)"); } } if (!inboundConnectorId.HasValue) { return(false); } var partInstance = Instantiate(partPrefab.gameObject, transform).GetComponent <DungeonPart>(); CurrentDungeonPartInstances.Add(partInstance); var inboundConnector = partInstance.GetConnector(inboundConnectorId.Value); // random rotation added separately from bounds check so that bounds check is deterministic if (inboundConnector.RandomRotation || outboundConnector.RandomRotation) { float rotateDegrees = Random.Range(0, 360); targetOrientation = Quaternion.AngleAxis(rotateDegrees, outboundConnector.transform.forward) * Quaternion.AngleAxis(180, outboundConnector.transform.up) * outboundConnector.transform.rotation * Quaternion.Inverse(inboundConnector.transform.localRotation); targetPosition = outboundConnector.transform.position - targetOrientation * inboundConnector.transform.localPosition; } partInstance.transform.localRotation = targetOrientation; partInstance.transform.position = targetPosition; CurrentDungeonBounds.Encapsulate(targetPosition); //Debug.Log($"Connected {outboundConnector.gameObject.name} (outbound) to {partPrefab.PartName} {inboundConnector.gameObject.name} (inbound)"); var nextOutboundConnectors = partInstance.OutboundConnectors(); nextOutboundConnectors.Remove(inboundConnector); currentPhaseStatus.OpenOutbound.Remove(outboundConnector); // check whether this part makes additional connections to other open connectors for (var i = nextOutboundConnectors.Count - 1; i > 0; i--) { var connA = nextOutboundConnectors[i]; for (var j = currentPhaseStatus.OpenOutbound.Count - 1; j > 0; j--) { var connB = currentPhaseStatus.OpenOutbound[j]; if (connA.CanConnectTo(connB)) { if ((connA.transform.position - connB.transform.position).sqrMagnitude < 0.01f) { nextOutboundConnectors.RemoveAt(i); currentPhaseStatus.OpenOutbound.RemoveAt(j); //Debug.Log($"Detected additional connection at {connA.transform.position} between {connA.gameObject.name} and {connB.gameObject.name}, closing both."); } } } } currentPhaseStatus.OpenOutbound.AddRange(nextOutboundConnectors); return(true); }