Ruben Bimmel

Game Development

Tetris Adventure

Opdracht: Maak een retro game na en geef deze een twist
Duur: 2 maanden
Team grootte: Solo project
Rol: Development
Vaardigheden:
Links: GitHub

Bij dit spel heb ik een unieke twist gegeven aan een van mijn favoriete retro games: Tetris. De twist is dat het spel ook een platformer is. Je bestuurd een mannetje dat over de tetris blokken moet klimmen om in leven te blijven. De speler bestuurd het mannetje en de tetris blokken tegelijk. Het is erg moeilijk om op allebei te blijven focussen, maar dit maakt het juist erg leuk om te spelen.

De uitdaging bij het maken van dit spel was om een combinatie te maken van de langzame blokkerige gameplay van tetris en de snelle vloeiende gameplay van een platformer. Er zijn veel regels voor nodig om de interactie tussen de blokken en de personages goed te laten verlopen. Tetris zit heel goed in elkaar en alles werkt goed samen. Als je hier iets aan toe voegt kan dit snel de gameplay van het originele spel breken.

Block class

Open »
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Block : MonoBehaviour {

    private Collectable collectable;
    private Grid grid;

    // Set colour of block
    public void SetColour (Color colour) {
        transform.GetComponent<SpriteRenderer>().color = colour;
    }

    // Set grid referance of block
    public void SetGrid (Grid newGrid) {
        grid = newGrid;
    }

    // Add collectable to Block
    public bool AddCollectable(Collectable newCollactable) {
        if (!collectable) {
            collectable = newCollactable;
            collectable.transform.parent = transform;
            collectable.transform.localPosition = new Vector3(0, 0, -1);
            return true;
        }
        return false;
    }

    // Destroy block and release collectables
    public void Destroy () {
        if (collectable != null) {
            int column = grid.GetLocalPosition(transform.position)[0];
            int row = grid.getHeighestAvailableCellInColumn(column);
            collectable.transform.parent = grid.transform;
            collectable.transform.localPosition = new Vector3(column, row, -1);
            collectable.SetActive(true);
        }
        Destroy(gameObject);
    }

    // Gets called every time a block moves
    public void CheckCharacterCollision() {
        Collider2D characterCollider = Physics2D.OverlapBox(transform.position, transform.lossyScale, 0f, LayerMask.GetMask("Character"));
        if (characterCollider) {
            Vector2 characterPosition = characterCollider.transform.position;
            if (Mathf.Abs(characterPosition.x - transform.position.x) < .158f && characterPosition.y - transform.position.y < .158f && characterPosition.y - transform.position.y > -.63f) {
                characterCollider.GetComponent<ICharacterController>().HitByBlock();
            }
        }
    }
}

Tetromino class

Open »
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Tetromino {

    private Grid grid;
    public enum TetrominoShape { I, J, L, O, S, T, Z };
    public TetrominoShape shape;
    private Color colour;
    public int[,] blockPositions;
    private int rotation;
    private Collectable[] collectables = new Collectable[4];

    // Constructor
    public Tetromino () {
        shape = (TetrominoShape)Random.Range(0, 6);
        
        switch (shape) {
            case TetrominoShape.I:
                colour = Color.red;
                break;
            case TetrominoShape.J:
                colour = Color.blue;
                break;
            case TetrominoShape.L:
                colour = new Color(1, .5f, 0);
                break;
            case TetrominoShape.O:
                colour = Color.yellow;
                break;
            case TetrominoShape.S:
                colour = Color.magenta;
                break;
            case TetrominoShape.T:
                colour = Color.cyan;
                break;
            case TetrominoShape.Z:
                colour = Color.green;
                break;
        }

        rotation = 0;
    }

    // Adds collactable to tetronimo list. When it is added to the grid it will be added to the newly created block
    public bool AddCollectable (Collectable newCollectable) {
        for (int i = 0; i < 4; i++) {
            if (collectables[i] == null) {
                collectables[i] = newCollectable;
                return true;
            }
        }
        return false;
    }

    // Add this tetromino to a grid. New Blocks will be constructed.
    public bool AddToGrid (Grid _grid) {
        grid = _grid;
        blockPositions = GetBlockPositions();

        for (int i = 0; i < 4; i++) {
            bool added = grid.AddBlock(blockPositions[i, 0], blockPositions[i, 1], colour);
            if (!added) {
                return false;
            }

            // Add collectables to blocks
            if (collectables[i]) {
                grid.GetBlock(blockPositions[i, 0], blockPositions[i, 1]).AddCollectable(collectables[i]);
                collectables[i].SetActive(false);
            }
        }

        return true;
    }

    // Public move function
    public bool Move (int xOffset, int yOffset) {
        int[,] targetPositions = (int[,])blockPositions.Clone();
        for (int i = 0; i < 4; i++) {
            targetPositions[i, 0] += xOffset;
            targetPositions[i, 1] += yOffset;
        }

        bool tetrominoCanMove = ((GameGrid) grid).TransformTetromino(blockPositions, targetPositions);

        if (tetrominoCanMove) {
            blockPositions = targetPositions;
        }

        return tetrominoCanMove;
    }

    // Public rotate function
    public bool Rotate () {
        //O can not rotate
        if (shape == TetrominoShape.O) {
            return true;
        }

        //calculate new positions for rotated tetromino
        int[,] targetPositions = new int[4, 2];
        int[,] relativePosition = new int[4, 2];

        int rotationBlock = GetRotationBlock();

        for (int i = 0; i < 4; i++) {
            relativePosition[i, 0] = blockPositions[i, 0] - blockPositions[rotationBlock, 0];
            relativePosition[i, 1] = blockPositions[i, 1] - blockPositions[rotationBlock, 1];

            targetPositions[i, 0] = blockPositions[rotationBlock, 0] + relativePosition[i, 1];
            targetPositions[i, 1] = blockPositions[rotationBlock, 1] - relativePosition[i, 0];
        }

        //I gets repositioned on rotation
        if (shape == TetrominoShape.I) {
            if (rotation % 2 == 1) {
                for (int i = 0; i < 4; i++) {
                    targetPositions[i, 0]--;
                }
            }
        }

        bool tetrominoCanMove = ((GameGrid)grid).TransformTetromino(blockPositions, targetPositions);

        if (tetrominoCanMove) {
            rotation = ++rotation % 4;
            blockPositions = targetPositions;
        }

        return tetrominoCanMove;

    }

    // Gets called on initialisation.
    private int[,] GetBlockPositions() {
        int[,] positions = new int[,] { };

        switch (shape) {
            case TetrominoShape.I:
                positions = new int[,] { { -1, 0 }, { 0, 0 }, { 1, 0 }, { 2, 0 } };
                break;
            case TetrominoShape.J:
                positions = new int[,] { { -1, 0 }, { 0, 0 }, { 1, 0 }, { 1, -1 } };
                break;
            case TetrominoShape.L:
                positions = new int[,] { { -1, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 } };
                break;
            case TetrominoShape.O:
                positions = new int[,] { { 0, 0 }, { 0, -1 }, { 1, 0 }, { 1, -1 } };
                break;
            case TetrominoShape.S:
                positions = new int[,] { { -1, -1 }, { 0, -1 }, { 0, 0 }, { 1, 0 } };
                break;
            case TetrominoShape.T:
                positions = new int[,] { { -1, 0 }, { 0, 0 }, { 0, -1 }, { 1, 0 } };
                break;
            case TetrominoShape.Z:
                positions = new int[,] { { -1, 0 }, { 0, 0 }, { 0, -1 }, { 1, -1 } };
                break;
        }

        for (int i = 0; i < 4; i++) {
            positions[i, 0] += grid.spawnPosition[0];
            positions[i, 1] += grid.spawnPosition[1];
        }

        return positions;
    }

    // Get anchor block for tetromino rotation, Tetrominos can't always rotate around its center
    // Returns the index of the block to rotate around
    private int GetRotationBlock() {
        switch (shape) {
            case TetrominoShape.I:
                if (rotation < 2) return 2;
                return 1;
            case TetrominoShape.J:
                return 1;
            case TetrominoShape.L:
                return 2;
            case TetrominoShape.S:
                if (rotation < 2) return 1;
                return 2;
            case TetrominoShape.T:
                return 1;
            case TetrominoShape.Z:
                if (rotation < 2) return 1;
                return 2;
        }

        return 0;
    }
}
« Terug naar het overzicht

Contact