// Find the match that this hexagon piece is part of (implementation) private void GetMatchingPiecesAt(HexagonPiece piece, HexagonMatch match) { bool isPieceAddedToMatch = false; // Iterate over each possible tuple that is formed with this hexagon piece and see if that tuple is a match for (int i = 0; i < 6; i++) { HexagonTuple tuple = GetTupleAtCorner(piece.X, piece.Y, (HexagonPiece.Corner)i); if (!tuple.IsEmpty && tuple.IsMatching) { if (!isPieceAddedToMatch) { match.Add(piece); isPieceAddedToMatch = true; } if (matchesOnGridSet.Add(tuple.piece1)) { GetMatchingPiecesAt(tuple.piece1, match); } if (matchesOnGridSet.Add(tuple.piece2)) { GetMatchingPiecesAt(tuple.piece2, match); } if (matchesOnGridSet.Add(tuple.piece3)) { GetMatchingPiecesAt(tuple.piece3, match); } } } }
// Find the match that this hexagon piece is part of (implementation) private void TryGetMatchingPiecesAt(HexagonPiece piece, HexagonMatch match) { bool isPieceAddedToMatch = false; // Iterate over each possible group that is formed with this hexagon piece and see if that group is a match for (int i = 0; i < 6; i++) { HexagonGroup _group = GetGroupAtCorner(piece.GridPos.x, piece.GridPos.y, (HexagonPiece.Edge)i); if (_group.IsEmpty || !_group.IsMatching) { continue; } if (!isPieceAddedToMatch) { match.Add(piece); isPieceAddedToMatch = true; } if (matchesOnGridSet.Add(_group.Piece1)) { TryGetMatchingPiecesAt(_group.Piece1, match); } if (matchesOnGridSet.Add(_group.Piece2)) { TryGetMatchingPiecesAt(_group.Piece2, match); } if (matchesOnGridSet.Add(_group.Piece3)) { TryGetMatchingPiecesAt(_group.Piece3, match); } } }
/// <summary> /// Returns true if there are no possible moves that result in a match on the grid /// </summary> /// <returns>true; no more move left, false moves possible.</returns> public bool IsDeadlocked() { matchesOnGridSet.Clear(); // Check only on even coloums; checking all coloums is redundant. for (int x = 0; x < gridInfo.x; x += 2) { for (int y = 0; y < gridInfo.y; y++) { for (int i = 0; i < 6; i++) { HexagonGroup group = GetGroupAtCorner(x, y, (HexagonPiece.Edge)i); if (!group.IsEmpty) { for (int j = 0; j < 2; j++) { group.RotateClockwise(); HexagonMatch match = TryGetMatchingPiecesAt(group); if (match != null) { PoolManager.Instance.Push(match); group.RotateClockwise(2 - j); return(false); } } group.RotateClockwise(); } } } } return(true); }
private void ProcessMatch(HexagonMatch match) { _score += _scoreMultiplier * match.Count; UIManager.Instance.UpdateScore(_score); SoundManager.Instance.PlayFx("cluster"); for (int i = match.Count - 1; i >= 0; i--) { // Destroy the matching pieces in a fashionable way GridManager.Instance[match[i].GridPos.x][match[i].GridPos.y] = null; // Mark that slot as empty AnimationManager.Instance.BlowPieceAway(match[i]); // Check if a bomb was attached to the destroyed piece for (int j = _bombs.Count - 1; j >= 0; j--) { if (_bombs[j].HexagonElement == match[i]) { PoolManager.Instance.Push(_bombs[j]); SoundManager.Instance.PlayFx("bomb_remove"); // This bomb is now defused, move the last bomb to this index if (j < _bombs.Count - 1) { _bombs[j] = _bombs[_bombs.Count - 1]; } _bombs.RemoveAt(_bombs.Count - 1); } } } PoolManager.Instance.Push(match); }
private void ProcessMatch(HexagonMatch match) { score += scoreMultiplier * match.Count; UIManager.Instance.UpdateScore(score); for (int i = match.Count - 1; i >= 0; i--) { // Destroy the matching pieces in a fashionable way GridManager.Instance[match[i].X][match[i].Y] = null; // Mark that slot as empty AnimationManager.Instance.BlowPieceAway(match[i]); // Check if a bomb was attached to the destroyed piece for (int j = bombs.Count - 1; j >= 0; j--) { if (bombs[j].AttachedPiece == match[i]) { PoolManager.Instance.Push(bombs[j]); // This bomb is now defused, move the last bomb to this index if (j < bombs.Count - 1) { bombs[j] = bombs[bombs.Count - 1]; } bombs.RemoveAt(bombs.Count - 1); } } } PoolManager.Instance.Push(match); }
// Finds the match that have at least one hexagon piece from this group public HexagonMatch TryGetMatchingPiecesAt(HexagonGroup group) { matchesOnGridSet.Clear(); HexagonMatch result = TryGetMatchingPiecesAt(group.Piece1); if (result == null) { result = TryGetMatchingPiecesAt(group.Piece2); } if (result == null) { result = TryGetMatchingPiecesAt(group.Piece3); } return(result); }
// Finds the match that have at least one hexagon piece from this tuple public HexagonMatch GetMatchingPiecesAt(HexagonTuple tuple) { matchesOnGridSet.Clear(); HexagonMatch result = GetMatchingPiecesAt(tuple.piece1); if (result == null) { result = GetMatchingPiecesAt(tuple.piece2); if (result == null) { result = GetMatchingPiecesAt(tuple.piece3); } } return(result); }
// Find the match that this hexagon piece is part of public HexagonMatch GetMatchingPiecesAt(HexagonPiece piece) { // Don't search the piece for match if it is already searched before if (!matchesOnGridSet.Add(piece)) { return(null); } HexagonMatch result = PoolManager.Instance.PopMatch(); GetMatchingPiecesAt(piece, result); if (result.Count > 0) { return(result); } PoolManager.Instance.Push(result); return(null); }
// Finds all matches on the grid public List <HexagonMatch> GetAllMatchingPiecesOnGrid() { matchesOnGrid.Clear(); matchesOnGridSet.Clear(); // We can skip odd columns, if there is a match, it will be found while iterating the even columns for (int x = 0; x < gridInfo.x; x += 2) { for (int y = 0; y < gridInfo.y; y++) { HexagonMatch match = TryGetMatchingPiecesAt(grid[x][y]); if (match != null) { matchesOnGrid.Add(match); } } } return(matchesOnGrid); }
// Returns true if there are no possible moves that result in a match on the grid public bool CheckDeadlock() { matchesOnGridSet.Clear(); // We can skip odd columns, if there is a match, it will be found while iterating the even columns for (int x = 0; x < gridWidth; x += 2) { for (int y = 0; y < gridHeight; y++) { for (int i = 0; i < 6; i++) { // For each possible tuple on the grid HexagonTuple tuple = GetTupleAtCorner(x, y, (HexagonPiece.Corner)i); if (!tuple.IsEmpty) { // Check if rotating the tuple once or twice results in a match for (int j = 0; j < 2; j++) { tuple.RotateClockwise(); HexagonMatch match = GetMatchingPiecesAt(tuple); if (match != null) { PoolManager.Instance.Push(match); tuple.RotateClockwise(2 - j); return(false); } } // There is no match after 2 rotations, rotate the tuple one last time to restore its original state tuple.RotateClockwise(); } } } } return(true); }
private void OnSwipe(PointerEventData eventData) { if (_isBusy) { return; } if (!selection.IsVisible) { return; } // Check if this is a clockwise swipe or a counter-clockwise swipe Vector2 center = CameraManager.Instance.WorldToScreenPoint(selection.transform.localPosition); bool clockwise = Vector2.SignedAngle(eventData.pressPosition - center, eventData.position - center) < 0f; // Check if rotating the selection by a certain amount results in a match on the grid int rotationAmount; HexagonMatch match = null; for (rotationAmount = 1; rotationAmount < 3; rotationAmount++) { SoundManager.Instance.PlayFx("rotation"); selection.Group.RotateClockwise(clockwise ? 1 : -1); match = GridManager.Instance.TryGetMatchingPiecesAt(selection.Group); if (match != null) { break; } } if (match == null) { selection.Group.RotateClockwise(clockwise ? 1 : -1); // So that the selection will rotate 360 degrees SoundManager.Instance.PlayFx("rotation"); } StartCoroutine(RotateSelection(clockwise, rotationAmount, match)); }
public void Push(HexagonMatch match) { _matchPool.Push(match); }
private IEnumerator RotateSelection(bool clockwise, int amount, HexagonMatch match) { _isBusy = true; // Wait for the rotate animation to finish yield return(StartCoroutine(AnimationManager.Instance.RotateSelection(selection, amount * (clockwise ? -120f : 120f)))); // The grid will be updated if there is a match if (match != null) { // Don't show the selection while updating the grid selection.IsVisible = false; UIManager.Instance.ToggleSettingGroup(false); // Wait for a short interval so that users can also realize the match yield return(new WaitForSeconds(0.5f)); // A column index that is selected randomly from the matching pieces int possibleBombColumn = match[Random.Range(0, match.Count)].GridPos.x; // Update the score and etc. ProcessMatch(match); // Start filling in the blank slots (slots previously occupied by matching pieces) but don't wait for it to finish yet Coroutine fillBlanksCoroutine = StartCoroutine(GridManager.Instance.FillBlankSlots()); if (_score >= _nextBombSpawnScore) { // Spawn a bomb at a random column if we've reached the target score _nextBombSpawnScore += _bombInterval; HexagonBomb bomb = PoolManager.Instance.PopBomb(); bomb.InitBomb(GridManager.Instance[possibleBombColumn][GridManager.Instance.Height - 1], _bombExplosionCounter + 1); // Counter will decrement after this round _bombs.Add(bomb); } // Wait for the blank slots to be filled yield return(fillBlanksCoroutine); // Check if there are another matches on the grid after the blank slots are filled // If so, continue to update the grid until there is no match left List <HexagonMatch> matchesOnGrid = GridManager.Instance.GetAllMatchingPiecesOnGrid(); while (matchesOnGrid != null && matchesOnGrid.Count > 0) { yield return(new WaitForSeconds(0.5f)); for (int i = 0; i < matchesOnGrid.Count; i++) { ProcessMatch(matchesOnGrid[i]); } yield return(StartCoroutine(GridManager.Instance.FillBlankSlots())); matchesOnGrid = GridManager.Instance.GetAllMatchingPiecesOnGrid(); } // Decrement the counters of the bombs and end the game if a bomb reaches 0 for (int i = _bombs.Count - 1; i >= 0; i--) { if (!_bombs[i].Pulse()) { SoundManager.Instance.PlayFx("bomb_explode"); EndGame(); yield break; } } // Update the selection with the new pieces selection.SelectGroup(selection.transform.localPosition); // Check if there are no more possible matches on the grid (i.e. deadlock) if (GridManager.Instance.IsDeadlocked()) { SoundManager.Instance.PlayFx("oops"); EndGame(); yield break; } } _isBusy = false; }