MatchableGrid.cs
unknown
csharp
a month ago
15 kB
5
Indexable
Never
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MatchableGrid : GridSystem<Matchable> { private MatchablePool pool; private ScoreManager score; [SerializeField] private Vector3 offScreenOffset; private void Start() { pool = (MatchablePool) MatchablePool.Instance; score = (ScoreManager) ScoreManager.Instance; } // private void Update() // { // Normally we could have used that since this game is not a big one. However, this would create inefficiency since we run multiple codes for each frame. // // } public IEnumerator PopulateGrid(bool allowMatches = false, bool initialPopulation = false) // yield return 0.1 only happens in init population. { List<Matchable> newMatchables = new List<Matchable>(); // List of new matchables during population. Matchable newMatchable; Vector3 onScreenPosition; for (int y=0; y!= Dimensions.y; ++y) for(int x=0; x!=Dimensions.x; ++x) if (IsEmpty(x,y)) { // Get a matchable from pool newMatchable = pool.GetRandomMatchable(); // position the matchable on screen newMatchable.transform.position = transform.position + new Vector3(x,y) + offScreenOffset; // activate the matchable newMatchable.gameObject.SetActive(true); // Position of the matchable newMatchable.position = new Vector2Int(x,y); // place the matchable in the grid-- since we are inheriting from gridsystem PutItemAt(newMatchable,x ,y); newMatchables.Add(newMatchable); int type = newMatchable.Type; while(!allowMatches && IsPartOfAMatch(newMatchable)) { // change the matchables type until it isnt a match anymore. if (pool.NextType(newMatchable) == type) { Debug.LogWarning("Failed to find a matchable type that didn't match at (" + x + "," + y + ")"); Debug.Break(); break; } } // Move the matchable to its own position // yield return new WaitForSeconds(0.1f); } for (int i =0; i < newMatchables.Count; ++i) { onScreenPosition = transform.position + new Vector3(newMatchables[i].position.x,newMatchables[i].position.y); if (i == newMatchables.Count - 1) yield return StartCoroutine(newMatchables[i].MoveToPosition(onScreenPosition)); else StartCoroutine(newMatchables[i].MoveToPosition(onScreenPosition)); // wait for 0.1 seconds after each. Just a cool effect. if (initialPopulation) yield return new WaitForSeconds(0.05f); } } // Check if the matchable being populated is part of a match or not -- private bool IsPartOfAMatch(Matchable toMatch) { int horizontalMatches = 0; int verticalMatches = 0; // We start checking for left. Will do the same algorithm for each side. horizontalMatches += CountMatchesInDirection(toMatch, Vector2Int.left); horizontalMatches += CountMatchesInDirection(toMatch, Vector2Int.right); if (horizontalMatches>1) return true; verticalMatches += CountMatchesInDirection(toMatch, Vector2Int.up); verticalMatches += CountMatchesInDirection(toMatch, Vector2Int.down); if (verticalMatches>1) return true; return false; } // Count the number of matches on the grid starting from the matchable to match moving in the direction indicated private int CountMatchesInDirection(Matchable toMatch, Vector2Int direction) { int matches =0; Vector2Int position = toMatch.position + direction; while(CheckBounds(position) && !IsEmpty(position) && GetItemAt(position).Type == toMatch.Type) { ++matches; position += direction; } return matches; } public IEnumerator TrySwap(Matchable[] selected) { // To prevent the crashes due to simultaneously mouse-enter while animate, we get a local copy so that function wont depend on // current array, it will depend on the version of the array when function starts. Matchable[] copies = new Matchable[2]; copies[0] = selected[0]; copies[1] = selected[1]; // yield until matchables animate swapping yield return StartCoroutine(Swap(copies)); // If both are gem, reoslve entire grid if (copies[0].IsGem && copies[1].IsGem) { MatchEverything(); yield break; } else if (copies[0].IsGem) { MatchEverythingByType(copies[0],copies[1].Type); yield break; } else if (copies[1].IsGem) { MatchEverythingByType(copies[1],copies[0].Type); yield break; } Match[] matches = new Match[2]; matches[0] = GetMatch(copies[0]); matches[1] = GetMatch(copies[1]); if (matches[0] != null) { // Need to resolve this StartCoroutine(score.ResolveMatch(matches[0])); } if (matches[1] !=null) { // Resolve also StartCoroutine(score.ResolveMatch(matches[1])); } if (matches[0] == null && matches[1] == null) { yield return StartCoroutine(Swap(copies)); if (ScanForMatches()) StartCoroutine(FillAndScanGrid()); } else StartCoroutine(FillAndScanGrid()); } private IEnumerator FillAndScanGrid() { CollapseGrid(); // After the match, matching matchables will leave empty spaces. yield return StartCoroutine(PopulateGrid(true)); // if there are matches // Instead of Update above, we will check whenever a match happens. Here we collapse grid, populate grid and now we will scan the grid for chain reactions. if (ScanForMatches()) // collapse - repopulate - scan again StartCoroutine(FillAndScanGrid()); } private Match GetMatch(Matchable toMatch) { Match match = new Match(toMatch); Match horizontalMatch, verticalMatch; /* for above 3 line code, explanation Below function will not contain the original matchable. However, we add it add first line of this function. Therefore, horizontalMatch or verticalMatch needs to contain at least 2 Matchables in the matching scenario. */ horizontalMatch = GetMatchesInDirection(match,toMatch, Vector2Int.left); horizontalMatch.Merge(GetMatchesInDirection(match, toMatch, Vector2Int.right)); horizontalMatch.orientation = Orientation.horizontal; if (horizontalMatch.Count > 1) { match.Merge(horizontalMatch); // scan for vertical branches GetBranches(match, horizontalMatch, Orientation.vertical); } verticalMatch = GetMatchesInDirection(match,toMatch, Vector2Int.up); verticalMatch.Merge(GetMatchesInDirection(match, toMatch, Vector2Int.down)); verticalMatch.orientation = Orientation.vertical; if (verticalMatch.Count > 1) { match.Merge(verticalMatch); GetBranches(match, verticalMatch, Orientation.horizontal); } if (match.Count == 1) return null; // No matches, only the input. return match; } private void GetBranches(Match tree, Match branchToSearch, Orientation perpendicular) { Match branch; foreach(Matchable matchable in branchToSearch.Matchables) { branch = GetMatchesInDirection(tree,matchable, perpendicular == Orientation.horizontal ? Vector2Int.left : Vector2Int.down); branch.Merge(GetMatchesInDirection(tree, matchable, perpendicular == Orientation.horizontal ? Vector2Int.right : Vector2Int.up)); branch.orientation = perpendicular; if (branch.Count > 1) // At least 2 and we also have the main matchable. tree.Merge(branch); GetBranches(tree, branch, perpendicular == Orientation.horizontal ? Orientation.vertical : Orientation.horizontal); } } // Add each matching matchable in the directino to a math and return that. private Match GetMatchesInDirection(Match tree, Matchable toMatch, Vector2Int direction) { Match match = new Match(); Vector2Int position = toMatch.position + direction; Matchable next; while(CheckBounds(position) && !IsEmpty(position)) { next = GetItemAt(position); if (next.Type == toMatch.Type && next.Idle) { if(!tree.Contains(next)) match.AddMatchable(next); else { match.AddUnlisted(); } position += direction; } else break; } return match; } private void CollapseGrid() { for (int x = 0; x != Dimensions.x; ++x) for (int yEmpty = 0; yEmpty != Dimensions.y - 1; ++yEmpty) { if (IsEmpty(x,yEmpty)) for (int yNotEmpty = yEmpty + 1; yNotEmpty != Dimensions.y; ++yNotEmpty) if(!IsEmpty(x, yNotEmpty) && GetItemAt(x, yNotEmpty).Idle) { // Move the matchables from nonempty to empty MoveMatchableToPosition(GetItemAt(x, yNotEmpty), x, yEmpty); break; } } } private void MoveMatchableToPosition(Matchable toMove, int x, int y) { // Remove it from the old position // Place it the matchable as its new position MoveItemTo(toMove.position, new Vector2Int(x,y)); // Update the matchable's internal grid position toMove.position = new Vector2Int(x,y); // Start animation to move it on screen StartCoroutine(toMove.MoveToPosition(transform.position + new Vector3(x,y))); } private bool ScanForMatches() { bool madeAMatch = false; Matchable toMatch; Match match; for (int y =0; y != Dimensions.y; ++y) for (int x= 0; x != Dimensions.x; ++x) if(!IsEmpty(x,y)) { toMatch = GetItemAt(x,y); if(!toMatch.Idle) { continue; } match = GetMatch(toMatch); if (match != null) { madeAMatch = true; StartCoroutine(score.ResolveMatch(match)); } } return madeAMatch; } public void MatchAllAdjacent(Matchable powerup) // Explode a square 3x3 { Match allAdjacent = new Match(); for (int y = powerup.position.y -1; y!= powerup.position.y +2; ++y) for (int x = powerup.position.x -1; x != powerup.position.x +2; ++x) if(CheckBounds(x,y) && !IsEmpty(x,y) && GetItemAt(x,y).Idle) { allAdjacent.AddMatchable(GetItemAt(x,y)); } StartCoroutine(score.ResolveMatch(allAdjacent, MatchType.match4)); } public void MatchRowAndColumn(Matchable powerup) // match all row and column, another powerup. { Match rowAndColumn = new Match(); for (int y = 0; y!= Dimensions.y; ++y) if(CheckBounds(powerup.position.x,y) && !IsEmpty(powerup.position.x,y) && GetItemAt(powerup.position.x,y).Idle) rowAndColumn.AddMatchable(GetItemAt(powerup.position.x, y)); for (int x = 0; x != Dimensions.x; ++x) if(CheckBounds(x,powerup.position.y) && !IsEmpty(x,powerup.position.y) && GetItemAt(x,powerup.position.y).Idle) rowAndColumn.AddMatchable(GetItemAt(x,powerup.position.y)); StartCoroutine(score.ResolveMatch(rowAndColumn, MatchType.cross)); } private IEnumerator Swap(Matchable[] selected) { // Swap in data SwapItemsAt(selected[0].position, selected[1].position); // assign new positions Vector2Int temp = selected[0].position; selected[0].position = selected[1].position; selected[1].position = temp; // get world positions of both Vector3[] worldPosition = new Vector3[2]; worldPosition[0] = selected[0].transform.position; worldPosition[1] = selected[1].transform.position; // move them to their new postions StartCoroutine(selected[0].MoveToPosition(worldPosition[1])); yield return StartCoroutine(selected[1].MoveToPosition(worldPosition[0])); } public void MatchEverything() { Match everything = new Match(); for (int y = 0; y != Dimensions.y; ++y) for (int x = 0; x != Dimensions.x; ++x) if (CheckBounds(x,y) && !IsEmpty(x,y) && GetItemAt(x,y).Idle) everything.AddMatchable(GetItemAt(x,y)); StartCoroutine(score.ResolveMatch(everything, MatchType.match5)); StartCoroutine(FillAndScanGrid()); } public void MatchEverythingByType(Matchable gem, int type) { Match everythingByType = new Match(gem); for (int y = 0; y != Dimensions.y; ++y) for (int x = 0; x != Dimensions.x; ++x) if (CheckBounds(x,y) && !IsEmpty(x,y) && GetItemAt(x,y).Idle && GetItemAt(x,y).Type == type) everythingByType.AddMatchable(GetItemAt(x,y)); StartCoroutine(score.ResolveMatch(everythingByType, MatchType.match5)); StartCoroutine(FillAndScanGrid()); } }
Leave a Comment