Untitled Metroidvania

A 2D metroidvania prototype built in Unity (C#), focused on tight platforming, modular combat systems, ability-driven progression, and scalable enemy AI architecture. I designed and implemented all core gameplay systems.

Role: Gameplay Programmer
Engine: Unity (C#)

Technical Overview

Ability-Driven Progression

Player traversal and combat abilities are implemented as modular components, enabling gated progression and clean expansion of movement mechanics.

State-Based Player Controller

Custom finite state machine manages grounded, airborne, dash, attack, and ability states without relying on Animator transitions.

Combat Framework

Hit detection, damage handling, knockback, invulnerability frames, and enemy reactions are handled through interface-driven contracts.

Modular Enemy AI

Coroutine-driven enemy behavior cycles with clear state transitions for patrol, chase, attack, stagger, and death.

Ability Gating System

World traversal checks player ability flags to control access to new areas, reinforcing classic metroidvania progression design.

Performance-Conscious Design

Object pooling, allocation-aware physics checks, and centralized managers ensure stable runtime performance.

Player Controller & Movement Systems

Designed a responsive Rigidbody-based controller with deterministic horizontal control, coyote time, jump buffering, and dash handling.

Core Movement

Horizontal velocity is directly controlled while vertical movement respects physics interactions for precise platforming.


// Horizontal movement
float xInput = input.move.x;
rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);

Jump Buffer & Coyote Time

Implemented timing buffers to improve responsiveness and eliminate input frustration common in precision platformers.


if (jumpBufferCounter > 0 && coyoteTimeCounter > 0)
{
    rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}

Dash Implementation

Dash temporarily overrides gravity and locks direction input to maintain consistent burst movement.


rb.gravityScale = 0;
rb.velocity = new Vector2(dashDirection * dashSpeed, 0);
Combat System

Built a modular combat framework supporting melee attacks, enemy damage handling, and knockback reactions.

Hit Detection

Physics overlap checks detect valid damage targets implementing a shared IDamageable interface.


foreach (Collider2D hit in Physics2D.OverlapCircleAll(attackPoint.position, attackRadius))
{
    if (hit.TryGetComponent(out IDamageable damageable))
        damageable.TakeDamage(damageAmount);
}

Invulnerability Frames

Damage triggers temporary invulnerability and visual feedback, preventing repeated collision-based damage.

Enemy AI Architecture

Implemented coroutine-driven AI state loops to manage patrol, aggro detection, attack timing, and recovery states.

Detection System

Radius-based detection checks for player presence and transitions to pursuit state when triggered.


if (Vector2.Distance(transform.position, player.position) < detectionRange)
{
    currentState = EnemyState.Chase;
}

Attack Cycle

Attack sequences are controlled via coroutine timing rather than Animator state graphs, improving clarity and flexibility.

World Systems & Progression

Designed ability-based traversal gating, ensuring players unlock new regions through earned movement mechanics (double jump, dash, wall climb, etc.).


// Ability check example
if (playerAbilities.canDoubleJump)
{
    OpenPath();
}

Persistence & Save System

Implemented JSON-based saving of player progression, unlocked abilities, and checkpoint positions.


string json = JsonUtility.ToJson(saveData, true);
File.WriteAllText(savePath, json);

Engineering Challenges Solved