Hi all, I’ve stuck for a couple of days finding solution the problem, but seems to no avail.
Anyway I’ve been creating a match 3 system for a week right now, and have succeeded in creating the basic core, such as swapping tiles, check for match, destroy if there is any match 3 or greater, and inserting new blocks. But I cannot find the best method to match check the new inserted tiles.
I’ve succeeded creating match 3 system using playmaker before, but it is rather complicated to adjust, and its been a while since I fiddle with it. Now I’m starting to understand c#, I want to create it using c#.
does anyone have insight on how to do just that?
Here is my code, please excuse the spaghetti code, I’m a noob with c#, and have no programming background whatsoever:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PlayerInput : MonoBehaviour {
public LayerMask tilesLayer;
public GameObject indicator;
public float switchSpeed;
public float gravity = 0.5f;
public bool debugMode;
private GridManager gridManager;
private GameObject activeTile;
private GameObject secondTile;
private List<GameObject> matchPositionsX = new List<GameObject>();
private List<GameObject> matchPositionsY = new List<GameObject>();
private List<GameObject> matchPositionsX2 = new List<GameObject>();
private List<GameObject> matchPositionsY2 = new List<GameObject>();
private List<GameObject> tileFallList = new List<GameObject>();
private int gridWidth;
private int gridHeight;
private float posOffset;
private bool isMoving = false;
private bool checkAfterFall = false;
private int firstX = -1;
private int firstY = -1;
void Awake ()
{
//Store GridManager script reference to var gridManager, and the needed variable
gridManager = GetComponent<GridManager>();
gridWidth = gridManager.gridWidth;
gridHeight = gridManager.gridHeight;
posOffset = gridManager.posOffset;
}
void Update ()
{
if (Input.GetButtonDown("Fire1"))
{
if (activeTile == null ) //if there are no selected tile yet, select the first tile
SelectTile ();
else
//else move the neighbor-ing tile
AttemptMove ();
}
if (checkAfterFall)
{
StartCoroutine(CheckAfterFall(tileFallList, matchPositionsX, matchPositionsY));
}
if (Input.GetKeyDown(KeyCode.Escape))
{
Application.Quit();
}
}
//function to select the first tile
void SelectTile ()
{
Vector2 screenPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(screenPos, Vector2.zero, 50f, tilesLayer);
if (hit.collider != null)
{
activeTile = hit.collider.gameObject;
indicator.transform.position = activeTile.transform.position;
indicator.SetActive(true);
indicator.transform.parent = activeTile.transform;
if (debugMode)
print (activeTile);
}
}
//function to move a tile
void AttemptMove ()
{
Vector2 screenPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(screenPos, Vector2.zero, 50f, tilesLayer);
Vector2 activeTilePos;
Vector2 secondTilePos;
if (hit.collider != null)
{
secondTile = hit.collider.gameObject;
if (debugMode)
print (secondTile);
if (NeighborCheck (secondTile))
{
if (debugMode)
print (NeighborCheck (secondTile));
activeTilePos = gridManager.gridPos[(int)activeTile.transform.position.x, (int)activeTile.transform.position.y];
secondTilePos = gridManager.gridPos[(int)secondTile.transform.position.x, (int)secondTile.transform.position.y];
//Switch!
StartCoroutine(MoveTiles(activeTile, secondTile, switchSpeed, activeTilePos, secondTilePos, 0.05f, false));
//Switch grid id temporary to check match Possibilites using CheckMatches method
SwitchGrid(activeTilePos, secondTilePos);
//Check if CheckMatches found matches on the switched positions
if (CheckMatches(activeTilePos.x, secondTilePos.y, matchPositionsX, matchPositionsY, true) ||
CheckMatches(secondTilePos.x, activeTilePos.y, matchPositionsX2, matchPositionsY2, true))
{
//Make sure secondTile get checked again, incase the checkMatch activeTile resulting true
//because OR operand skip the second condition if the first one is true
CheckMatches(secondTilePos.x, activeTilePos.y, matchPositionsX2, matchPositionsY2, true);
//If found, destroy matched tiles --
//CheckMatches by running the public void from GridManager Script
StartCoroutine(DestroyMatches(matchPositionsX, matchPositionsY));
StartCoroutine(DestroyMatches(matchPositionsX2, matchPositionsY2));
StartCoroutine(CheckEmpty(tileFallList));
//Fill new Tiles
}
else
{
//Found no match, then switch back!
StartCoroutine(MoveTiles(secondTile, activeTile, switchSpeed, activeTilePos, secondTilePos, 0.05f, true));
//Switch grid id back from previous temporary modification
SwitchGrid(activeTilePos, secondTilePos);
}
}
activeTile = null;
secondTile = null;
}
}
bool NeighborCheck (GameObject objectToCheck)
{
int xDifference = (int) Mathf.Abs (activeTile.transform.position.x - objectToCheck.transform.position.x);
int yDifference = (int) Mathf.Abs (activeTile.transform.position.y - objectToCheck.transform.position.y);
if (xDifference + yDifference == 1)
return true;
else
return false;
}
void SwitchGrid(Vector3 obj1pos, Vector3 obj2pos)
{
GameObject activeTileID;
//Switch ID base on switched Tiles
activeTileID = gridManager.gridObj[(int)obj1pos.x, (int)obj1pos.y];
gridManager.gridObj[(int)obj1pos.x, (int)obj1pos.y] = gridManager.gridObj[(int)obj2pos.x, (int)obj2pos.y];
gridManager.gridObj[(int)obj2pos.x, (int)obj2pos.y] = activeTileID;
}
IEnumerator MoveTiles (GameObject obj1, GameObject obj2, float smooth, Vector3 obj1pos, Vector3 obj2pos, float snap, bool resetTile)
{
while (isMoving)
yield return null;
isMoving = true;
while (Vector3.Distance(obj1.transform.position, obj2pos) > snap)
{
obj1.transform.position = Vector3.Lerp (obj1.transform.position, obj2pos, smooth * Time.deltaTime);
obj2.transform.position = Vector3.Lerp (obj2.transform.position, obj1pos, smooth * Time.deltaTime);
yield return null;
}
//Make sure obj1 and obj2 switch pos at the end of lerp
obj1.transform.position = obj2pos;
obj2.transform.position = obj1pos;
indicator.SetActive(false);
indicator.transform.parent = null;
isMoving = false;
//reset both activeTile and secondTile variable
if (resetTile)
{
activeTile = null;
secondTile = null;
}
}
bool CheckMatches(float x1, float y1, List<GameObject> listX, List<GameObject> listY, bool clearList)
{
if (clearList)
{
listX.Clear();
listY.Clear();
}
string tileTag = gridManager.gridObj[(int)x1,(int)y1].tag;
GameObject currentObj;
if (debugMode)
print ("Check Match Tiles Tag: " + gridManager.gridObj[(int)x1,(int)y1].tag + " x:" + (int)x1 + " y:" + (int)y1);
//---HORIZONTAL CHECK
//Check to the Right after Swap
for (int x = (int)x1; x < gridWidth; x++)
{
int y = (int)y1;
currentObj = gridManager.gridObj[x,y];
if (currentObj.tag == tileTag || x == (int)x1)
{
if (!listX.Contains(currentObj))
listX.Add(currentObj);
} else
break;
}
//Check to the Left after Swap
for (int x = (int)x1; x >= 0; x--)
{
int y = (int)y1;
currentObj = gridManager.gridObj[x,y];
if (currentObj.tag == tileTag || x == (int)x1)
{
if (!listX.Contains(currentObj))
listX.Add(currentObj);
} else
break;
}
//----VERTICAL CHECK
//Check to the Top after Swap
for (int y = (int)y1; y < gridHeight; y++)
{
int x = (int)x1;
currentObj = gridManager.gridObj[x,y];
if (currentObj.tag == tileTag || y == (int)y1)
{
if (!listY.Contains(currentObj))
listY.Add(currentObj);
} else
break;
}
//Check to the Bottom after Swap
for (int y = (int)y1; y >= 0; y--)
{
int x = (int)x1;
currentObj = gridManager.gridObj[x,y];
if (currentObj.tag == tileTag || y == (int)y1)
{
if (!listY.Contains(currentObj))
listY.Add(currentObj);
} else
break;
}
if (listX.Count > 2 || listY.Count > 2)
return true;
else
{
listX.Clear();
listY.Clear();
return false;
}
}
IEnumerator DestroyMatches(List<GameObject> listX, List<GameObject> listY)
{
while (isMoving)
yield return null;
// Check if found Match more than 2
if (listX.Count > 2)
{
// Destroy gameObjects in listX
foreach (GameObject obj in listX)
{
if (debugMode)
print ("Tiles destroyed horizontally: " + obj.tag + " x:" + obj.transform.position.x + " y:" + obj.transform.position.y);
Destroy(obj, 0f);
}
}
if (listY.Count > 2)
{
// Destroy gameObjects in listY
foreach (GameObject obj in listY)
{
if (debugMode)
print ("Tiles destroyed vertically: " + obj.tag + " x:" + obj.transform.position.x + " y:" + obj.transform.position.y);
Destroy(obj, 0f);
if (debugMode)
print ("Destroy Executed at: " + Time.time);
}
}
listX.Clear();
listY.Clear();
}
IEnumerator CheckEmpty(List<GameObject> tilesFall)
{
while (isMoving)
yield return null;
Vector2 currentPos;
GameObject currentObj;
//Check all of the grid from index 0
for (int x = 0; x < gridWidth; x++)
{
List<GameObject> newTilesCol = new List<GameObject>();
for (int y = 0; y < gridHeight; y++)
{
//store the current grid position to a variable
currentPos = new Vector2 ((float)x + posOffset, (float)y + posOffset);
//Raycast to see if there is a tile
RaycastHit2D hit = Physics2D.Raycast(currentPos, Vector2.zero, 50f, tilesLayer);
if (hit.collider == null) //if raycast hit nothing, check all the way up
{
if (firstX == -1 && firstY == -1)
{
firstX = x;
firstY = y;
}
//declare emptySpaces variable to count the row with empty spaces
int emptySpaces = 0;
int fillSpaces = 0;
//Check vertically
for (int y2 = y; y2 < gridHeight; y2++)
{
currentPos = new Vector2 ((float)x + posOffset, (float)y2 + posOffset);
//get the current iteration X Y as vec2
currentObj = gridManager.gridObj[x,y2]; //store the current ID
RaycastHit2D hit2 = Physics2D.Raycast(currentPos, Vector2.zero, 50f, tilesLayer);
if (hit2.collider != null)
currentObj = hit2.collider.gameObject;
else
currentObj = null;
if (currentObj != null)
{
//switch grid ID base on the new position
int yNew = (int)(currentPos.y - (float)emptySpaces);
gridManager.gridObj[x,yNew] = currentObj;
//increment the fillspaces var, for getting y position later for the new tiles
fillSpaces++;
//Animate tiles with empty grid blow to fall (gravity effect)
StartCoroutine(TilesFall(currentObj, currentPos.y - (float)emptySpaces, gravity, 3));
tilesFall.Add(currentObj);
if (debugMode)
print ("Tiles Fell: " + currentObj.tag + " new x: " + x + " new y: " + yNew);
} else {
emptySpaces++;
GameObject newTilesObj = gridManager.GenerateTile(x, y2);
newTilesCol.Add(newTilesObj);
newTilesObj.transform.position += new Vector3 (0f, 8f, 0f);
}
}
//Drop the Generated new Tiles
foreach (GameObject obj in newTilesCol)
{
float targetY = (obj.transform.position.y - 8f) + (float)fillSpaces;
StartCoroutine(TilesFall(obj, targetY, gravity, 3));
gridManager.gridObj[x, (int)targetY] = obj;
tilesFall.Add(obj);
if (debugMode)
print ("New Tile: " + gridManager.gridObj[x, (int)targetY].tag + " x:" + x + " y:" + (int)targetY);
}
//set y to gridHeight amount, to stop vertical for-loop outside this loop from looping again.
y = gridHeight;
if (debugMode)
print ("Check Empty Executed at: " + Time.time);
}
}
}
checkAfterFall = true;
//CheckEmpty ends
}
IEnumerator CheckAfterFall(List<GameObject> tileList, List<GameObject> listX, List<GameObject> listY)
{
checkAfterFall = false;
foreach (GameObject obj in tileList)
{
while (obj.transform.position.y > gridHeight)
yield return null;
if (obj != null)
CheckMatches(obj.transform.position.x, obj.transform.position.y, listX, listY, false);
obj.GetComponent<SpriteRenderer>().color += new Color (0.3f,0f,0f);
}
//StartCoroutine(DestroyMatches(listX, listY));
tileList.Clear ();
listX.Clear();
listY.Clear();
yield return null;
}
IEnumerator TilesFall (GameObject obj, float targetY, float gravity, int bounce)
{
float speed = 0f;
isMoving = true;
if (obj != null)
{
while (bounce > 0)
{
speed -= gravity;
//Bounce effect
if (obj.transform.position.y < targetY)
{
obj.transform.position = new Vector3 (obj.transform.position.x, targetY, obj.transform.position.z);
speed *= -0.35f;
bounce--;
}
obj.transform.position += new Vector3 (0, speed * Time.deltaTime, 0);
yield return null;
}
obj.transform.position = new Vector3 (obj.transform.position.x, targetY, obj.transform.position.z);
isMoving = false;
}
}
}
Thanks for any help before!