Computer Organization
Computer Organization
Game Description
Number Crush is a "match-three" game, where the core game play is based on
swapping two adjacent random values among several on the game board to make
a row or column of at least 3 matching-random values. On this match, the
matched random values are removed from the board, and random values above
them fall into the empty spaces, with new random values appearing from the
top of the board. This may create a new matched set of random values, which is
automatically cleared in the same manner. The game is split among many levels,
which must be completed in sequence.
LEVELS
Level 1
Only external assets we’re using are some candy graphics (Public Domain, found on
OpenGameArt here) and a very cool sound (found on FreeSound here) to build our game. User
can drag (in an attempt to swap) one candy either horizontally or vertically. When the swap
happens, the game checks for a match. As soon as a vertical or horizontal match of three (or
more!) is encountered, the matched candies disappear. Remaining candies collapse, new candies
get created to replace them which collapse, too (imagine gravity acting upon them). The game
checks if another match of three is encountered (without any user intervention). If this happens,
the matched ones disappear again, remaining candies collapse, new candies fall and so on and so
forth. This goes on until no match of three exist and user intervention is required for the game to
go on. If the user does not touch the screen for a while, potential matches (candies that if one of
them gets swapped will form a match of three) start animating, to give the user a small hint in
order to continue the game.
The described game flow can be visualized in the below diagram
Such a game can have many types of bonuses. For the sake of this blog post, we have
implemented only one. This is created if the user’s drag/swap has a match of four (or more) as an
immediate result (i.e. it is not created in matches of four that occur in the subsequent loop of
collapses/creations). These bonus candy have a certain color (matching the one found in the
normal game candy). If the user later does a match that contains a bonus, then the whole row or
column is removed (depending on whether the match was horizontally or vertically oriented).
Our game never ends; user can swap and destroys candy (while having her score increased)
forever. In production games, user progresses through levels by achieving a certain score or by
other means, e.g. by destroying an amount of special bonus candy. The game has only one scene,
which we’ll describe. This scene has two buttons, one to restart the level and one to load a
predefined level (quite useful for debugging!).
Let’s dive into the code! As in our previous blog posts, we’ll see the code file by file.
Enums
Our game contains two enumerations. The BonusType contains info about the bonus that a
shape/candy can carry. It has been defined with the Flags attribute to allow multiple values in the
enumeration (check here for a nice article). The class BonusTypeUtilities contains only one
method, to determine whether an enumeration variable contains the specified bonus type.
Finally, the GameState enum contains the three states of our game.
– None: initial state (idle)
– Animating: when the game is animating (showing animations, collapsing, creating new candies
etc.)
[Flags]
{
None,
DestroyWholeRowColumn
== BonusType.DestroyWholeRowColumn;
None,
SelectionStarted,
Animating
Constants
Contains some useful constant and self-explainable variables for our game, regarding animation
durations, score, rows and columns for the array that will contain our candy and more.
}
Shape
The Shape class will be used to hold details for each individual candy. Each candy GameObject
on the screen will have a Shape component, so each Shape instance is a MonoBehaviour. It
contains info about potential bonus(es), the Type of the Shape (in our case, the candy color) the
Column and Row that the candy is placed, a constructor that initializes the bonus enumeration
and a method that compares the current Shape with another one, by comparing its Type. We use
a Row and Column since we’ll have a two dimensional array host our candies.
public Shape()
Bonus = BonusType.None;
Since Shape is a MonoBehaviour that is attached to our prefabs (as we’ll see later), we cannot
use a constructor to initialize it. So, we’ve implemented an Assign method that sets the basic
properties of the Shape. The SwapColumnRow method swaps the row and the column properties
of two shape instances.
if (string.IsNullOrEmpty(type))
Column = column;
Row = row;
Type = type;
a.Row = b.Row;
b.Row = temp;
temp = a.Column;
a.Column = b.Column;
b.Column = temp;
SoundManager
The SoundManager is an easily extendable class that contains AudioClip and relevant
AudioSource for the crincle sound. Plus, there is a public method to play this sound.
If one wants to add more sounds, she could easily do that by adding an AudioClip, and
AudioSource, add that during Awake and create another “PlayCrincle” method, just like the code
below.
AudioSource crincle;
void Awake()
crincle = AddAudio(crincleAudioClip);
}
AudioSource AddAudio( AudioClip audioClip)
audioSource.playOnAwake = false;
audioSource.clip = audioClip;
return audioSource;
crincle.Play();
Utilities
This static class contains some static helper methods.
The AnimatePotentialMatches coroutine takes a list of GameObjects and modifies their opacity
(from 1.0 to 0.3 and then to 1.0) using a constant time delay. This is to animate the potential
matches that are given as a hint to the user.
Color c = item.GetComponent<SpriteRenderer>().color;
c.a = i;
item.GetComponent<SpriteRenderer>().color = c;
Color c = item.GetComponent<SpriteRenderer>().color;
c.a = i;
item.GetComponent<SpriteRenderer>().color = c;
The AreVerticalOrHorizontalNeighbors method returns true if the two shapes that are passed as
parameters are next to each other, either vertically or horizontally.
public static bool AreVerticalOrHorizontalNeighbors(Shape s1, Shape s2)
s1.Row == s2.Row)
The GetPotentialMatches method tries to find and return a list of possible matches for the game
to animate, as a hint to the user. It loops in all candy, calls six different methods that search for
potential matches and gathers their results. When we have more than 3 results (different sets of
matches), we return a random one of them. However, if we search half the array and we have
less than or equal to two matches, we return a random one. This, because we don’t want the
algorithm to search more, since by running on a mobile device i) we’ll have a performance
penalty which in turn ii) will lead to a battery drain.
if (matches.Count >= 3)
return null;
The code for the six “search” methods won’t be fully listed (it was longer than I originally
thought), we’ll just include the comments next to the code that visualize what kind of patterns
the methods are searching for.
CheckHorizontal methods search for these patterns (imagine this like a 5×5 array, those
elements marked with * are random shapes whereas the ones marked with & are of the same
color)
AlteredCandyInfo
This class contains information about candy that are about to be moved after a collapse/new
candy creation event. It contains
– a property that returns the Distinct (i.e. unique) result of the above list. This is necessary in
case the internal list contains the same shape twice
get
return newCandy.Distinct();
{
if (!newCandy.Contains(go))
newCandy.Add(go);
public AlteredCandyInfo()
MatchesInfo
The MatchesInfo class contains useful information about the candies that were matches (either a
match of three or more). It looks a lot like the before mentioned AlteredCandyInfo class (we
could possible use some inheritance here) with the addition of the BonusType information for the
entire match.
get
return matchedCandies.Distinct();
}
if (!matchedCandies.Contains(go))
matchedCandies.Add(go);
AddObject(item);
public MatchesInfo()
BonusesContained = BonusType.None;
}
ShapesArray
As we previously described, we’ll be using a two dimensional array to store our candy shapes.
One option would be to create an instance of the array and then do operations on it. However, a
much better option is to encapsulate this array (along with some useful operations and variables)
in a class. This is the purpose of the ShapesArray class.
Initially, we can see that a two dimensional array is declared. Its dimensions correspond to
values taken from the Constants class. There is also an indexer that returns the specific
GameObject via requested column/row.
get
try
throw ex;
set
The Swap method has the responsibility to swap two GameObjects. It starts by creating a backup
of them, in case there is no match and they need to get back to their original positions. Then, it
swaps their position in the array and, finally, it calls the SwapColumnRow static method in the
Shape class, to swap the individual properties of the two Shape components.
backupG1 = g1;
backupG2 = g2;
Shape.SwapColumnRow(g1Shape, g2Shape);
The UndoSwap method will undo the swap by simply calling the Swap method on the backup
GameObjects.
Swap(backupG1, backupG2);
The ShapesArray class contains two methods for matches checking. One of them does a
horizontal check whereas the other does a vertical one. They both accept a GameObject as a
parameter and will check either the row or the column in which this GameObject belongs to.
Also, this GameObject is always added to the list of matches. However, if they find less than
three matches, they return an empty list.
matches.Add(go);
//check left
if (shape.Column != 0)
if (shapes[shape.Row, column].GetComponent<Shape>().IsSameType(shape))
{
matches.Add(shapes[shape.Row, column]);
else
break;
//check right
if (shape.Column != Constants.Columns - 1)
if (shapes[shape.Row, column].GetComponent<Shape>().IsSameType(shape))
matches.Add(shapes[shape.Row, column]);
else
break;
matches.Clear();
return matches.Distinct();
matches.Add(go);
//check bottom
if (shape.Row != 0)
shapes[row, shape.Column].GetComponent<Shape>().IsSameType(shape))
matches.Add(shapes[row, shape.Column]);
else
break;
}
//check top
if (shape.Row != Constants.Rows - 1)
shapes[row, shape.Column].GetComponent<Shape>().IsSameType(shape))
matches.Add(shapes[row, shape.Column]);
else
break;
matches.Clear();
return matches.Distinct();
The GetEntireRow and GetEntireColumn methods return the collection of GameObjects that
belong in a specific row or column. They are used when a match contains a bonus candy.
matches.Add(shapes[row, column]);
return matches;
matches.Add(shapes[row, column]);
return matches;
}
The ContainsDestroyRowColumnBonus method checks if a collection of matches contains a
bonus candy with type “DestroyRowColumn”. This, in order to have the entire row/column
removed later.
if (BonusTypeUtilities.ContainsDestroyWholeRowColumn
(go.GetComponent<Shape>().Bonus))
return true;
return false;
The GetMatches method has two overloads. The first one takes a single GameObject as a
parameter. It sequentially
– if there are any bonuses there, it will retrieve the entire row. It will also add the
DestroyWholeRowColumn bonus flag to the matchesInfo.BonusesContained property if it does
not already exist.
if (ContainsDestroyRowColumnBonus(horizontalMatches))
horizontalMatches = GetEntireRow(go);
if (!BonusTypeUtilities.ContainsDestroyWholeRowColumn(matchesInfo.BonusesContained))
matchesInfo.BonusesContained |= BonusType.DestroyWholeRowColumn;
matchesInfo.AddObjectRange(horizontalMatches);
if (ContainsDestroyRowColumnBonus(verticalMatches))
verticalMatches = GetEntireColumn(go);
if (!BonusTypeUtilities.ContainsDestroyWholeRowColumn(matchesInfo.BonusesContained))
matchesInfo.BonusesContained |= BonusType.DestroyWholeRowColumn;
}
matchesInfo.AddObjectRange(verticalMatches);
return matchesInfo;
The other overload of the GetMatches method gets a collection of GameObjects as a parameter.
For each one, it will use the previously described overload to check for matches.
matches.AddRange(GetMatches(go).MatchedCandy);
return matches.Distinct();
The Remove method removes (sets as null) an item from the array. It will be called once for each
match encountered.
The Collapse method will collapse the remaining candies in the specified columns, after the
matched candies removal. Basically, it searches for null items. If it finds any, it will move the
nearest top candy to the null item position. It will continue to do so until all null items are
stacked on the top positions of the column. Moreover, it will calculate the max distance a candy
will have to be moved (this will assist in calculating the animation duration). All the required
information is passed into an AlteredCandyInfo class, which is returned to the caller.
for (int row2 = row + 1; row2 < Constants.Rows; row2++) { //if you find
one, bring it down (i.e. replace it with the null you found) if (shapes[row2, column] != null)
{ shapes[row, column] = shapes[row2, column]; shapes[row2, column] = null;
//calculate the biggest distance if (row2 - row > collapseInfo.MaxDistance)
collapseInfo.AddCandy(shapes[row, column]);
break;
return collapseInfo;
The GetEmptyItemsOnColumn method gets a specified column as a parameter. It will return the
Shape details (more specifically, the positions) via the ShapeInfo class in this column which are
empty (null).
return emptyItems;
The ShapeInfo class contains details about row and column for a shape.
ShapesManager
The ShapesManager class is the main class of our game. It handles the array creation, score
keeping and the candy GameObjects’ creation and destruction.
It is attached to the ShapesManager GameObject. We pass some prefabs and GameObjects via
the Editor to the ShapesManager public fields. Specifically, the CandyPrefabs array contains our
candy GameObjects, the explosion prefabs contains some animated GameObjects that will run
when any candy is destroyed and the BonusPrefabs array contains 5 candy GameObjects, with
each one having a corresponding color with our normal candy (for correct matching). The
DebugText and ScoreText fields contain UI Text GameObject references, whereas the
ShowDebugInfo boolean variable allows the game to show some debug information, on
developer’s request.
Let’s see the code! In the beginning, there are some private members’ declarations, along with
the public ones that we previously described. Candy size is also specified, along with the first
candy (the one at [0,0]) position in the scene (called BottomRight). We also declare two
IEnumerator variables, which will hold references to coroutines instantiated throughout this
class, to make their termination easier.
IEnumerable<GameObject> potentialMatches;
The Awake method enables or disables a UI Text GameObject. This GameObject, if enabled,
shows some debug info during the game.
The Start method calls 3 methods to initialize our game.
void Awake()
DebugText.enabled = ShowDebugInfo;
void Start()
InitializeTypesOnPrefabShapesAndBonuses();
InitializeCandyAndSpawnPositions();
StartCheckForPotentialMatches();
– sets the Type of each prefab Shape component with the name of the GameObject (e.g.
bean_blue)
– sets the Type of each prefab Bonus Shape component with the name of the corresponding
prefab (e.g. the swirl_blue bonus candy will get bean_blue as a type). This, in order to be
precisely matched (the blue bonus matches the blue candy etc.).
private void InitializeTypesOnPrefabShapesAndBonuses()
{
//just assign the name of the prefab
item.GetComponent<Shape>().Type = item.name;
//assign the name of the respective "normal" candy as the type of the Bonus
item.GetComponent<Shape>().Type = CandyPrefabs.
InitializeCandyAndSpawnPositions
The InitializeCandyAndSpawnPositions method is based on some other methods and functions.
The score related methods are listed below, featuring a simple initialization and UI updates.
score = 0;
ShowScore();
}
score += amount;
ShowScore();
The GetRandomCandy method returns a random candy prefab from the candy prefabs collection.
{
GameObject go = Instantiate(newCandy,
as GameObject;
The SetupSpawnPositions method gives initial values to the spawn positions. Those are the
positions that new candy will be created to replace the ones that were removed because of a
match of three or four. After their creation at the designated positions, they’ll be animated to the
positions they’ll cover (the null/empty positions in the array).
//create the spawn positions for the new shapes (will pop from the 'ceiling')
SpawnPositions[column] = BottomRight
}
The DestroyAllCandy method calls the GameObject.Destroy method on all candy in the array, in
order to remove them from our scene.
Destroy(shapes[row, column]);
The InitializeCandyAndSpawnPositions
– reinitializes the array and the spawn positions for the new candy
– loops through all the array elements and creates new candy taking caution *not* to initially
create any matches of three. It’s up to the user to do that, via her swaps!
InitializeVariables();
if (shapes != null)
DestroyAllCandy();
.IsSameType(newCandy.GetComponent<Shape>())
newCandy = GetRandomCandy();
}
//check if two previous vertical are of the same type
.IsSameType(newCandy.GetComponent<Shape>())
&& shapes[row - 2,
column].GetComponent<Shape>().IsSameType(newCandy.GetComponent<Shape>()))
newCandy = GetRandomCandy();
SetupSpawnPositions();
The FixSortingLayer method is used during a user swap, to make sure that the candy that was
dragged will appear on top of the other one, for better visual results.
sp1.sortingOrder = 1;
sp2.sortingOrder = 0;
potentialMatches = Utilities.GetPotentialMatches(shapes);
if (potentialMatches != null)
while (true)
{
AnimatePotentialMatchesCoroutine = Utilities.AnimatePotentialMatches(potentialMatches);
StartCoroutine(AnimatePotentialMatchesCoroutine);
The ResetOpacityOnPotentialMatches sets the opacity to default (1.0f) at the candy that were
animated, as potential matches.
if (potentialMatches != null)
Color c = item.GetComponent<SpriteRenderer>().color;
c.a = 1.0f;
item.GetComponent<SpriteRenderer>().color = c;
The StartCheckForPotentialMatches method stops the check if it’s already running and starts the
CheckPotentialMatches coroutine, storing a reference to it so it can be stopped at a later time.
private void StartCheckForPotentialMatches()
StopCheckForPotentialMatches();
CheckPotentialMatchesCoroutine = CheckPotentialMatches();
StartCoroutine(CheckPotentialMatchesCoroutine);
if (AnimatePotentialMatchesCoroutine != null)
StopCoroutine(AnimatePotentialMatchesCoroutine);
if (CheckPotentialMatchesCoroutine != null)
StopCoroutine(CheckPotentialMatchesCoroutine);
ResetOpacityOnPotentialMatches();
The GetBonusFromType method will return the bonus prefab that corresponds to a normal candy
type. For example, if the parameter type is a blue candy, it will return the blue bonus prefab.
if (item.GetComponent<Shape>().Type.Contains(color))
return item;
The RemoveFromScene method creates a new explosion, sets it to be destroyed after a specified
amount of seconds and destroys the candy which is passed as a parameter. This method makes
for a nice “disappear with a bang” effect!
Destroy(newExplosion, Constants.ExplosionDuration);
Destroy(item);
The CreateNewCandyInSpecificColumns takes the columns that have missing candy (null
values) as a parameter. For each column
var go = GetRandomCandy();
as GameObject;
newCandy.GetComponent<Shape>().Assign(go.GetComponent<Shape>().Type, item.Row,
item.Column);
newCandyInfo.AddCandy(newCandy);
}
return newCandyInfo;
– creates a new bonus (copied from the prefab) based on the candy type given as parameter
as GameObject;
BonusShape.Bonus |= BonusType.DestroyWholeRowColumn;
}
The Update method is split into two parts, each one handling a different state.
In the none/initial/idle state, game checks if the user has touched a candy. If this happens, the
game transitions to the SelectionStarted page.
void Update()
if (ShowDebugInfo)
DebugText.text = DebugUtilities.GetArrayContents(shapes);
if (state == GameState.None)
if (Input.GetMouseButtonDown(0))
hitGo = hit.collider.gameObject;
state = GameState.SelectionStarted;
}
}
– we get a reference of the second GameObject (the second part of the swap operation)
– if user dragged diagonally or very quickly (skipped a GameObject), state changes to idle/none
– else, we transition to the animating state, fix the sorting layer of the two GameObjects and
initialize the FindMatchesAndCollapse coroutine, to detect potential matches as a result of the
swap and proceed accordingly
//user dragged
if (Input.GetMouseButton(0))
StopCheckForPotentialMatches();
//if the two shapes are diagonally aligned (different row and column), just return
if (!Utilities.AreVerticalOrHorizontalNeighbors(hitGo.GetComponent<Shape>(),
hit.collider.gameObject.GetComponent<Shape>()))
state = GameState.None;
else
state = GameState.Animating;
FixSortingLayer(hitGo, hit.collider.gameObject);
StartCoroutine(FindMatchesAndCollapse(hit));
The FindMatchesAndCollapse method is a big one, we’ll split it into smaller parts to property
dissect it.
At the beginning, the method swaps and moves the two candies. Eventually, it gets the matches
(matched candies) around the two candies. If they are less than three, then the swap is undone.
Otherwise, we hold a boolean variable to indicate that a bonus will be created if
shapes.Swap(hitGo, hitGo2);
hitGo.transform.positionTo(Constants.AnimationDuration, hitGo2.transform.position);
hitGo2.transform.positionTo(Constants.AnimationDuration, hitGo.transform.position);
.Union(hitGo2matchesInfo.MatchedCandy).Distinct();
//if user's swap didn't create at least a 3-match, undo their swap
hitGo.transform.positionTo(Constants.AnimationDuration, hitGo2.transform.position);
hitGo2.transform.positionTo(Constants.AnimationDuration, hitGo.transform.position);
shapes.UndoSwap();
//if more than 3 matches and no Bonus is contained in the line, we will award a new Bonus
!BonusTypeUtilities.ContainsDestroyWholeRowColumn(hitGomatchesInfo.BonusesContained) &&
!BonusTypeUtilities.ContainsDestroyWholeRowColumn(hitGo2matchesInfo.BonusesContained);
Afterwards, if the addBonus variable is equal to true, we get a reference to the GameObject that
is part of the match of four. We create a temporary Shape (hitGoCache) to store the necessary
details (type, row, column) of this GameObject.
if (addBonus)
hitGoCache = sameTypeGo.GetComponent<Shape>();
}
If the total matches are more than three, a while loop starts. There, the score is increased and the
matches are removed from the array and destroyed from the scene. If we have to add a bonus
candy, we create a bonus GameObject. The addBonus boolean is set to false, so that the bonus
can be added only in the first run of the while loop. After that, we get the indices of the columns
that have null/empty items (have had matches destroyed).
int timesRun = 1;
//increase score
IncreaseScore((totalMatches.Count() - 2) * Constants.Match3Score);
if (timesRun >= 2)
IncreaseScore(Constants.SubsequentMatchScore);
soundManager.PlayCrincle();
shapes.Remove(item);
RemoveFromScene(item);
CreateBonus(hitGoCache);
addBonus = false;
We continue by collapsing the candy in these columns, creating new candy in them and
calculating the max distance needed for animations. These animations are executed and then, we
again check for new matches (after candies have collapsed and new candies have been created).
We continue the while loop, doing the same stuff.
Eventually, in a subsequent run of the while loop, the matches encountered are less than three.
We exit the loop, transition to the none/idle state and run the method that checks for potential
matches (as hint for the user).
MoveAndAnimate(newCandyInfo.AlteredCandy, maxDistance);
MoveAndAnimate(collapsedCandyInfo.AlteredCandy, maxDistance);
//will wait for both of the above animations
totalMatches = shapes.GetMatches(collapsedCandyInfo.AlteredCandy).
Union(shapes.GetMatches(newCandyInfo.AlteredCandy)).Distinct();
timesRun++;
state = GameState.None;
StartCheckForPotentialMatches();
Debugging
During the development of the game, there was the need to test specific scenarios. E.g. can we
test multiple collapses at the same time? Can we easily get a match of five to see the algorithm’s
behavior? As you saw, the algorithm is pretty random so we couldn’t easily test such scenarios.
This is the reason we developed a way to load custom levels. Take a look at the level.txt file,
found in the Resources folder.
The pipe character (|) is used to separate the items in the same row, the new line character (n)
acts as a row separator and blanks are ignored (trimmed). The candies that are created
correspond to the defined color. If there is a “_B” at the end of the color, then the respective
bonus candy is created.
In the DebugUtilities file there is a static method to load this file into a two dimensional string
array.
return shapes;
if (tokens.Count() == 1)
if (item.GetComponent<Shape>().Type.Contains(tokens[0].Trim()))
return item;
{
foreach (var item in BonusPrefabs)
if (item.name.Contains(tokens[0].Trim()))
return item;
InitializeVariables();
if (shapes != null)
DestroyAllCandy();
SetupSpawnPositions();
We saw that there are two buttons on our scene. The “Restart” button calls the
IntializeCandyAndSpawnPositions method whereas the “Premade level” calls the
InitializeCandyAndSpawnPositionsFromPremadeLevel method.
Moreover, since we use Visual Studio for our development, we saved invaluable time though the
use of Visual Studio Tools for Unity that allow for easy integration of Unity and Visual Studio
plus setting breakpoints and debugging. Highly recommended!
The end
Game is ready for all platforms, including mouse and touch input. Here is a screenshot of the
game running in Windows Phone 8.1 emulator (512 MB devices are supported!). The “Premade
level” button needs, of course, removal for production use.
Thanks for reading this! Hope it’s helpful for your next game. As always, you can try the
game here and find the source code here on GitHub.
If you are new to Unity, check out a cool intro video series here. For instructions on how to
deploy your existing game onto Windows Store/Phone, check out the Microsoft Virtual
Academy video here: http://www.microsoftvirtualacademy.com/training-courses/porting-unity-
games-to-windows-store-and-windows-phone
Share this:
Twitter
Facebook
Loading...
Post navigation
PREVIOUS POST
Playing music on netduino via a web browser
NEXT POST
My articles featured on Gamasutra!
1. chocolatekiss says:
JANUARY 9, 2016 AT 5:43 AM
Hello! Love your tutorial. Will the code work even if we only use MonoDevelop?
Like
REPLY
dgkanatsios says:
JANUARY 9, 2016 AT 10:38 AM
Yes, of course. In the end, Unity will compile and run the code, the editor is just a tool to
help you write your scripts.
Like
REPLY
2. Building the 2048 game in Unity via C# and Visual Studio – Dimitris-Ilias
Gkanatsios says:
Like
REPLY
3. Can a Raspberry Pi 2 with Windows 10 IoT Core run a game made in Unity? –
Dimitris-Ilias Gkanatsios says:
[…] to jump into the mediocre performance conclusion were two pretty simple 2D games I’ve
built; the match-3 game and the puzzle one. Frame rate was 2-3 frames per second, making the
performance totally […]
Like
REPLY
4. Shad says:
FEBRUARY 1, 2016 AT 1:50 AM
Thank you for such a wonderful tutorial. Seeing a really well made Match-3 really helped me
think about the deeper issues of this kind of project.
5. shadract says:
FEBRUARY 1, 2016 AT 1:51 AM
Thank you for doing such a wonderful tutorial! Seeing such a well made match-3 example really
helps me to think about the deeper issues of such a project.
6. psxbrasil says:
FEBRUARY 23, 2016 AT 1:46 PM
Hi, great tutorial! I want to know if I can use this source code as a base to create my own match
3 game, I plan to create more functions to the game. I ask that because I want to sell the source
code to other developers? Do you give me permission to do that?
Regards
Like
REPLY
dgkanatsios says:
FEBRUARY 23, 2016 AT 1:59 PM
You can do whatever you wish, good luck! Let me know if you find any bugs :)
Like
REPLY
7. psxbrasil says:
FEBRUARY 24, 2016 AT 12:04 AM
Hi, thanks dgkanatsios
Like
REPLY
8. Marry says:
MARCH 21, 2016 AT 11:00 AM
can u plz provide code…u give link but it’s didn’t work…:(
dgkanatsios says:
MARCH 21, 2016 AT 11:10 AM
Hi, what link is not working? Links to my GitHub profile do work
correctly: https://github.com/dgkanatsios/matchthreegame
Like
REPLY
9. Marry says:
MARCH 21, 2016 AT 11:38 AM
thanku dgkanatsios:):) thanku so much..:):)
Like
REPLY
Like
REPLY
dgkanatsios says:
MARCH 22, 2016 AT 5:47 AM
Of course! A link to this blog post would be great, but not obligatory. Also, feel free to let
me know about the game as I’d love to share and play it!
Like
REPLY
Like
REPLY
dgkanatsios says:
JUNE 8, 2016 AT 3:57 PM
Can you point me to the Disco Panda game? Can’t find it
Like
REPLY
Tim Cooley says:
AUGUST 24, 2016 AT 9:09 PM
it is like best fiends, that should be easier to find.
Like
dgkanatsios says:
AUGUST 25, 2016 AT 12:47 PM
So, the mechanism in Best Fiends is as follows, from what I can tell. User starts dragging
her finger from one gem to one nearby (vertically, horizontally or diagonally). You push
the initial get type to a stack. On the subsequent gems, if they are the same type as the
original one, you continue to add them to the stack. Upon the end of the drag, you can
check if this stack contains more than 3 items. If this is the case, you remove them from
the game board and new gems fall from the top (as in my match three game tutorial).
Is this clear enough for you?
Like
12. Niel Vil says:
JUNE 24, 2016 AT 10:32 PM
Match 3 games are incredibly complex to learn , I might just buy a match 3 game kit on the unity
store. This project is great however for learning programing in C#. Thanks.
Like
REPLY
dgkanatsios says:
JUNE 24, 2016 AT 10:53 PM
Thanks for the kind words :)
Like
REPLY
13. Ewolf says:
JULY 18, 2016 AT 8:23 AM
uhmmm sir dgk what version of unity did u use for making this project?
Like
REPLY
Like
REPLY
dgkanatsios says:
JULY 18, 2016 AT 8:34 AM
It was created on 4.6, have tested it with 5.2. Thanks!
Like
REPLY
15. westerbuins says:
AUGUST 24, 2016 AT 6:15 PM
Hi there, i was wondering how would you go about adding another special candy in? i’ve taken
your project and adjusted it to include a new star that the user gets for matching 5 (it can be used
with any type to remove all of that type), but my problem is that at random times the game will
break.
Like
REPLY
dgkanatsios says:
AUGUST 25, 2016 AT 12:50 PM
The question is how to do match 5, correct? Here is the part that I’m getting all of the candy
matches in an array. You can easily check if this array has more than or equal to 5 items and
do your stuff!
https://github.com/dgkanatsios/MatchThreeGame/blob/master/Assets/Scripts/ShapesManage
r.cs#L268
Hope that helps,
Dimitris
Like
REPLY
westerbuins says:
AUGUST 25, 2016 AT 3:14 PM
Hi Dimitris,
if you could take a look at my repo and give some help that would be appreciated. I have
the 5 match working, but it randomly bugs out with the following message:
Any help would be appreciated as i cant seem to wrap my head around it.
Cheers,
Like
westerbuins says:
AUGUST 25, 2016 AT 3:14 PM
forgot the repositry link! https://github.com/Westerveld/starRush
Like
dgkanatsios says:
AUGUST 25, 2016 AT 3:25 PM
Have you tried using Visual Studio tools for Unity to debug your code? It could be
proven really useful in finding why this exception is thrown.
https://unity3d.com/learn/tutorials/topics/scripting/debugging-unity-games-visual-studio
Like
westerbuins says:
AUGUST 26, 2016 AT 11:37 AM
I’ve tried using the debug method within visual studio, i know where the code breaks,
but i can’t seem to find a work around. any ideas? (its within shapesarray line 227)
Like
16. dgkanatsios says:
AUGUST 26, 2016 AT 11:53 AM
This line, right?
if (shapes [shape.Row, column].GetComponent ().IsSameType (shape)) {
well, hard to say what is null there without debugging. I would suggest you insert a breakpoint
and find the exact variable which is null, this would help you understand what is going on.
Like
REPLY
westerbuins says:
AUGUST 26, 2016 AT 11:57 AM
Okay, will give that a go in a bit, thanks for your quick replies!
Like
REPLY
17. Winston says:
SEPTEMBER 9, 2016 AT 10:35 AM
There is a bug in InitializeCandyAndSpawnPositions method. When checking the two previous
vertical are of the same type, you may generate a type which is the same as the two previous
horizontal type.
Like
REPLY
dgkanatsios says:
SEPTEMBER 9, 2016 AT 10:37 AM
Can you please issue a GitHub issue (if you can also submit a fix, that would be great!)?
Like
REPLY
Like
REPLY
dgkanatsios says:
SEPTEMBER 24, 2016 AT 4:08 PM
When the player does a match and the game increases the score, you can compare it to a
target one and act accordingly. Hope I understood your question correctly!
Like
REPLY
19. budi says:
NOVEMBER 11, 2016 AT 8:25 AM
hai dgkanatsios, i have a problem when i try to change the prefabs, it gives me an error like this :
InvalidOperationException: Operation is not valid due to the current state of the object
System.Linq.Enumerable.Single[GameObject] (IEnumerable`1 source, System.Func`2 predicate,
Fallback fallback)
System.Linq.Enumerable.Single[GameObject] (IEnumerable`1 source)
ShapesManager.InitializeTypesOnPrefabShapesAndBonuses () (at
Assets/Scripts/ShapesManager.cs:67)
ShapesManager.Start () (at Assets/Scripts/ShapesManager.cs:45)
im sorry but i still learning c# and newbie to unity. Any help would be appreciated, thank you
Like
REPLY
dgkanatsios says:
NOVEMBER 11, 2016 AT 11:58 AM
Single() throws this, probably because there are more than one elements on the list.
Like
REPLY
budi says:
NOVEMBER 11, 2016 AT 5:39 PM
thanks for you response. i think i need to learn more, thanks anyway
Like
20. michel says:
NOVEMBER 19, 2016 AT 9:01 AM
Hi dimitris I’m completely new to game development so maybe my question is stupid but i
wonder how i can make level animation. You know the animation between each level that we
can see in many game for example a character progressing on a road every times you achieve a
level http://www.gamasutra.com/db_area/images/blog/262755/Title.jpg
Like
REPLY
dgkanatsios says:
NOVEMBER 19, 2016 AT 9:54 AM
Hi, you could use the GoKit library (or any other animation library you like) to animate a
GameObject from one position to another.
Like
REPLY
Lukos says:
JANUARY 12, 2017 AT 10:51 PM
Hi, can u tell, how I can add all prefabs to canvas? I want change background image and
etc but prefabs haven’t parents for this. I tried create canvas and used setParent(); in
insilization but it’s not working.Thanks for your answer
Like
dgkanatsios says:
FEBRUARY 11, 2017 AT 2:13 AM
Hi, you can place an empty GameObject on the screen in the position you wish and add
an image to it programmatically.
Like
21. michel says:
NOVEMBER 19, 2016 AT 1:45 PM
thank you for your response
Like
REPLY
22. lenten says:
FEBRUARY 27, 2017 AT 9:45 AM
Hi, great project for learning. I’m newbie. I want to add one more lightning effect not just
random explosion when we has bonus which kill whole row or column. How can i do that? took
me a week but still can’t figure it out. Something like:
if (bonus in totalmatches) {
do animation at that position();
shapes.remove(item);
removefromscene(item);
}
Like
REPLY
dgkanatsios says:
MARCH 1, 2017 AT 2:29 PM
Are you starting an animation there? If yes, you could yield return new
WaitForSeconds(animation_duration) there before removing the shapes.
Like
REPLY
23. lenten says:
MARCH 1, 2017 AT 5:38 PM
Well, it’s not just something like that, I know how to do animation or call an yield. I mean a real
code actually work in your project. How do i call an If like that? How do i figure out when bonus
kill a column to start animation from bottom or start from left when it kill a row? I played many
match 3 games on store they have a lot of effects, make their games more exciting. Thanks for
replying, i think i will do it myself, try & learn more.
Like
REPLY
dgkanatsios says:
MARCH 3, 2017 AT 9:29 PM
Have you had any luck? All magic happens in this
function: https://github.com/dgkanatsios/MatchThreeGame/blob/master/Assets/Scripts/Const
ants.cs#L9 I should have made it smaller, to be honest, but anyway you can see what
happens when a bonus is to be added.
Like
REPLY
24. Janny says:
MARCH 3, 2017 AT 9:23 PM
Hi just want to know how to make the gridsize declared because I want to make a new level of it
with different grid sizes. I don’t know how to make another set cause the Gridsize is constant.
Like
REPLY
dgkanatsios says:
MARCH 3, 2017 AT 9:26 PM
Hi, have you tried changing the values on Constants.cs
file? https://github.com/dgkanatsios/MatchThreeGame/blob/master/Assets/Scripts/Constants.
cs#L9
Like
REPLY
Janny says:
MARCH 3, 2017 AT 9:31 PM
Yes but when I did all of the 5 scenes GridSize I created by following your tutorial will
be equal. I just want it to be different for example
Level1 8×8
Level2 9×8
Level3 9×9
Level2 8×10
Level3 10×10
Like
dgkanatsios says:
MARCH 3, 2017 AT 9:32 PM
Ah OK. In this case, I would have different variables for each level.
Like
Janny says:
MARCH 3, 2017 AT 9:32 PM
Don’t mind the level names
Like
Janny says:
MARCH 3, 2017 AT 9:34 PM
Can you please give me a sample sir?
Like
dgkanatsios says:
MARCH 3, 2017 AT 9:39 PM
Well, the most simple way, if you are sure you will have a certain amount of levels is
load them into an array. Hence, for 3 levels you could do
public static readonly int[] Rows = {4,5,6}; //level 1: 4 rows, level 2: 5 rows etc.
public static readonly int[] Columns = {8,9,10}; //level 1: 8 rows, level 2: 9 rows etc.
And then,
in https://github.com/dgkanatsios/MatchThreeGame/blob/master/Assets/Scripts/Shapes
Manager.cs#L109
you could write Constants.Rows[0] for level 1, Constants.Rows[1] for level 2 etc.
Hope this helps!
Like
Janny says:
MARCH 3, 2017 AT 9:46 PM
Will that work in a multiple scenes?
Like
dgkanatsios says:
MARCH 3, 2017 AT 9:47 PM
well, what you have to do is select the proper value in the array depending on the scene
you are!
Like
Janny says:
MARCH 3, 2017 AT 9:51 PM
Can you help me sir on how to do that? That will help me a lot sir.
Like
Janny says:
MARCH 3, 2017 AT 10:04 PM
public static readonly int[] Rows = {4,5,6}; //level 1: 4 rows, level 2: 5 rows etc.
public static readonly int[] Columns = {8,9,10}; //level 1: 8 rows, level 2: 9 rows etc.
And then,
in https://github.com/dgkanatsios/MatchThreeGame/blob/master/Assets/Scripts/Shapes
Manager.cs#L109
you could write Constants.Rows[0] for level 1, Constants.Rows[1] for level 2 etc.
I tried this method but it gives me a lot of error in ShapesArray.cs, DebugUtilities.cs, and
in Utilities.cs
Like
dgkanatsios says:
MARCH 4, 2017 AT 12:53 PM
Yup, you need to make some changes in the code, depending on how you have built your
level/scene flow.
Like
25. Nelson says:
MARCH 31, 2017 AT 1:26 PM
Mr. Dgkanatsios
I am recently learning how to make match3 game in Unity for my first commercial game app.
Your tutorial is very easy to understand, and I found that you code base is really good for me to
start coding and build new rule on top. I also understand that there are a lot of developers was
ask you the same question about if it is fine to use your match3 source code for development.
However, i just want to inquire you direct, to let me use and modify your source code freely
without charges and legal issue.
Of course, It would be very much my pleasure to info you when the game is released and have
you the enjoy it.
Like
REPLY
dgkanatsios says:
MARCH 31, 2017 AT 1:28 PM
Thanks for the kind worlds. Feel free to use the code, check the license on the GitHub
repository: https://github.com/dgkanatsios/MatchThreeGame/blob/master/License.md
Like
REPLY
Nelson says:
MARCH 31, 2017 AT 3:25 PM
Mr. Dgkanatsios, thanks for your speed reply. (just to double check, you wrote “feel free
to SUE the code”, you guess you meant “feel free to USE the code”, right?), so for being
so careful!
you have a great day!
Cheers!
Like
dgkanatsios says:
MARCH 31, 2017 AT 3:27 PM
Haha, correct, fixed now :)
Like
26. Nelson says:
MARCH 31, 2017 AT 3:32 PM
wow. so cool, I did not know that you can actually fix the previous message?! that’s very cool. (I
am not a tech person!!! sorry!!!). I fully understood! You have a great day, Mr. Dgkanatsios.
Like
REPLY
27. Shahzaib says:
APRIL 5, 2017 AT 10:30 AM
Thanks for the Game i made a Flag Destroy with Match 3 i also added Admob advertisement its
working fine for me but after 5 to 10 Min its disappear what will be the problem i am still finding
Like
REPLY
28. Armyn says:
APRIL 7, 2017 AT 11:34 PM
Hi Mr. Gkanatsios, about the Shape class, you said;
“It contains … a constructor that initializes the bonus enumeration …”
“Since Shape is a MonoBehaviour that is attached to our prefabs (as we’ll see later), we cannot
use a constructor to initialize it.”
Like
REPLY
dgkanatsios says:
APRIL 9, 2017 AT 9:57 AM
Hi, yup, we’re not calling the Shape constructor anywhere. This is (probably) called
internally by Unity, so the one line it contains is executed at that time. More proper way
would have been to either implement this line at Start() or Awake(), or just initialize the field
in the class (public BonusType Bonus = BonusType.None). Thanks!
Like
REPLY
29. Nelson says:
APRIL 8, 2017 AT 5:35 AM
Mr. Dgkanatsios
I was trying to put a new gameplay logic into the system, that is eventually base on many match
patterns. But first, I want to keep it simple and step by step. That’s of course, it is something I
should figure it out by myself, but I was wondering if there is way to find out the matches is a
vertical or horizontal match? I was looking into the MatchesInfo class, but it seems it does not
doing any bookkeeping on directional information. I think it is a right class to extend the
functionalities. If you have any good advice or pointers, it would be very much appreciated!
thank you sir!
Like
REPLY
dgkanatsios says:
APRIL 9, 2017 AT 9:58 AM
Check here for the GetMatchesHorizontally
method https://github.com/dgkanatsios/MatchThreeGame/blob/master/Assets/Scripts/Shapes
Array.cs#L177
You’ll also see the GetMatchesVertically method nearby. thanks!
Like
REPLY
30. Nelson says:
APRIL 9, 2017 AT 5:30 PM
Thank you so much for your pointers! it works perfectly. :)
Like
REPLY
31. TheSuperhero says:
APRIL 30, 2017 AT 4:12 AM
Hello dgkanatsios,
Thanks for the tutorial, it was good that you even provided the code.
I want to know is there a possibility to add obstacles that do not move and the candy or shapes
move around the obstacle. like how we see in many match 3 games. If yes can you guide me how
to add them to the project.
Like
REPLY
dgkanatsios says:
MAY 2, 2017 AT 8:28 PM
Hi and thanks for the kind words. So, what you ask can be done (of course) but it can be a
little tricky. There are two basic parts that obstacle-like functionality should be included.
1. At the candy comparison. When you compare candies either vertically or horizontally, you
should stop when you encounter an obstacle.
2. At the candy drop, when the user makes a successful match. Candy should *not* drop
vertically when an obstacle is directly below them.
Let me know if you have any more questions!
Like
REPLY
32. Paulo Edward John says:
JUNE 10, 2017 AT 3:15 AM
thanks for this wonderful tutorial!
guys how to add levels??
Like
REPLY
dgkanatsios says:
JUNE 10, 2017 AT 8:39 AM
Thanks! You could replicate the one that is demonstrated here
Like
REPLY
33. Manuel says:
JUNE 26, 2017 AT 9:34 PM
Hello dgkanatsios, I have a quick question concerning the Shapes Manager. When I import the
code as a new asset in Unity 5.6.1 and open it in visual studio I encounter 5 errors. All five are
errors are CS1061 errors and state “error CS1061: Type `UnityEngine.Transform’ does not
contain a definition for `positionTo’ and no extension method `positionTo’ of type
`UnityEngine.Transform’ could be found. Are you missing an assembly reference?” I was
wondering what the problem is, and how I can fix it in order for the game to work.
-Thanks
Like
REPLY
dgkanatsios says:
JUNE 26, 2017 AT 9:52 PM
positionTo is implemented in
GoKit https://github.com/dgkanatsios/MatchThreeGame/blob/586054556ef8fb093d50faab18
c30d71842daafc/Assets/Plugins/GoKit/extensions/GoKitTweenExtensions.cs
Like
REPLY
34. xindaar says:
DECEMBER 4, 2017 AT 1:14 AM
Hello and thanks for the match3 game code.
My problem is when there are no matches left on board (possible whit a 4×4 or 5×5 game board),
a new random board is not created and the game becomes stuck.
Can you show a workaround for this?
Like
REPLY
dgkanatsios says:
DECEMBER 4, 2017 AT 10:01 AM
Thanks for the comment. Yeah, I tried to make the tutorial as simple as possible (even
though it became much bigger than I originally imagined!). Anyway, what I would suggest
you could do is use the Utilities.GetPotentialMatches method. This method returns null if it
cannot find any matches at all. So you could use this method to check for null, and, if this is
the case, take the user to a next level or restart the level or show a “congrats” message or
whatever you like. Hope this helps, Dimitris
Like
REPLY
35. URLS says:
Like
REPLY
36. Christian says:
DECEMBER 30, 2017 AT 3:05 PM
@dgkanatsios: can you tell me how much time you’ve invested to create this code?
Like
REPLY
dgkanatsios says:
DECEMBER 30, 2017 AT 3:29 PM
Not sure, since it’s been a couple of years since I wrote it. I would guess two weeks part-
time, tops
Like
REPLY
37. Doi says:
JULY 26, 2018 AT 5:15 AM
what if I want to get matches diagonally?
Like
REPLY
dgkanatsios says:
JULY 26, 2018 AT 10:20 AM
You could create another method that does exactly that. It would be similar to the
GetMatchesHorizontally and GetMatchesVertically methods
Like
REPLY
38. BPG says:
SEPTEMBER 25, 2018 AT 10:00 AM
Hello, thanks for the tutorial!
I was wondering how I would go about referencing specific colors of the candy to increase a
score for each. For example clearing blue candies would increase the score for “blue”, and pink
candies increase “pink”. How can I “check” for the color and increase the score from there?
Thank you!
Like
REPLY
dgkanatsios says:
SEPTEMBER 25, 2018 AT 10:53 AM
Thanks for the comment. We’re already checking the Color via the “IsSameType” method
on the “Shape” class. That’s where we’re comparing candies to see if they are of the same
color.
To solve your problem, you could have an array (or better, a Dictionary) of scores where
each entry in the Dictionary would hold the score for the specified color.
Like
REPLY
BPG says:
DECEMBER 19, 2018 AT 6:47 AM
Thanks for the solution!
I’m now trying to create a new bonus that clears all candies of the same color. I’m
following the same steps of the previous bonus but I’m unsure of how to create an
IEnumerable like GetEntireColumn, albeit for getting all the candy of the color with
which the bonus is swapped.
Like
39. Week 6 – (18/02/19 – 24/02/19) – task 4 – Gameplay and Prototyping says:
40. Jacques says:
APRIL 14, 2019 AT 9:16 AM
Even though I’m no hardcore coder your efficient code and detailed description is, as far as I
have seen, the best example for making unique match 3 games. After trying out several other
match 3 tutorials/codes this is still the most streamlined. Although it has taken me nearly a week
to get to grips with how your routines talk to each other, I have managed to (finally) get a
reshuffle routine up and running as well as 2 different bonus styles (adjacent and same color) . I
just have a couple of questions though, f#1 is probably easy but I’m a noob with gokit, i use
scaleTo (in place of your color tinting for hint tiles, but how to make sure all game objects no
longer tween in case I need to restart a puzzleboard? GoTween does capture the error with a
yellow warning icon but any easier way of detecting which gameobjects are scaling with
goTween? #2 has me pulling out my hair for the last week, how on earth do we make it so that
new incoming tiles will also create bonus tiles for match 4/5 instead of just destroying them?
Appreciate any help and curious to know what are you doing these days? :)
Like
REPLY
dgkanatsios says:
OCTOBER 2, 2020 AT 6:35 PM
wow, thank you! Yes, I’d love a PR, also edit the blog post to point to your website!
Like
REPLY
Leave a Reply
SOCIAL
Twitter
GitHub
LinkedIn
RECENT POSTS
Dedicated Game Server Scaling on Azure Kubernetes Service – OpenConf presentation
ARCHIVES
Archives
Back to top