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#)
Player traversal and combat abilities are implemented as modular components, enabling gated progression and clean expansion of movement mechanics.
Custom finite state machine manages grounded, airborne, dash, attack, and ability states without relying on Animator transitions.
Hit detection, damage handling, knockback, invulnerability frames, and enemy reactions are handled through interface-driven contracts.
Coroutine-driven enemy behavior cycles with clear state transitions for patrol, chase, attack, stagger, and death.
World traversal checks player ability flags to control access to new areas, reinforcing classic metroidvania progression design.
Object pooling, allocation-aware physics checks, and centralized managers ensure stable runtime performance.
Designed a responsive Rigidbody-based controller with deterministic horizontal control, coyote time, jump buffering, and dash handling.
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);
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 temporarily overrides gravity and locks direction input to maintain consistent burst movement.
rb.gravityScale = 0;
rb.velocity = new Vector2(dashDirection * dashSpeed, 0);
Built a modular combat framework supporting melee attacks, enemy damage handling, and knockback reactions.
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);
}
Damage triggers temporary invulnerability and visual feedback, preventing repeated collision-based damage.
Implemented coroutine-driven AI state loops to manage patrol, aggro detection, attack timing, and recovery states.
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 sequences are controlled via coroutine timing rather than Animator state graphs, improving clarity and flexibility.
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();
}
Implemented JSON-based saving of player progression, unlocked abilities, and checkpoint positions.
string json = JsonUtility.ToJson(saveData, true);
File.WriteAllText(savePath, json);