예제 #1
0
        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;
                        }
                    }
                }
            }
        }
예제 #2
0
        /// <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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        /// <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);
        }