Unreal Engine provides a powerful framework for implementing games like Tetris. Below is a comprehensive guide and implementation of Tetris in Unreal Engine, including core mechanics such as block movement, rotation, row clearing, random block generation, scoring, and game-over conditions.


1. Tetromino Class

Each Tetromino (Tetris piece) is a custom AActor class that handles player input, movement, and rotation.

// Tetromino.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Tetromino.generated.h"

UCLASS()
class TETRIS_API ATetromino : public AActor
{
    GENERATED_BODY()

public:
    ATetromino();

protected:
    virtual void BeginPlay() override;

public:
    virtual void Tick(float DeltaTime) override;

    void Move(FVector Direction);
    void Rotate();

private:
    float FallTime = 1.0f;
    float FallTimer = 0.0f;

    bool IsValidPosition();
};

// Tetromino.cpp
#include "Tetromino.h"
#include "GridManager.h"
#include "Kismet/GameplayStatics.h"

ATetromino::ATetromino()
{
    PrimaryActorTick.bCanEverTick = true;
}

void ATetromino::BeginPlay()
{
    Super::BeginPlay();
}

void ATetromino::Tick(float DeltaTime)
{
    FallTimer += DeltaTime;

    if (FallTimer >= FallTime)
    {
        Move(FVector(0, 0, -1));
        FallTimer = 0;
    }

    if (IsInputKeyDown(EKeys::Left))
        Move(FVector(-1, 0, 0));

    if (IsInputKeyDown(EKeys::Right))
        Move(FVector(1, 0, 0));

    if (IsInputKeyDown(EKeys::Down))
        Move(FVector(0, 0, -1));

    if (IsInputKeyDown(EKeys::Up))
        Rotate();
}

void ATetromino::Move(FVector Direction)
{
    SetActorLocation(GetActorLocation() + Direction * 100.0f);

    if (!IsValidPosition())
        SetActorLocation(GetActorLocation() - Direction * 100.0f);
}

void ATetromino::Rotate()
{
    AddActorLocalRotation(FRotator(0, 0, -90));

    if (!IsValidPosition())
        AddActorLocalRotation(FRotator(0, 0, 90));
}

bool ATetromino::IsValidPosition()
{
    TArray<USceneComponent*> Children;
    GetComponents<USceneComponent>(Children);

    for (USceneComponent* Child : Children)
    {
        FVector Location = Child->GetComponentLocation() / 100.0f;
        Location = FVector(FMath::RoundToInt(Location.X), FMath::RoundToInt(Location.Y), FMath::RoundToInt(Location.Z));

        if (!AGridManager::IsInsideGrid(Location) || AGridManager::IsOccupied(Location))
            return false;
    }
    return true;
}

2. Grid Management

The grid manages block positions, detects collisions, and handles row clearing.

// GridManager.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GridManager.generated.h"

UCLASS()
class TETRIS_API AGridManager : public AActor
{
    GENERATED_BODY()

public:
    static const int Width = 10;
    static const int Height = 20;

    static bool IsInsideGrid(FVector Location);
    static bool IsOccupied(FVector Location);
    static void AddToGrid(AActor* Block);
    static void CheckRows();

private:
    static TArray<AActor*> Grid[Width][Height];
};

// GridManager.cpp
#include "GridManager.h"

TArray<AActor*> AGridManager::Grid[Width][Height];

bool AGridManager::IsInsideGrid(FVector Location)
{
    return Location.X >= 0 && Location.X < Width && Location.Z >= 0;
}

bool AGridManager::IsOccupied(FVector Location)
{
    return Grid[(int)Location.X][(int)Location.Z].Num() > 0;
}

void AGridManager::AddToGrid(AActor* Block)
{
    TArray<USceneComponent*> Children;
    Block->GetComponents<USceneComponent>(Children);

    for (USceneComponent* Child : Children)
    {
        FVector Location = Child->GetComponentLocation() / 100.0f;
        Location = FVector(FMath::RoundToInt(Location.X), FMath::RoundToInt(Location.Y), FMath::RoundToInt(Location.Z));

        Grid[(int)Location.X][(int)Location.Z].Add(Child->GetOwner());
    }

    CheckRows();
}

void AGridManager::CheckRows()
{
    for (int Z = 0; Z < Height; ++Z)
    {
        bool IsFull = true;

        for (int X = 0; X < Width; ++X)
        {
            if (Grid[X][Z].Num() == 0)
            {
                IsFull = false;
                break;
            }
        }

        if (IsFull)
        {
            for (int X = 0; X < Width; ++X)
            {
                for (AActor* Actor : Grid[X][Z])
                    Actor->Destroy();

                Grid[X][Z].Empty();
            }

            for (int ZAbove = Z + 1; ZAbove < Height; ++ZAbove)
            {
                for (int X = 0; X < Width; ++X)
                {
                    for (AActor* Actor : Grid[X][ZAbove])
                    {
                        Actor->SetActorLocation(Actor->GetActorLocation() - FVector(0, 0, 100));
                        Grid[X][ZAbove - 1].Add(Actor);
                    }
                    Grid[X][ZAbove].Empty();
                }
            }
            Z--;
        }
    }
}

3. Game Manager

The game manager spawns Tetrominoes, tracks the score, and detects game-over conditions.

// GameManager.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GameManager.generated.h"

UCLASS()
class TETRIS_API AGameManager : public AActor
{
    GENERATED_BODY()

public:
    void SpawnTetromino();
    void AddScore(int RowsCleared);
    void CheckGameOver();

private:
    int Score = 0;
    TSubclassOf<class ATetromino> TetrominoClass;
};

// GameManager.cpp
#include "GameManager.h"
#include "Kismet/GameplayStatics.h"
#include "Tetromino.h"

void AGameManager::SpawnTetromino()
{
    FVector SpawnLocation(5, 0, 20);
    FActorSpawnParameters SpawnParams;
    GetWorld()->SpawnActor<ATetromino>(TetrominoClass, SpawnLocation, FRotator::ZeroRotator, SpawnParams);
}

void AGameManager::AddScore(int RowsCleared)
{
    Score += RowsCleared * 100;
    UE_LOG(LogTemp, Warning, TEXT("Score: %d"), Score);
}

void AGameManager::CheckGameOver()
{
    for (int X = 0; X < AGridManager::Width; ++X)
    {
        if (AGridManager::IsOccupied(FVector(X, 0, AGridManager::Height - 1)))
        {
            UE_LOG(LogTemp, Warning, TEXT("Game Over!"));
            UGameplayStatics::SetGamePaused(GetWorld(), true);
            break;
        }
    }
}

Features and Setup

  1. Tetromino Prefabs: Use modular components for each Tetromino block.
  2. Game Grid: A 2D grid system that detects collisions and clears rows.
  3. Game Over: Detect overflow at the top of the grid.

This implementation uses Unreal’s Blueprint system where necessary, making it easily extendable and functional for real-world applications.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다