Documentatie Jason - j5x/PvB2025 GitHub Wiki

Unity Game Mechanic Documentation

๐Ÿงฉ Grid Offset Documentation for Match-3 Game

๐ŸŽฏ Goal

Allow the Match-3 grid to be manually positioned in the Unity Inspector using a gridOffset field, without it being overwritten at runtime.


โœ… Final Setup

Features

โœ… No pre-existing matches at the start.
โœ… Swappable tiles with valid match detection.
โœ… Tile gravity ensures tiles fall into empty spaces.
โœ… Grid automatically refills after matches.

Grid Offset Variable

[SerializeField] private Vector3 gridOffset;

This lets you position the grid wherever you want in the scene from the Unity Inspector.


๐Ÿ“ฆ Updated Functions

SpawnTile

private void SpawnTile(int x, int y)
{
    GameObject tilePrefab;
    do
    {
        tilePrefab = GetRandomTilePrefab();
    } while (WouldCreateMatch(x, y, tilePrefab));

    Vector3 spawnPosition = new Vector3(x * tileSize, y * tileSize, 0) + gridOffset;

    GameObject tile = Instantiate(tilePrefab, spawnPosition, Quaternion.identity, transform);
    
    Tile tileComponent = tile.GetComponent<Tile>();
    tileComponent.SetGridPosition(new Vector2Int(x, y));

    _grid[x, y] = tile;
}

MoveTile

private IEnumerator MoveTile(Tile tile, Vector3 targetPos)
{
    if (tile == null || tile.gameObject == null) yield break;

    float duration = 0.2f;
    float elapsedTime = 0f;
    Vector3 startPos = tile.transform.position;

    while (elapsedTime < duration)
    {
        if (tile == null || tile.gameObject == null) yield break;

        tile.transform.position = Vector3.Lerp(startPos, targetPos + gridOffset, elapsedTime / duration);
        elapsedTime += Time.deltaTime;
        yield return null;
    }

    if (tile != null && tile.gameObject != null)
    {
        tile.transform.position = targetPos + gridOffset;
    }
}


Grid Manager

gridmanager



๐ŸŽฎ Main Menu

Loads the next scene when pressed: This is the start button

using UnityEngine;
using UnityEngine.SceneManagement;

public class StartButton : MonoBehaviour
{
    public void StartGame()
    {
        SceneManager.LoadScene("LevelSelector"); // Replace with your level name
    }
}

๐Ÿ“ฑ UI Scaling (Main Menu)

  • Background Scaling: Uses a Canvas with CanvasScaler set to Scale With Screen Size to support phone resolutions.
  • Button Scaling: Buttons are inside the Canvas with anchoring and responsive layout (e.g., Vertical Layout Group, Content Size Fitter).

This is the credits button

  • UI Text/Panel toggled by button:
using UnityEngine;

public class CreditsPopup : MonoBehaviour
{
    [SerializeField] private GameObject creditsPanel;

    public void ToggleCredits()
    {
        creditsPanel.SetActive(!creditsPanel.activeSelf);
    }
}

๐Ÿ”ข Level Selector

  • Button assigned to different scenes via a simple script:
using UnityEngine;
using UnityEngine.SceneManagement;

public class LevelButton : MonoBehaviour
{
    public void LoadLevel(string levelName)
    {
        SceneManager.LoadScene(levelName);
    }
}

๐Ÿง Character Selector

  • Character is selected via UI and remembered using a persistent GameManager:
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance;
    public GameObject selectedPlayerPrefab;

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}
  • In the scene:
void Start()
{
    Instantiate(GameManager.Instance.selectedPlayerPrefab, spawnPosition, Quaternion.identity);
}

๐Ÿ‘พ Enemy Respawn System

HealthComponent.cs

  • Triggers OnDeath event when HP reaches 0.

Enemy.cs

  • Subscribes to OnDeath and notifies spawner:
private void HandleDeath()
{
    if (spawner != null)
    {
        spawner.SpawnEnemy();
    }
    Destroy(gameObject);
}

๐Ÿง  Enemy Spawner

Features:

  • Delay before spawn
  • Max number of enemies that can spawn
using UnityEngine;
using System.Collections;

public class EnemySpawner : MonoBehaviour
{
    [SerializeField] private GameObject enemyPrefab;
    [SerializeField] private Transform spawnPoint;
    [SerializeField] private float respawnDelay = 2f;
    [SerializeField] private int maxSpawns = 5;

    private int spawnCount = 0;

    public void SpawnEnemy()
    {
        if (spawnCount >= maxSpawns) return;
        StartCoroutine(SpawnEnemyWithDelay(respawnDelay));
    }

    private IEnumerator SpawnEnemyWithDelay(float delay)
    {
        yield return new WaitForSeconds(delay);

        GameObject newEnemy = Instantiate(enemyPrefab, spawnPoint.position, Quaternion.identity);
        Enemy enemyScript = newEnemy.GetComponent<Enemy>();

        if (enemyScript != null)
        {
            enemyScript.spawner = this;
        }

        spawnCount++;
    }
}

๐Ÿงช Notes

  • Make sure all prefabs are assigned in the inspector.
  • Use DontDestroyOnLoad only on necessary singletons like GameManager.
  • Consider object pooling for performance if many enemies are involved.

๐Ÿ“Œ GameManager

Purpose:
Manages global game state across scenes, specifically tracking the selected character prefab.

Key Features:

  • Singleton pattern with DontDestroyOnLoad.
  • Holds reference to SelectedCharacterPrefab.
  • Event OnCharacterSelected notifies UI when a character is picked.

Usage:

GameManager.Instance.SelectCharacter(prefab);
GameManager.Instance.SelectedCharacterPrefab;

๐Ÿง  Dynamic Enemy Health Targeting

Purpose:
Automatically tracks the next enemy when the current one is defeated.

Key Features:

  • Searches for GameObject with tag "Enemy".
  • Subscribes to new enemy's HealthComponent dynamically.

Usage:

GameObject enemy = GameObject.FindWithTag("Enemy");
targetHealthComponent = enemy.GetComponent<HealthComponent>();

๐Ÿ“˜ DonutCopTutorial System

This system handles a talking animated character ("Donut Cop") that delivers tutorial lines via a speech bubble on screen.


๐Ÿงฉ Components

Object Description
DonutCopTutorial Main script controlling the tutorial sequence.
Animator Animates the cop (Idle, Talk, etc.).
SpeechBubble A UI GameObject (enabled/disabled to show dialogue).
TextMeshProUGUI Displays typed-out tutorial text inside the bubble.
SceneManager Used to transition to the next scene after tutorial ends.

๐Ÿง  How It Works

  1. Tutorial Starts Automatically on Start().
  2. Each line of dialogue is typed out character by character.
  3. Cop animation is triggered ("Talk" trigger).
  4. Player can tap to skip typing and instantly show full text.
  5. After all lines, the speech bubble disappears.
  6. Scene is changed automatically to the next level or scene.

โš™๏ธ Configuration

In DonutCopTutorial.cs:

[SerializeField] private string[] tutorialLines;         // Lines of dialogue
[SerializeField] private float delayBetweenLines = 1.5f; // Delay after each line
[SerializeField] private float textTypingSpeed = 0.03f;  // Speed of character-by-character typing
[SerializeField] private string nextSceneName = "Level1"; // Scene to load after tutorial

โœ… Example Usage

  • Place a Donut Cop GameObject in Canvas.
  • Attach DonutCopTutorial.cs.
  • Assign:
    • Animator with "Talk" trigger.
    • speechBubble GameObject.
    • TextMeshPro component inside the bubble.
  • Populate tutorialLines[] in Inspector.
  • Make sure nextSceneName matches your level name.

๐Ÿ–ฑ๏ธ Interaction

Action Result
Tap during typing Skips to full sentence instantly.
Wait for auto progression Moves to next sentence.
Last line completes Loads new scene.
โš ๏ธ **GitHub.com Fallback** โš ๏ธ