A Match 3 game template.
Gameplay
MatchSweetsGameplay.mp4
A Match 3 game template with three implementations to fill the playing field. Use this project as a starting point for creating your own Match 3 game.
Items Scale Fill Strategy | Items Drop Fill Strategy | Items Roll Down Fill Strategy |
Note: The
ItemsDropFillStrategy
&ItemsRollDownFillStrategy
are given as an example. Consider to implement an object pooling technique for theItemMoveData
to reduce memory pressure.
To add a new icons set, simply create a SpriteAtlas
and add it to the AppContext
via the Inspector.
Note: You can change icons size by changing the
Pixels Per Unit
option in the sprite settings.
Let's say we want to add a goal to collect a certain number of specific items. First of all, create a class CollectItems
and inherit from the LevelGoal
.
public class CollectItems : LevelGoal
{
private readonly int _contentId;
private readonly int _itemsCount;
private int _collectedItemsCount;
public CollectItems(int contentId, int itemsCount)
{
_contentId = contentId;
_itemsCount = itemsCount;
}
public override void RegisterSolvedSequences(IEnumerable<ItemSequence<IUnityItem>> sequences)
{
foreach (var sequence in sequences)
{
foreach (var solvedGridSlot in sequence.SolvedGridSlots)
{
if (solvedGridSlot.Item.ContentId == _contentId)
{
_collectedItemsCount++;
}
}
}
if (_collectedItemsCount >= _itemsCount)
{
MarkAchieved();
}
}
}
Once the level goal is implemented. Don't forget to register it in the LevelGoalsProvider
.
public class LevelGoalsProvider : ILevelGoalsProvider
{
public LevelGoal[] GetLevelGoals(IGameBoard<IUnityItem> gameBoard)
{
return new LevelGoal[]
{
new CollectItems(0, 25),
new CollectRowMaxItems(gameBoard)
};
}
}
Note: You can modify the
LevelGoalsProvider
to return goals for a certain level, for example.
...
Let's implement a new sequence detector to detect square shapes. Create a class SquareShapeDetector
and inherit from the ISequenceDetector<TItem>
.
First of all, we have to declare an array of lookup directions.
public class SquareShapeDetector : ISequenceDetector<IUnityItem>
{
private readonly GridPosition[][] _squareLookupDirections;
public SquareShapeDetector()
{
_squareLookupDirections = new[]
{
new[] { GridPosition.Up, GridPosition.Left, GridPosition.Up + GridPosition.Left },
new[] { GridPosition.Up, GridPosition.Right, GridPosition.Up + GridPosition.Right },
new[] { GridPosition.Down, GridPosition.Left, GridPosition.Down + GridPosition.Left },
new[] { GridPosition.Down, GridPosition.Right, GridPosition.Down + GridPosition.Right },
};
}
public ItemSequence<IUnityItem> GetSequence(IGameBoard<IUnityItem> gameBoard, GridPosition gridPosition)
{
throw new NotImplementedException();
}
}
Then let's implement the GetSequence
method.
public ItemSequence<IUnityItem> GetSequence(IGameBoard<IUnityItem> gameBoard, GridPosition gridPosition)
{
var sampleGridSlot = gameBoard[gridPosition];
var gridSlots = new List<GridSlot<IUnityItem>>(4);
foreach (var lookupDirections in _squareLookupDirections)
{
foreach (var direction in lookupDirections)
{
var position = gridPosition + direction;
if (gameBoard.IsPositionOnBoard(position) == false)
{
break;
}
var gridSlot = gameBoard[position];
if (gridSlot.Item == null)
{
break;
}
if (gridSlot.Item.ContentId == sampleGridSlot.Item.ContentId)
{
gridSlots.Add(gridSlot);
}
}
if (gridSlots.Count == 3)
{
gridSlots.Add(sampleGridSlot);
break;
}
gridSlots.Clear();
}
return gridSlots.Count > 0 ? new ItemSequence<IUnityItem>(GetType(), gridSlots) : null;
}
Finally, add the SquareShapeDetector
to the sequence detector list of the GameBoardSolver
constructor in the AppContext
class.
public class AppContext : MonoBehaviour, IAppContext
{
...
private IGameBoardSolver<IUnityItem> GetGameBoardSolver()
{
return new GameBoardSolver(new ISequenceDetector<IUnityItem>[]
{
new SquareShapeDetector(),
new VerticalLineDetector(),
new HorizontalLineDetector()
});
}
...
}
...
Give a ⭐ if this project helped you!
Usage is provided under the MIT License.