Sponsored By

Implementing Domino chains in Unity Android game, The Domingos

This post covers how the domino chain drawing algorithm was implemented in the unity android game - The Domingos

satish chandra, Blogger

February 25, 2014

10 Min Read
Game Developer logo in a gray background | Game Developer



 

One of the things I will glee in content when I look at one of the Flappy Bird clones is the fact that those clone bots who made those games will never be able to clone Domingos, even if it made thousands of dollars a day (which it isn’t). Granted, a talented developer with decent math knowledge should be able to clone the mechanic, but I wager those guys will have better roles to play than that of a greedy little copycat.

But then I realised that I shouldn’t worry about copycats and lose out on sharing (which I should do more often) some knowledge with the game dev. Community. The Domingos is a level based game anyway. So, here we go:

It’s recommended that you play the game (The Domingos, link: http://goo.gl/aJUf85) to actually experience the drawing mechanic. If not, take a look at this video.
 


To get that domino effect, one has to carefully place the dominos one after another within a certain distance.  In real world, that manual work is hard and needs meticulous concentration. But when it comes to doing the same in a game – it will be (and has to be) a different story. No player/user has the patience to place each domino carefully one after another like in the real world. Also, given the limited screen space when it comes to mobile devices- micro management of position and rotation of dominos can’t be expected from the user.

So, these are our objectives:
1. Creation – the player has to be able to create the chain of dominos with minimal effort.
2. Continuation – the player should be able to add more dominoes to an existing chain of dominos by creating new chains at the end with ease.
3. Propagation – the player’s chain of dominos must propagate the domino effect without any breakage no matter how zigzag he/she draws.

Since Unity is multi-platform, I decide to make the same drawing code, multi-platform. This would mean that the mouse input (for web and pc/mac devices) and touch input (for mobiles/tablets) has to be merged into one stream before we call the domino chain drawing code.
So I am populating these variables with the respective data based on the device.

//touchPoint will be Input.mousePositionon on Windows/mac devices, and //Input.touches[drawingTouch].position on touch devices. What is the drawingTouch ?
//It’s explained further down
var touchPoint:Vector3;

// fingerPressed will be true if  Input.GetButtonDown ("Fire1")== true on Windows/Mac
//devices, and if Input.touches[drawingTouch].phase== TouchPhase.Began on touch devices
var fingerPressed:boolean;

// fingerPressed will be true if  Input. GetButtonUp ("Fire1")== true on Windows/Mac devices,
//and if Input.touches[drawingTouch].phase== TouchPhase. Ended(or TouchPhase.
//Cancelled) on touch devices
var fingerReleased:boolean;

//touchPointGround is the point on the ground where the finger is touching. This is calculated
//by raycasting from the camera’s touchpoint
Physics.Raycast (mainCam.ScreenPointToRay(touchPoint), hit, Mathf.Infinity);
var touchPointGround:Vector3 = hit.point;

At this point, you might wonder- “Why not use Input.mousePosition irrespective of the device, Unity simulates and populates mouse input data on touch devices anyway.”
True, while this feature is extremely handy while prototyping, it will cripple you when you add more features into your game. When you give an input of multiple touches, unity calculates the mean of all the touches and uses it to fill the simulated mouse input data – the result is an unpredictable mess.

Coming to the drawingTouch variable I had liberally used in the above declarations, here’s the explanation:  Input.touchCount gives you the number of current touches on the screen. Out of these touches, some touches could be touching the HUD buttons or other HUD inputs (In Domingos it’s a joystick to move around the map. It will come in Act 2) that shouldn’t interfere with the drawing code. So, out of Input.touches array, we eliminate the touches that are going for the HUD elements, and select the first touch that is ‘touching’ the ground where the dominos can be created and make that specific touch the ‘drawingTouch.’
For readability’s sake, I won’t post the code for the above but if anyone wants to take a look, ask away, that’s what the comments section is for :)

 
Finally, coming to the code that draws the domino chains:

//if finger is pressed, and if we are not drawing dominos, start drawing dominos
//drawingDominos is a Boolean class variable
if(fingerPressed && drawingDominos==false )
{
       drawingDominos=true;
//lastDomino is a class variable of type GameObject  which stores the reference to the            
//last domino that was created in the chain of dominos
       lastDomino=null;
}

if(drawingDominos )
{
//if lastDomino is null, create the first domino in the sequence of dominos
       if(lastDomino==null)
       {
//GetDominosNearby returns the number of dominos near touchPointGround. We need            
//This to meet our second objective – “Continuation”
//The function also assigns the reference to the closest domino to the reference variable we pass
       var closestDomino:GameObject;
       var numberOfNearbyDominos:int= 
       GetDominosNearby(touchPointGround,out closestDomino );
       If(numberOfNearbyDominos==1)
       {
//here, we are making sure that we create the new domino at an 
//optimal distance, represented by idleDominoDistance.
              distanceBetweenCreations=
              Vector3.Distance(closestDomino.transform.position ,touchPointGround);
//here, we are multipling idleDominoDistance with the unit vector of  the direction we are drawing
              creationTranslate=idleDominoDistance*
              (touchPointGround- closestDomino.transform.position)/distanceBetweenCreations;
              createPosition=closestDomino.transform.position+creationTranslate;
//CreateDominoAt function creates a domino at a given point and returns a reference to          
//the newly created domino. The function also checks(using Physics.CapsuleCast) if a      
//domino created there will not collide with any other game objects and creates issues.            
//if a domino can’t be created, the function will return null
              lastDomino = CreateDominoAt(createPosition);
        }
       else
       {
              lastDomino = CreateDominoAt(touchPointGround);
       }

       If(numberOfNearbyDominos>=1 && lastDomino!=null)
       {
//if we have more than one domino close by, we make the new domino turn towards
//the closest domino
              lastDomino.transform.LookAt(closestDomino.transform.position);
       }

       }
//if lastDomino is not null, it means that we already created the first domino in the chain
//and we have to align the new domino with respective to lastDomino, thus taking care of
//our first objective -"Creation' 
       else
       {
//this vector math is very similar to what we did before, except we are using lastDomino,
//instead of closestDomino
              distanceBetweenCreations=
              Vector3.Distance(lastDomino .transform.position ,touchPointGround);
              creationTranslate=idleDominoDistance*
              (touchPointGround- lastDomino .transform.position)/distanceBetweenCreations;
              createPosition=lastDomino .transform.position+creationTranslate;
 //creating the domino at the optimal position

               var tempDomino:GameObject = CreateDominoAt(createPosition);
//if the CreateDominoAt function returns null because a domino created at the point will
//collide with game objects in the level, we end the domino drawing sequence and set
//lastDomino to null
               if(tempDomino==null)

               {
               lastDomino=null;
               drawingDominos=false;

               }
//else align the new domino's rotation to look at last domino and update last Domino
//reference with new domino
              else
              {

               tempDomino.transform.LookAt(lastDomino.transform.position);
               tempDomino = lastDomino;

              }

       }
}

//and finally when the player releases the finger, we end the domino chain
if(fingerReleased && lastDomino!=null)) 
{
//here again, we find the closest domino to the lastDomino, and change its rotation to look at
//the closest domino. for readability’s sake i am not putting that code here. then...
drawingDominos = false;
lastDomino=null;
}

But we still have one objective to cover – "Propagation"
The only way we can ensure that the Domino effect propagate properly, is by drawing only when the angle between the new drawing direction and the old drawing direction is between -30 to +30 degrees(or -45 to +45). What is the new drawing direction? Its
touchPointGround - lastDomino.transform.position;
what is the old drawing direction?
lastDomino.transform.position - dominoBeforeLastDomino.transform.position
//so, if we make sure that the angle is within limits
if(Mathf.abs(Vector3.Angle(newDrawingDirection, oldDrawingDirection))<30)
{
//draw the new domino only if this is true
}
This will make sure that the domino sequence is created with a reasonably curvature and correct the zigzag inputs from the user into a curve that can propagate.
Needless to say, you have to merge this functionality with the big blocks of code above. For ease of understanding, i separated it from the original flow.

 

In closing, if you feel some part of the post as under explained, let me know.

Check out my old post on some of the optimization techniques we implemented in the Android port of Domingos here.


Try the Domingos over here: 
https://play.google.com/store/apps/details?id=com.teapotgames.thedomingos
You can try the bread winner of our studio, our debut game- Total Parking, over here:
https://play.google.com/store/apps/details?id=com.teapotgames.totalparking

Follow TeaPOT Games
Facebook: http://goo.gl/EpIULi
Twitter: http://goo.gl/VV5T4p
Google+: http://goo.gl/HXs7am
 

Read more about:

Featured Blogs
Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like