MatchableGrid.cs
unknown
csharp
a year ago
15 kB
16
Indexable
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());
}
}
Editor is loading...
Leave a Comment