I'm currently working on a match-3 game genre in the Unity engine, and I'm using some gem artwork for it. I've encountered a situation where, after storing the gem positions in a 2D array and swapping them, I need to update the array again.
I have two questions:
Why do we need to update a 2D array again, considering it's already been updated? What is this concept called in programming? Could you please recommend a book or tutorial to learn more about it?
And please note that there's no need to read all of the code; please only focus on the sections where I've mentioned in the comments
class 1 :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Board : MonoBehaviour
{
public int width;
public int height;
public GameObject backgroundTilePrefab;
public Gem[] gems;
//this one
public Gem[,] allGems;
public float gemSpeed;
public MatchFinder matchFinder;
private void Awake()
{
matchFinder = FindObjectOfType<MatchFinder>();
}
private void Start()
{
//this one
allGems = new Gem[width, height];
Setup();
}
private void Update()
{
matchFinder.FindAllMatches();
}
private void Setup()
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Vector2 pos = new Vector2(x, y);
GameObject backgroundTile = Instantiate(backgroundTilePrefab,pos,Quaternion.identity);
backgroundTile.transform.parent = transform;
backgroundTile.transform.name = "Background Tile " + x + "," + y;
int gemToUse = Random.Range(0, gems.Length);
int iterations = 0;
while (MatchesAt(new Vector2Int(x,y), gems[gemToUse]) && iterations < 100)
{
gemToUse = Random.Range(0, gems.Length);
print("already matched found");
iterations++;
}
SpawnGem(gems[gemToUse],new Vector2Int(x,y));
}
}
}
private void SpawnGem(Gem gemToSpawn,Vector2Int pos)
{
Gem gem = Instantiate(gemToSpawn,new Vector3(pos.x,pos.y,0),Quaternion.identity);
gem.transform.parent = transform;
gem.transform.name = "Gem " + pos.x + "," + pos.y;
//this one
allGems[pos.x, pos.y] = gem;
gem.SetupGem(pos,this);
}
bool MatchesAt(Vector2Int posToCheck, Gem gemToCheck)
{
if (posToCheck.x > 1)
{
if (allGems[posToCheck.x - 1,posToCheck.y].gemTypes == gemToCheck.gemTypes && allGems[posToCheck.x - 2, posToCheck.y].gemTypes == gemToCheck.gemTypes)
{
return true;
}
}
if (posToCheck.y > 1)
{
if (allGems[posToCheck.x, posToCheck.y - 1].gemTypes == gemToCheck.gemTypes && allGems[posToCheck.x, posToCheck.y -2].gemTypes == gemToCheck.gemTypes)
{
return true;
}
}
return false;
}
public void DestroyMatchesGemAt(Vector2Int pos)
{
if (allGems[pos.x,pos.y] != null)
{
if (allGems[pos.x,pos.y].isMatched)
{
Destroy(allGems[pos.x,pos.y].gameObject);
allGems[pos.x,pos.y] = null;
}
}
}
public void DestroyMatches()
{
for (int i=0; i < matchFinder.currentMatches.Count; i++)
{
if (matchFinder.currentMatches[i] != null)
{
DestroyMatchesGemAt(matchFinder.currentMatches[i].posIndex);
}
}
StartCoroutine(DecreaseRowCoroutine());
}
public int nullCounter;
IEnumerator DecreaseRowCoroutine()
{
yield return new WaitForSeconds(0.2f);
nullCounter = 0;
for (int x=0; x < width; x++)
{
for (int y=0;y<height;y++)
{
if (allGems[x,y] == null)
{
nullCounter++;
}else if (nullCounter > 0)
{
allGems[x, y - nullCounter] = allGems[x, y];
allGems[x, y - nullCounter].posIndex.y -= nullCounter;
allGems[x, y] = null;
}
}
}
}
}
class 2 :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gem : MonoBehaviour
{
public Vector2Int posIndex;
public Board board;
public enum GemTypes { blue, green, red,purple,yellow }
public GemTypes gemTypes;
public bool isMatched;
public Vector2Int previousPos;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (mousePressed && Input.GetMouseButtonUp(0))
{
mousePressed = false;
finalTouchPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
CalculateAngle();
}
if (Vector2.Distance(transform.position,posIndex) > 0.01f)
{
transform.position = Vector2.Lerp(transform.position, posIndex, board.gemSpeed * Time.deltaTime);
}else
{
transform.position = new Vector3(posIndex.x,posIndex.y);
board.allGems[posIndex.x,posIndex.y] = this;
}
}
public void SetupGem(Vector2Int posIndex,Board board)
{
this.posIndex = posIndex;
this.board = board;
}
public Vector2 firstTouchPosition;
public bool mousePressed;
public Vector2 finalTouchPosition;
public float swipeAngle;
private void OnMouseDown()
{
firstTouchPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//Debug.Log(firstTouchPosition);
mousePressed = true;
}
private void CalculateAngle()
{
swipeAngle = Mathf.Atan2(finalTouchPosition.y - firstTouchPosition.y, finalTouchPosition.x - firstTouchPosition.x);
swipeAngle = swipeAngle * 180 / Mathf.PI;
print(swipeAngle);
if (Vector2.Distance(firstTouchPosition,finalTouchPosition) > 0.5f)
{
MovePieces();
}
}
Gem otherGem;
private void MovePieces()
{
previousPos = posIndex;
//drag left to right
if (swipeAngle < 45 && swipeAngle > -45 && posIndex.x < board.width - 1)
{
otherGem = board.allGems[posIndex.x + 1, posIndex.y];
otherGem.posIndex.x--;
posIndex.x++;
}
//drag down to up
else if (swipeAngle > 45 && swipeAngle < 135 && posIndex.y < board.height - 1)
{
otherGem = board.allGems[posIndex.x , posIndex.y + 1];
otherGem.posIndex.y--;
posIndex.y++;
}
//drag up to down
else if (swipeAngle < -45 && swipeAngle > -135 && posIndex.y > 0)
{
otherGem = board.allGems[posIndex.x, posIndex.y - 1];
otherGem.posIndex.y++;
posIndex.y--;
}
//drag right to left
else if (swipeAngle > 135 || swipeAngle < -135 && posIndex.x > 0)
{
otherGem = board.allGems[posIndex.x - 1, posIndex.y];
otherGem.posIndex.x++;
posIndex.x--;
}
//my main problem is these 2 following lines
board.allGems[posIndex.x, posIndex.y] = this;
board.allGems[otherGem.posIndex.x,otherGem.posIndex.y] = otherGem;
StartCoroutine(CheckMoveCoroutine());
}//MovePieces()
public IEnumerator CheckMoveCoroutine()
{
yield return new WaitForSeconds(0.5f);
//board.matchFinder.FindAllMatches();
if (otherGem != null)
{
if (!isMatched && !otherGem.isMatched)
{
otherGem.posIndex = posIndex;
posIndex = previousPos;
board.allGems[posIndex.x, posIndex.y] = this;
board.allGems[otherGem.posIndex.x, otherGem.posIndex.y] = otherGem;
}else
{
board.DestroyMatches();
}
}
}
}//class
if I understanded correctly by updating array you mean this part:I think you misunderstood
Instantiatefunctionality. when you callInstantiateyou should give it a reference to prefab then it will clone that prefab into new GameObject in scene. if you give it an already existing GameObject in scene it will do similar thing (clone it into new GameObject) that most likely is not desired behavior.so I think that is the problem,
gemfreshly created in this method and no one has reference to it so you need to assign it in array. after instantiating,gemandgemToSpawnhave nothing to do with each other.or maybe I didn't understand your problem correctly if so, please specify the part of code that you had problem with, those are very large portion of codes to track.