private static void DetectConnectivity(HashSet <Brick> onlyConnectTo, HashSet <Brick> ignoreForCollision = null) { Physics.SyncTransforms(); var ignored = onlyConnectTo; if (ignoreForCollision != null) { ignored = new HashSet <Brick>(ignored.Union(ignoreForCollision)); } foreach (var brick in onlyConnectTo) { var modelGroup = brick.GetComponentInParent <ModelGroup>(); var fields = brick.GetComponentsInChildren <ConnectionField>(); foreach (var field in fields) { var query = field.QueryConnections(onlyConnectTo); foreach (var(connection, otherConnection) in query) { if (ConnectionField.IsConnectionValid(connection, otherConnection, connection.transform.position, ignored)) { var connections = ConnectionField.Connect(connection, otherConnection, connection.transform.position, onlyConnectTo, ignored); foreach (var c in connections) { var model = c.connectivity.part.brick.GetComponentInParent <Model>(); } break; } } } } }
/// <summary> /// Compute the overlap between two fields in their current transformations /// </summary> /// <param name="f1">The first field</param> /// <param name="f2">The second field</param> /// <param name="min">Out parameter for the minimum position of the overlap</param> /// <param name="max">Out parameter for the maximum position of the overlap</param> /// <returns></returns> public static bool GetOverlap(ConnectionField f1, ConnectionField f2, out Vector2Int min, out Vector2Int max) { var f1Size = new Vector3(f1.gridSize.x, 0.0f, f1.gridSize.y) * BrickBuildingUtility.LU_5; var f2Size = new Vector3(f2.gridSize.x, 0.0f, f2.gridSize.y) * BrickBuildingUtility.LU_5; var f1_1 = new Vector3(0.0f, 0.0f, 0.0f); var f1_2 = new Vector3(-f1Size.x, 0.0f, f1Size.z); var f1_3 = new Vector3(-f1Size.x, 0.0f, 0.0f); var f1_4 = new Vector3(0.0f, 0.0f, f1Size.z); var f2_1 = new Vector3(0.0f, 0.0f, 0.0f); var f2_2 = new Vector3(-f2Size.x, 0.0f, f2Size.z); var f2_3 = new Vector3(-f2Size.x, 0.0f, 0.0f); var f2_4 = new Vector3(0.0f, 0.0f, f2Size.z); var s1 = f1.transform.InverseTransformPoint(f2.transform.TransformPoint(f2_1)); var s2 = f1.transform.InverseTransformPoint(f2.transform.TransformPoint(f2_2)); var s3 = f1.transform.InverseTransformPoint(f2.transform.TransformPoint(f2_3)); var s4 = f1.transform.InverseTransformPoint(f2.transform.TransformPoint(f2_4)); var sMinX = Mathf.Min(s1.x, Mathf.Min(s2.x, Mathf.Min(s3.x, s4.x))); var sMinZ = Mathf.Min(s1.z, Mathf.Min(s2.z, Mathf.Min(s3.z, s4.z))); var sMaxX = Mathf.Max(s1.x, Mathf.Max(s2.x, Mathf.Max(s3.x, s4.x))); var sMaxZ = Mathf.Max(s1.z, Mathf.Max(s2.z, Mathf.Max(s3.z, s4.z))); var fMinX = Mathf.Min(f1_1.x, Mathf.Min(f1_2.x, Mathf.Min(f1_3.x, f1_4.x))); var fMinZ = Mathf.Min(f1_1.z, Mathf.Min(f1_2.z, Mathf.Min(f1_3.z, f1_4.z))); var fMaxX = Mathf.Max(f1_1.x, Mathf.Max(f1_2.x, Mathf.Max(f1_3.x, f1_4.x))); var fMaxZ = Mathf.Max(f1_1.z, Mathf.Max(f1_2.z, Mathf.Max(f1_3.z, f1_4.z))); if (sMinX > fMaxX || fMinX > sMaxX || sMinZ > fMaxZ || fMinZ > sMaxZ) { min = Vector2Int.zero; max = Vector2Int.zero; return(false); } var minX = Mathf.RoundToInt(Mathf.Max(sMinX, fMinX) / BrickBuildingUtility.LU_5); var maxX = Mathf.RoundToInt(Mathf.Min(sMaxX, fMaxX) / BrickBuildingUtility.LU_5); var minZ = Mathf.RoundToInt(Mathf.Max(sMinZ, fMinZ) / BrickBuildingUtility.LU_5); var maxZ = Mathf.RoundToInt(Mathf.Min(sMaxZ, fMaxZ) / BrickBuildingUtility.LU_5); min = new Vector2Int(minX, minZ); max = new Vector2Int(maxX, maxZ); return(true); }
/// <summary> /// Query the possible connections for this field /// </summary> /// <param name="onlyConnectTo">An optional filter field if you only want to check connections on a specific field</param> /// <returns>A list of tuples for the possible connections</returns> public HashSet <(Connection, Connection)> QueryConnections(ConnectionField onlyConnectTo = null) { HashSet <(Connection, Connection)> validConnections = new HashSet <(Connection, Connection)>(); foreach (var connection in connections) { var querySuccess = QueryConnection(connection.transform.position, connection.connectionType); foreach (var con in querySuccess) { if (onlyConnectTo == null || onlyConnectTo == con.field) { validConnections.Add((connection, con)); } } } return(validConnections); }
/// <summary> /// Check if any connections on a field match with any on another field /// </summary> /// <param name="f1">The first field</param> /// <param name="f2">The field to check against</param> /// <returns></returns> public static bool MatchTypes(ConnectionField f1, ConnectionField f2) { if (f1 == null || f2 == null) { return(false); } foreach (var c1 in f1.connections) { foreach (var c2 in f2.connections) { if (Connection.MatchTypes(c1.connectionType, c2.connectionType)) { return(true); } } } return(false); }
public static bool UnpackConnectivityForPart(string designID, bool forceUpdate = false) { if (File.Exists(Path.Combine(connectivityPath, designID + ".prefab")) && !forceUpdate) { return(true); } OpenDB(); var partConnectivityPath = "CollisionBox_Connectivity_Info/" + designID + ".xml"; var entry = newPartsZipArchive.GetEntry(partConnectivityPath); if (entry != null) { var zipStream = entry.Open(); var doc = new XmlDocument(); doc.Load(zipStream); var primitiveNode = doc.SelectSingleNode("LEGOPrimitive"); var connectivityNode = primitiveNode.SelectSingleNode("Connectivity"); if (connectivityNode != null) { var connectionFields = connectivityNode.SelectNodes("Custom2DField"); var connectivityGO = new GameObject("Connectivity"); var connectivityComponent = connectivityGO.AddComponent <Connectivity>(); var bounding = primitiveNode.SelectSingleNode("Bounding"); var aabb = bounding.SelectSingleNode("AABB"); var extents = new Bounds { min = new Vector3 { x = -float.Parse(aabb.Attributes["minX"].Value, CultureInfo.InvariantCulture), y = float.Parse(aabb.Attributes["minY"].Value, CultureInfo.InvariantCulture), z = float.Parse(aabb.Attributes["minZ"].Value, CultureInfo.InvariantCulture) }, max = new Vector3 { x = -float.Parse(aabb.Attributes["maxX"].Value, CultureInfo.InvariantCulture), y = float.Parse(aabb.Attributes["maxY"].Value, CultureInfo.InvariantCulture), z = float.Parse(aabb.Attributes["maxZ"].Value, CultureInfo.InvariantCulture) } }; connectivityComponent.extents = extents; connectivityComponent.version = ConnectivityVersionChecker.currentVersion; foreach (XmlNode connectionField in connectionFields) { var value = connectionField.InnerText; var fieldGO = new GameObject("Custom2DField"); var fieldComponent = fieldGO.AddComponent <ConnectionField>(); fieldComponent.connectivity = connectivityComponent; fieldComponent.kind = int.Parse(connectionField.Attributes["type"].Value, CultureInfo.InvariantCulture) % 2 == 0 ? ConnectionField.FieldKind.receptor : ConnectionField.FieldKind.connector; fieldComponent.gridSize = new Vector2Int { x = int.Parse(connectionField.Attributes["width"].Value, CultureInfo.InvariantCulture), y = int.Parse(connectionField.Attributes["height"].Value, CultureInfo.InvariantCulture) }; fieldComponent.fieldType = ConnectionField.FieldType.custom2DField; var position = new Vector3 { x = -float.Parse(connectionField.Attributes["tx"].Value, CultureInfo.InvariantCulture), y = float.Parse(connectionField.Attributes["ty"].Value, CultureInfo.InvariantCulture), z = float.Parse(connectionField.Attributes["tz"].Value, CultureInfo.InvariantCulture) }; fieldComponent.transform.localPosition = position; var angle = float.Parse(connectionField.Attributes["angle"].Value, CultureInfo.InvariantCulture); var rotation = Quaternion.AngleAxis( -angle, new Vector3( -float.Parse(connectionField.Attributes["ax"].Value, CultureInfo.InvariantCulture), float.Parse(connectionField.Attributes["ay"].Value, CultureInfo.InvariantCulture), float.Parse(connectionField.Attributes["az"].Value, CultureInfo.InvariantCulture) ) ); fieldComponent.transform.localRotation = rotation; var connections = new List <Connection>(); var connectionDescriptions = value.Split(','); var connectionPositionX = 0.0f; var connectionPositionZ = 0.0f; var featuresPerRow = fieldComponent.gridSize.x + 1; var features = 0; var layer = LayerMask.NameToLayer(ConnectionField.GetLayer(fieldComponent.kind)); if (layer != -1) { fieldGO.layer = layer; } var collider = fieldGO.AddComponent <BoxCollider>(); collider.isTrigger = true; collider.size = new Vector3((fieldComponent.gridSize.x + 1) * BrickBuildingUtility.LU_5, 0.1f, (fieldComponent.gridSize.y + 1) * BrickBuildingUtility.LU_5); collider.center = new Vector3((collider.size.x - BrickBuildingUtility.LU_5) * -0.5f, 0.0f, (collider.size.z - BrickBuildingUtility.LU_5) * 0.5f); // Build the connection field grid foreach (var description in connectionDescriptions) { var descs = description.Split(':'); if (descs.Length > 3 || descs.Length < 2) { Debug.LogError("Connectivity description has wrong format -> " + designID); continue; } var connectionType = (Connection.ConnectionType) int.Parse(descs[0], CultureInfo.InvariantCulture); var connection = new Connection(); connection.quadrants = int.Parse(descs[1], CultureInfo.InvariantCulture); connection.connectionType = connectionType; connection.flags = descs.Length == 3 ? (Connection.Flags) int.Parse(descs[2], CultureInfo.InvariantCulture) : 0; connection.field = fieldComponent; connections.Add(connection); connection.index = connections.Count - 1; if (Connection.IsConnectableType(connection)) { fieldComponent.connectableConnections++; } var x = features % featuresPerRow; var y = features / featuresPerRow; connectionPositionX -= BrickBuildingUtility.LU_5; features++; if (features % featuresPerRow == 0) { connectionPositionZ += BrickBuildingUtility.LU_5; connectionPositionX = 0.0f; } } // Only add the field if it has any connections. if (connections.Count > 0) { connectivityComponent.connectionFields.Add(fieldComponent); fieldGO.transform.parent = connectivityGO.transform; fieldComponent.connections = connections.ToArray(); fieldComponent.connectedTo = new ConnectionField.ConnectionTuple[connections.Count]; } else { Object.DestroyImmediate(fieldGO); } } var fileName = designID + ".prefab"; var filePath = Path.Combine(connectivityPath, fileName); var directoryName = Path.GetDirectoryName(filePath); if (directoryName.Length > 0) { Directory.CreateDirectory(directoryName); } #if UNITY_EDITOR PrefabUtility.SaveAsPrefabAsset(connectivityGO, filePath); #endif Object.DestroyImmediate(connectivityGO); return(true); } } return(false); }
static (Connection, Connection) FindBestConnectionOnBrick(Vector3 pickupOffset, Brick brick, Ray ray, List <ConnectionField> selectedFields, HashSet <Brick> selectedBricks) { var physicsScene = brick.gameObject.scene.GetPhysicsScene(); var oldPositions = new List <Vector3>(); var oldRotations = new List <Quaternion>(); foreach (var selected in selectedBricks) { oldPositions.Add(selected.transform.position); oldRotations.Add(selected.transform.rotation); } foreach (var part in brick.parts) { if (part.connectivity == null) { continue; } // Only pick from matching feature types var validConnectionPairs = new List <(ConnectionField, ConnectionField)>(); foreach (var field in part.connectivity.connectionFields) { if (field.GetConnectedConnections().Count == field.connections.Count) { continue; } foreach (var selectedField in selectedFields) { if (!ConnectionField.MatchTypes(field, selectedField)) { continue; } validConnectionPairs.Add((field, selectedField)); } } if (validConnectionPairs.Count == 0) { continue; } // Sort by distance to ray origin and then by angle of the up angle in case the distance is the same validConnectionPairs = validConnectionPairs.OrderBy(f => { var field = f.Item1; var fieldSize = new Vector3(field.gridSize.x, 0.0f, field.gridSize.y) * LU_5 * 0.5f; var localCenter = new Vector3(-fieldSize.x, 0.0f, fieldSize.z); var fieldCenter = field.transform.TransformPoint(localCenter); return(Vector3.Distance(ray.origin, fieldCenter)); }).ThenBy(f => { return(Vector3.Angle(f.Item1.transform.up, f.Item2.transform.up)); }).ToList(); // Run through every possible connectionfield foreach (var(field, selectedField) in validConnectionPairs) { var oldPos = selectedField.transform.position; var oldRot = selectedField.transform.rotation; Quaternion newRot; if (!ConnectionField.AlignRotation(selectedField.transform, field.transform, out newRot)) { continue; } var selectedBrick = selectedField.connectivity.part.brick; var pivot = selectedBrick.transform.position + pickupOffset; newRot.ToAngleAxis(out float angle, out Vector3 axis); selectedField.transform.RotateAround(pivot, axis, angle); // Project the selected field onto the static field along the ray direction var selectedPos = selectedField.transform.position; var localRay = field.transform.InverseTransformDirection(ray.direction); var localOrigin = field.transform.InverseTransformPoint(ray.origin); var localSelectedPos = field.transform.InverseTransformPoint(selectedPos); var localProjectedT = (-Vector3.Dot(Vector3.up, localSelectedPos)) / Vector3.Dot(Vector3.up, localRay); var localNewPoint = localSelectedPos + localRay * localProjectedT; AlignToGrid(ref localNewPoint, Matrix4x4.identity); // Check neighbouring positions in the order defined by connectionFieldOffsets. // If we do not overlap with no offset, do not try the other offsets. for (var i = 0; i < connectionFieldOffsets.Length; i++) { var offset = connectionFieldOffsets[i]; var localToWorld = field.transform.TransformPoint(localNewPoint - Vector3.right * offset.x * LU_5 + Vector3.forward * offset.y * LU_5); selectedField.transform.position = localToWorld; var fieldSize = new Vector3(field.gridSize.x, 0.0f, field.gridSize.y) * LU_5; var selectedFieldSize = new Vector3(selectedField.gridSize.x, 0.0f, selectedField.gridSize.y) * LU_5; var f1 = new Vector3(0.0f, 0.0f, 0.0f); var f2 = new Vector3(-fieldSize.x, 0.0f, fieldSize.z); var f3 = new Vector3(-fieldSize.x, 0.0f, 0.0f); var f4 = new Vector3(0.0f, 0.0f, fieldSize.z); var p1 = new Vector3(0.0f, 0.0f, 0.0f); var p2 = new Vector3(-selectedFieldSize.x, 0.0f, selectedFieldSize.z); var p3 = new Vector3(-selectedFieldSize.x, 0.0f, 0.0f); var p4 = new Vector3(0.0f, 0.0f, selectedFieldSize.z); var s1 = field.transform.InverseTransformPoint(selectedField.transform.TransformPoint(p1)); var s2 = field.transform.InverseTransformPoint(selectedField.transform.TransformPoint(p2)); var s3 = field.transform.InverseTransformPoint(selectedField.transform.TransformPoint(p3)); var s4 = field.transform.InverseTransformPoint(selectedField.transform.TransformPoint(p4)); var sMinX = Mathf.Min(s1.x, Mathf.Min(s2.x, Mathf.Min(s3.x, s4.x))); var sMinZ = Mathf.Min(s1.z, Mathf.Min(s2.z, Mathf.Min(s3.z, s4.z))); var sMaxX = Mathf.Max(s1.x, Mathf.Max(s2.x, Mathf.Max(s3.x, s4.x))); var sMaxZ = Mathf.Max(s1.z, Mathf.Max(s2.z, Mathf.Max(s3.z, s4.z))); var fMinX = Mathf.Min(f1.x, Mathf.Min(f2.x, Mathf.Min(f3.x, f4.x))); var fMinZ = Mathf.Min(f1.z, Mathf.Min(f2.z, Mathf.Min(f3.z, f4.z))); var fMaxX = Mathf.Max(f1.x, Mathf.Max(f2.x, Mathf.Max(f3.x, f4.x))); var fMaxZ = Mathf.Max(f1.z, Mathf.Max(f2.z, Mathf.Max(f3.z, f4.z))); if (sMinX > fMaxX || fMinX > sMaxX || sMinZ > fMaxZ || fMinZ > sMaxZ) { selectedField.transform.position = oldPos; selectedField.transform.rotation = oldRot; // If we do not overlap with no offset, do not try the other offsets. if (i == 0) { break; } else { continue; } } // Overlapping rect var minX = Mathf.RoundToInt(Mathf.Max(sMinX, fMinX) / LU_5); var maxX = Mathf.RoundToInt(Mathf.Min(sMaxX, fMaxX) / LU_5); var minZ = Mathf.RoundToInt(Mathf.Max(sMinZ, fMinZ) / LU_5); var maxZ = Mathf.RoundToInt(Mathf.Min(sMaxZ, fMaxZ) / LU_5); var currentFieldPos = selectedField.transform.position; var currentFieldRot = selectedField.transform.rotation; // Iterate through overlap for (var x = minX; x < maxX + 1; x++) { for (var z = minZ; z < maxZ + 1; z++) { var localPos = new Vector3(x * LU_5, 0.0f, z * LU_5); var fieldConnection = field.GetConnectionAt(ConnectionField.ToGridPos(localPos)); if (fieldConnection != null && !fieldConnection.HasConnection()) { var connection = selectedField.GetConnectionAt(fieldConnection.transform.position); if (connection != null && !connection.HasConnection()) { if (!Connection.MatchTypes(fieldConnection.connectionType, connection.connectionType)) { continue; } selectedField.transform.position = oldPos; selectedField.transform.rotation = oldRot; // Find the rotation and position we need to offset the other selected bricks with ConnectionField.GetConnectedTransformation(connection, fieldConnection, pivot, out Vector3 connectedOffset, out angle, out axis); var oldBrickPos = selectedBrick.transform.position; var oldBrickRot = selectedBrick.transform.rotation; AlignTransformations(selectedBricks, pivot, axis, angle, connectedOffset); Physics.SyncTransforms(); var colliding = Colliding(selectedBricks, selectedBrick); selectedBrick.transform.position = oldBrickPos; selectedBrick.transform.rotation = oldBrickRot; Physics.SyncTransforms(); if (!colliding && ConnectionField.IsConnectionValid(connection, fieldConnection, pivot, selectedBricks)) { ResetTransformations(selectedBricks, oldPositions, oldRotations, selectedField.connectivity.part.brick); return(connection, fieldConnection); } selectedField.transform.position = currentFieldPos; selectedField.transform.rotation = currentFieldRot; ResetTransformations(selectedBricks, oldPositions, oldRotations, selectedField.connectivity.part.brick); } } } } } selectedField.transform.position = oldPos; selectedField.transform.rotation = oldRot; } } return(null, null); }
static (Connection, Connection) FindBestConnectionOnBrick(Vector3 pickupOffset, Brick brick, Ray ray, List <ConnectionField> selectedFields, HashSet <Brick> selectedBricks) { var physicsScene = brick.gameObject.scene.GetPhysicsScene(); var oldPositions = new List <Vector3>(); var oldRotations = new List <Quaternion>(); foreach (var selected in selectedBricks) { oldPositions.Add(selected.transform.position); oldRotations.Add(selected.transform.rotation); } foreach (var part in brick.parts) { if (part.connectivity == null) { continue; } // Only pick from matching feature types var validConnectionPairs = new List <(ConnectionField, ConnectionField)>(); foreach (var field in part.connectivity.connectionFields) { if (!field.HasAvailableConnections()) { continue; } foreach (var selectedField in selectedFields) { if (field.kind == selectedField.kind) { continue; } if (!ConnectionField.MatchTypes(field, selectedField)) { continue; } validConnectionPairs.Add((field, selectedField)); } } if (validConnectionPairs.Count == 0) { continue; } // Sort by distance to ray origin and then by angle of the up angle in case the distance is the same validConnectionPairs = validConnectionPairs.OrderBy(f => { var field = f.Item1; var fieldSize = new Vector3(field.gridSize.x, 0.0f, field.gridSize.y) * LU_5 * 0.5f; var localCenter = new Vector3(-fieldSize.x, 0.0f, fieldSize.z); var fieldCenter = field.transform.TransformPoint(localCenter); return(Vector3.Distance(ray.origin, fieldCenter)); }).ThenBy(f => { return(Vector3.Angle(f.Item1.transform.up, f.Item2.transform.up)); }).ToList(); // Run through every possible connectionfield foreach (var(field, selectedField) in validConnectionPairs) { var oldPos = selectedField.transform.position; var oldRot = selectedField.transform.rotation; Quaternion newRot; if (!ConnectionField.AlignRotation(selectedField.transform, field.transform, out newRot)) { continue; } var selectedBrick = selectedField.connectivity.part.brick; var pivot = selectedBrick.transform.position + pickupOffset; newRot.ToAngleAxis(out float angle, out Vector3 axis); selectedField.transform.RotateAround(pivot, axis, angle); // Project the selected field onto the static field along the ray direction var selectedPos = selectedField.transform.position; var localRay = field.transform.InverseTransformDirection(ray.direction); var localOrigin = field.transform.InverseTransformPoint(ray.origin); var localSelectedPos = field.transform.InverseTransformPoint(selectedPos); var localProjectedT = (-Vector3.Dot(Vector3.up, localSelectedPos)) / Vector3.Dot(Vector3.up, localRay); var localNewPoint = localSelectedPos + localRay * localProjectedT; AlignToGrid(ref localNewPoint, Matrix4x4.identity); // Check neighbouring positions in the order defined by connectionFieldOffsets. // If we do not overlap with no offset, do not try the other offsets. for (var i = 0; i < connectionFieldOffsets.Length; i++) { var offset = connectionFieldOffsets[i]; var localToWorld = field.transform.TransformPoint(localNewPoint - Vector3.right * offset.x * LU_5 + Vector3.forward * offset.y * LU_5); selectedField.transform.position = localToWorld; if (!ConnectionField.GetOverlap(field, selectedField, out Vector2Int min, out Vector2Int max)) { selectedField.transform.position = oldPos; selectedField.transform.rotation = oldRot; // If we do not overlap with no offset, do not try the other offsets. if (i == 0) { break; } else { continue; } } var currentFieldPos = selectedField.transform.position; var currentFieldRot = selectedField.transform.rotation; var reject = false; List <(Vector2, Connection, Connection)> matches = new List <(Vector2, Connection, Connection)>(); // Check for rejections on the overlap for (var x = min.x; x < max.x + 1; x++) { if (reject) { break; } for (var z = min.y; z < max.y + 1; z++) { if (reject) { break; } var localPos = new Vector3(x * LU_5, 0.0f, z * LU_5); var fieldConnection = field.GetConnectionAt(ConnectionField.ToGridPos(localPos)); if (fieldConnection != null && !field.HasConnection(fieldConnection)) { var fieldConnectionPosition = fieldConnection.field.GetPosition(fieldConnection); var connection = selectedField.GetConnectionAt(fieldConnectionPosition); if (connection != null && !selectedField.HasConnection(connection)) { var pairMatch = Connection.MatchTypes(fieldConnection.connectionType, connection.connectionType); if (pairMatch == Connection.ConnectionMatch.reject) { reject = true; break; } else if (pairMatch == Connection.ConnectionMatch.connect) { matches.Add((new Vector2(x, z), connection, fieldConnection)); } } } } } if (reject) { continue; } foreach (var(pos, c1, c2) in matches) { if (reject) { break; } selectedField.transform.position = oldPos; selectedField.transform.rotation = oldRot; // Find the rotation and position we need to offset the other selected bricks with ConnectionField.GetConnectedTransformation(c1, c2, pivot, out Vector3 connectedOffset, out angle, out axis); var oldBrickPos = selectedBrick.transform.position; var oldBrickRot = selectedBrick.transform.rotation; AlignTransformations(selectedBricks, pivot, axis, angle, connectedOffset); Physics.SyncTransforms(); foreach (var checkBrick in selectedBricks) { if (reject) { break; } foreach (var checkPart in checkBrick.parts) { if (reject) { break; } foreach (var checkField in checkPart.connectivity.connectionFields) { var cons = checkField.QueryConnections(out Connection.ConnectionMatch match, true); if (match == Connection.ConnectionMatch.reject) { selectedBrick.transform.position = oldBrickPos; selectedBrick.transform.rotation = oldBrickRot; ResetTransformations(selectedBricks, oldPositions, oldRotations, selectedField.connectivity.part.brick); reject = true; break; } } } } if (reject) { break; } Physics.SyncTransforms(); var colliding = Colliding(selectedBricks, selectedBrick); selectedBrick.transform.position = oldBrickPos; selectedBrick.transform.rotation = oldBrickRot; Physics.SyncTransforms(); if (!colliding && ConnectionField.IsConnectionValid(c1, c2, pivot, selectedBricks)) { ResetTransformations(selectedBricks, oldPositions, oldRotations, selectedField.connectivity.part.brick); return(c1, c2); } selectedField.transform.position = currentFieldPos; selectedField.transform.rotation = currentFieldRot; ResetTransformations(selectedBricks, oldPositions, oldRotations, selectedField.connectivity.part.brick); } } selectedField.transform.position = oldPos; selectedField.transform.rotation = oldRot; } } return(null, null); }
/// <summary> /// Query the possible connections for this field /// </summary> /// <param name="match">Out parameter that signifies the type of match (ignore, reject or connect)</param> /// <param name="bothkinds">Optional boolean to specify whether we want to check for both connection field kinds</param> /// <param name="onlyConnectTo">An optional filter field if you only want to check connections on specific fields</param> /// <returns>A list of tuples for the possible connections</returns> public HashSet <(Connection, Connection)> QueryConnections(out Connection.ConnectionMatch match, bool bothkinds = false, ICollection <ConnectionField> onlyConnectTo = null) { LayerMask mask; if (bothkinds) { mask = LayerMask.GetMask(GetLayer(FieldKind.receptor), GetLayer(FieldKind.connector)); } else { var opposite = kind == FieldKind.connector ? FieldKind.receptor : FieldKind.connector; mask = LayerMask.GetMask(GetLayer(opposite)); } HashSet <(Connection, Connection)> validConnections = new HashSet <(Connection, Connection)>(); match = Connection.ConnectionMatch.ignore; // PhysicsScene var physicsScene = gameObject.scene.GetPhysicsScene(); var size = new Vector3((gridSize.x + 1) * BrickBuildingUtility.LU_5, BrickBuildingUtility.LU_1 * 2, (gridSize.y + 1) * BrickBuildingUtility.LU_5); var center = new Vector3((size.x - BrickBuildingUtility.LU_5) * -0.5f, 0.0f, (size.z - BrickBuildingUtility.LU_5) * 0.5f); var hits = physicsScene.OverlapBox(transform.TransformPoint(center), size * 0.5f, BrickBuildingUtility.colliderBuffer, transform.rotation, mask, QueryTriggerInteraction.Collide); for (var i = 0; i < hits; i++) { var overlap = BrickBuildingUtility.colliderBuffer[i]; var field = overlap.GetComponent <ConnectionField>(); if (field == null || field == this) { continue; } if (onlyConnectTo != null && !onlyConnectTo.Contains(field)) { continue; } if (Mathf.Abs(Vector3.Dot(field.transform.up, transform.up)) < 0.95f) { continue; } if (!GetOverlap(field, this, out Vector2Int min, out Vector2Int max)) { continue; } for (var x = min.x; x < max.x + 1; x++) { for (var z = min.y; z < max.y + 1; z++) { var localPos = new Vector3(x * BrickBuildingUtility.LU_5, 0.0f, z * BrickBuildingUtility.LU_5); var fieldConnection = field.GetConnectionAt(ConnectionField.ToGridPos(localPos)); if (fieldConnection != null && !field.HasConnection(fieldConnection)) { var worldPos = field.GetPosition(fieldConnection); var connection = GetConnectionAt(worldPos); if (connection != null && !HasConnection(connection)) { // Note: ConnectionValid checks both rejection and distance (position + rotation) so we need // to make sure we take care of both in case of false. if (!Connection.ConnectionValid(fieldConnection, connection, out Connection.ConnectionMatch pairMatch)) { if (pairMatch != Connection.ConnectionMatch.reject) { continue; } else { match = pairMatch; validConnections.Clear(); return(validConnections); } } if (pairMatch == Connection.ConnectionMatch.connect) { validConnections.Add((connection, fieldConnection)); } } } } } } match = Connection.ConnectionMatch.connect; return(validConnections); }