/// <summary> /// Transform minimum and maximum bounds from one space to another. For example, /// this is useful to find the minimum and maximum bounds of a tile collider /// within local space of tile system. /// </summary> /// <param name="bounds">Bounds that are to be transformed.</param> /// <param name="mat">Space-to-space transformation matrix.</param> /// <returns> /// The transformed bounds. /// </returns> private static BoxBounds SpaceToSpace(BoxBounds bounds, Matrix4x4 mat) { return(new BoxBounds( mat.MultiplyPoint3x4(bounds.Min), mat.MultiplyPoint3x4(bounds.Max) )); }
public bool Encapsulate(BoxBounds other) { int matchCount = 0; for (int i = 0; i < 8 && matchCount != 4; ++i) { var p = this[i]; for (int j = 0; j < 8; ++j) { var q = other[j]; if (Approx(p, q, ErrorThreshold)) { ++matchCount; break; } } } if (matchCount != 4) { return(false); } this.min = Vector3.Min(this.min, other.min); this.max = Vector3.Max(this.max, other.max); return(true); }
/// <summary> /// Gather collider information from 3D <see cref="BoxCollider"/> component. /// </summary> /// <param name="info">Collider information instance.</param> /// <param name="collider">Component which resides somewhere within tile.</param> private void GatherInfo_BoxCollider3D(ColliderInfo info, BoxCollider collider) { var tileToSystemMatrix = this.worldToSystem * collider.transform.localToWorldMatrix; info.IsTrigger = collider.isTrigger; info.Material = collider.sharedMaterial; info.Collider = collider; info.Bounds = SpaceToSpace(BoxBounds.FromBounds(collider.center, collider.size), tileToSystemMatrix); }
/// <summary> /// Create new "Combined Collider" game object which covers bounds of tiles that /// have been reduced. Reduced tile game objects are stripped if they nolonger /// contain any useful components. /// </summary> /// <param name="info">Information about reduced collider.</param> /// <param name="reducedBounds">Bounding area of all reduced colliders.</param> private void Reduce(ColliderInfo info, BoxBounds reducedBounds) { var reducedGO = new GameObject("Combined Collider"); // Can only copy layer and tag from collider information when the collider // information actually contains a game object! if (info.Tile.gameObject != null) { if (this.separateByLayer) { reducedGO.layer = info.Tile.gameObject.layer; } if (this.separateByTag) { reducedGO.tag = info.Tile.gameObject.tag; } } var reducedTransform = reducedGO.transform; reducedTransform.SetParent(this.system.transform, false); reducedBounds = SpaceToSpace(reducedBounds, reducedTransform.worldToLocalMatrix * this.systemToWorld); switch (info.Type) { case ColliderType.BoxCollider2D: { var collider = reducedGO.AddComponent <BoxCollider2D>(); collider.offset = reducedBounds.center; collider.size = reducedBounds.size; collider.isTrigger = info.IsTrigger; collider.sharedMaterial = info.Material as PhysicsMaterial2D; this.DestroyTrackedColliders(); } break; case ColliderType.BoxCollider3D: { var collider = reducedGO.AddComponent <BoxCollider>(); collider.center = reducedBounds.center; collider.size = reducedBounds.size; collider.isTrigger = info.IsTrigger; collider.sharedMaterial = info.Material as PhysicMaterial; this.DestroyTrackedColliders(); } break; default: Debug.LogWarning(string.Format("Collider reduction not implemented for '{0}'.", info.Type)); break; } }
/// <summary> /// Gather hypothetical collider information from solid flag. /// </summary> /// <param name="info">Collider information instance.</param> private void GatherInfo_SolidFlag(ColliderInfo info) { info.IsTrigger = false; // Bounds of 2D colliders need to be consistent. Vector3 boundsSize = this.system.CellSize; if (info.Type == ColliderType.BoxCollider2D) { boundsSize.z = 1f; } info.Bounds = BoxBounds.FromBounds(this.system.LocalPositionFromTileIndex(info.Row, info.Column), boundsSize); }
/// <summary> /// Gather collider information from <see cref="BoxCollider2D"/> component. /// </summary> /// <param name="info">Collider information instance.</param> /// <param name="collider">Component which resides somewhere within tile.</param> private void GatherInfo_BoxCollider2D(ColliderInfo info, BoxCollider2D collider) { var tileToSystemMatrix = this.worldToSystem * collider.transform.localToWorldMatrix; // Bounds of 2D colliders need to be consistent. Vector3 boundsSize = collider.size; boundsSize.z = 1f; Vector3 center = collider.offset; info.IsTrigger = collider.isTrigger; info.Material = collider.sharedMaterial; info.Collider = collider; info.Bounds = SpaceToSpace(BoxBounds.FromBounds(center, boundsSize), tileToSystemMatrix); }
/// <summary> /// Scans for and applies reductions if specified anchor tile contains a collider. /// </summary> /// <remarks> /// <para>This method searches from left-to-right from specified anchor tile to /// find furthest tile horizontally that it can be reduced with. Once the initial /// horizontal sequence is known this method proceeds to scan vertically for other /// sequences of colliders that can further reduce the initial horizontal sequence.</para> /// </remarks> /// <param name="anchor">Index of anchor tile.</param> /// <returns></returns> private int Scan(TileIndex anchor) { ColliderInfo anchorInfo = null; this.GetColliderInfo(ref anchorInfo, anchor); if (anchorInfo == null) { return(anchor.column); } ColliderInfo info = null, firstInfo = null, lastInfo = null; // Begin tracking list of collider components which will be combined. this.ResetReducedColliderTracking(); this.TrackReducedCollider(anchorInfo.Row, anchorInfo.Column, anchorInfo.Collider); try { TileIndex target = anchor; BoxBounds reducedBounds = anchorInfo.Bounds; // Scan rightwards for colliders which can be combined with anchor. for (int column = anchor.column + 1; column < this.system.ColumnCount; ++column) { if (this.markedTiles[anchor.row, column]) { break; } this.GetColliderInfo(ref info, anchor.row, column); if (!this.CanReduce(info, anchorInfo)) { break; } // Can candidate collider be combined with anchor? if (!reducedBounds.Encapsulate(info.Bounds)) { break; } target.column = column; this.TrackReducedCollider(info.Row, info.Column, info.Collider); } this.GetColliderInfo(ref info, target); var prevFirstBounds = anchorInfo.Bounds; var prevLastBounds = info.Bounds; // Extend downwards if possible, though avoid crossing T-junctions since splitting // a T-junction will often cause more colliders to occur in result. for (int row = anchor.row + 1; row < this.system.RowCount; ++row) { this.GetColliderInfo(ref firstInfo, row, anchor.column); this.GetColliderInfo(ref lastInfo, row, target.column); if (firstInfo == null || lastInfo == null || !this.CanReduce(firstInfo, anchorInfo) || this.CheckForTJunction(firstInfo, lastInfo)) { goto FinishedExtendingDownards; } // Does first and last tile from previous row extend downwards? if (!prevFirstBounds.Encapsulate(firstInfo.Bounds) || !prevLastBounds.Encapsulate(lastInfo.Bounds)) { goto FinishedExtendingDownards; } // No need to track collider from `lastInfo` since it will be included // within the following loop. this.candidateColliders.Clear(); this.candidateColliders.Add(firstInfo.Collider); // Reduce bounds for strip. BoxBounds stripBounds = firstInfo.Bounds; for (int column = anchor.column + 1; column <= target.column; ++column) { this.GetColliderInfo(ref info, row, column); if (!this.CanReduce(info, anchorInfo) || !stripBounds.Encapsulate(info.Bounds)) { goto FinishedExtendingDownards; } this.candidateColliders.Add(info.Collider); } // The following should not fail, but let's just be safe! if (!reducedBounds.Encapsulate(stripBounds)) { Debug.LogWarning("Unable to encapsulate collider on reduce."); goto FinishedExtendingDownards; } target.row = row; // Accept and track candidate colliders! for (int i = 0; i < this.candidateColliders.Count; ++i) { this.TrackReducedCollider(row, anchor.column + i, this.candidateColliders[i]); } prevFirstBounds = firstInfo.Bounds; prevLastBounds = lastInfo.Bounds; } FinishedExtendingDownards: ; bool hasManyColliders = (this.reducedColliderCount > 1); bool hasOneHypotheticalCollider = (this.reducedColliderCount == 1 && this.reducedColliderComponents.Count == 0); if (hasManyColliders || hasOneHypotheticalCollider) { this.Reduce(anchorInfo, reducedBounds); } return(target.column); } finally { ColliderInfo.Despawn(anchorInfo); ColliderInfo.Despawn(info); ColliderInfo.Despawn(firstInfo); ColliderInfo.Despawn(lastInfo); } }