Second part of tutorials on how to create Memory game.
Now add empty GameObject in the Hierarchy – right click in Hierarchy window and choose Create Empty. When object is created rename it to “GameManager”, reset its position and add new script “GameManager”.
GameManager Script:
using System.Collections; using System.Collections.Generic; using UnityEngine; // ? 2017 TheFlyingKeyboard and released under MIT License // theflyingkeyboard.net public class GameManager : MonoBehaviour { private GameObject _firstCard = null; private GameObject _secondCard = null; private int _cardsLeft; private bool _canFlip = true; [SerializeField] private float _timeBetweenFlips = 0.75f; //I think that particullar value works best, but you can adjust it as you want public bool canFlip { get { return _canFlip; } set { _canFlip = value; } } public int cardsLeft { get { return _cardsLeft; } set { _cardsLeft = value; } } public void AddCard(GameObject card) //This function will be called from CardController class { if (_firstCard == null) //Adds first card { _firstCard = card; } else //Adds second card and checks if both cards match { _secondCard = card; _canFlip = false; if (CheckIfMatch()) { DecreaseCardCount(); StartCoroutine(DeactivateCards()); } else { StartCoroutine(FlipCards()); } } } IEnumerator DeactivateCards() { yield return new WaitForSeconds(_timeBetweenFlips); //Wait so player can see flipped cards and not click to fast _firstCard.SetActive(false); _secondCard.SetActive(false); Reset(); } IEnumerator FlipCards() { yield return new WaitForSeconds(_timeBetweenFlips); //Wait so player can see flipped cards and not click too fast _firstCard.GetComponent<CardController>().ChangeSide(); _secondCard.GetComponent<CardController>().ChangeSide(); Reset(); } public void Reset() { _firstCard = null; _secondCard = null; _canFlip = true; } public void DecreaseCardCount() { _cardsLeft -= 2; if (_cardsLeft <= 0) { //TODO GameOver } } bool CheckIfMatch() { if (_firstCard.GetComponent<CardController>().cardName == _secondCard.GetComponent<CardController>().cardName) { return true; } return false; } }
After that come back to “CardController” script and modify it so it should look like this (changes are marked with <—):
using System.Collections; using System.Collections.Generic; using UnityEngine; // ? 2017 TheFlyingKeyboard and released under MIT License // theflyingkeyboard.net public class CardController : MonoBehaviour { [SerializeField] private string _cardName; //It will be used to check if two cards match private bool _isUpsideDown = false; //To know if card was flipped private Sprite _backSideCardSprite; //Sprite that represents back of the card [SerializeField] private Sprite _frontSideCardSprite; //Sprite that represents front of the card private SpriteRenderer _spriteRenderer; private GameManager _gameManager; //<--- private void Start() { _spriteRenderer = GetComponent<SpriteRenderer>(); _gameManager = FindObjectOfType<GameManager>(); //Finds GameManager<--- _backSideCardSprite = _spriteRenderer.sprite; } public string cardName { get { return _cardName; } set { _cardName = value; } } private void OnMouseDown() //This function is called each time player clicks on GameObject { if (!_isUpsideDown) { _gameManager.AddCard(gameObject); //Adds this card to it<--- ChangeSide(); } } public void ChangeSide() { if (!_isUpsideDown) { _spriteRenderer.sprite = _frontSideCardSprite; _isUpsideDown = true; } else { _spriteRenderer.sprite = _backSideCardSprite; _isUpsideDown = false; } } }
Next step is to add another GameObject, reset its position and name it “CardSpawner”.
CardSpawner script:
using System.Collections; using System.Collections.Generic; using UnityEngine; // ? 2017 TheFlyingKeyboard and released under MIT License // theflyingkeyboard.net public class CardSpawner : MonoBehaviour { [SerializeField] private List<GameObject> _cardsToSpawn; [SerializeField] private List<int> _cardsToSpawnCount; [SerializeField] private Transform _startPoint; [SerializeField] private int _rows = 5; [SerializeField] private int _columns = 12; [SerializeField] private float _xDistance = 1.25f; [SerializeField] private float _yDistance = 1.75; private GameManager _gameManager; // Use this for initialization void Start() { _gameManager = FindObjectOfType<GameManager>(); } private void Update() //Objects are spawned in Update, so script execution order won't affect this scrip { Spawn(); gameObject.SetActive(false); //After spawning objects deactivate this gameObject } void Spawn() { int cardNumber; int maxCards = _rows * _columns; if (maxCards % 2 == 0) { _cardsToSpawnCount = new List<int>(); _gameManager.cardsLeft = maxCards; for (int i = 0; i < _cardsToSpawn.Count; i++) { _cardsToSpawnCount.Add(0); } for (int i = 0; i < _cardsToSpawnCount.Count; i++) { _cardsToSpawnCount[i] = Random.Range(0, (int)(maxCards / (_cardsToSpawn.Count - 1)) + 1); if (_cardsToSpawnCount[i] % 2 != 0) { if (Random.Range(0, 100) < 50) { _cardsToSpawnCount[i]--; } else { _cardsToSpawnCount[i]++; } } } int sum = 0; for (int i = 0; i < _cardsToSpawnCount.Count; i++) { sum += _cardsToSpawnCount[i]; } if (sum < maxCards) { for (int i = 0; i < (int)((maxCards - sum) / 2); i++) { _cardsToSpawnCount[Random.Range(0, _cardsToSpawn.Count - 1)] += 2; //Because it must be even } } bool forward = true; for (int i = 0; i < _rows; i++) { for (int j = 0; j < _columns; j++) { cardNumber = Random.Range(0, _cardsToSpawnCount.Count - 1); if (_cardsToSpawnCount[cardNumber] > 0) { Instantiate(_cardsToSpawn[cardNumber], new Vector3(_startPoint.position.x + _xDistance * j, _startPoint.position.y - _yDistance * i, _startPoint.position.z), Quaternion.Euler(0.0f, 0.0f, 0.0f)); _cardsToSpawnCount[cardNumber]--; } else { if (forward) { for (int k = 0; k < _cardsToSpawnCount.Count; k++) { if (_cardsToSpawnCount[k] > 0) { Instantiate(_cardsToSpawn[k], new Vector3(_startPoint.position.x + _xDistance * j, _startPoint.position.y - _yDistance * i, _startPoint.position.z), Quaternion.Euler(0.0f, 0.0f, 0.0f)); _cardsToSpawnCount[k]--; break; } } } else { for (int k = _cardsToSpawnCount.Count - 1; k >= 0; k--) { if (_cardsToSpawnCount[k] > 0) { Instantiate(_cardsToSpawn[k], new Vector3(_startPoint.position.x + _xDistance * j, _startPoint.position.y - _yDistance * i, _startPoint.position.z), Quaternion.Euler(0.0f, 0.0f, 0.0f)); _cardsToSpawnCount[k]--; break; } } } forward = !forward; } } } } else { Debug.Log("Number of cards must be even!"); } } }
Now add new GameObject, rename it to “SpawnStartingPoint” and place it in (-7, 3.5, 0):
After that fill field “Start Point” of CardSpawner with this object, and field “Cards To Spawn” with prefabs from “Prefabs” folder.
CardSpawner GameObject should look like this:
Next step is creating a script to count time of play and show GameOverMenu.
Next Part Of Tutorial: Time And GameOver
Assets/GameManager.cs(67,51): error CS0122: `CardController.ChangeSide()’ is inaccessible due to its protection level
Fixed, there was “public” access specifier missing 🙂