A 2D puzzle–platformer built entirely in Unity (C#). I was solely responsible for gameplay systems architecture, player systems, AI, interaction framework, persistence, and runtime tooling.
Role: Gameplay Programmer
Engine: Unity (C#)
Core systems communicate via shared interfaces
(IActivate, IHealth, IThrowable, IIgnorable)
to reduce coupling and allow modular interaction between gameplay systems.
A Rigidbody based movement used by both player and clones, enabling seamless possession without code duplication.
Lightweight coroutine sequencing used for enemy behaviors, animation timing, and gameplay transitions instead of large Animator state machines.
Weight-driven activation framework allowing pressure plates, wind volumes, and environmental elements to interact without hard references.
Built an animation manager to customise animation handling replace complex Animator graphs, improving control and debugging clarity.
Implemented object pooling for audio, minimized per-frame memory allocations, and avoided unnecessary runtime instantiation.
Designed a shared character framework supporting player control, clone instantiation, possession, merging, and physics interaction.
Both player and clones use direct Rigidbody velocity manipulation for deterministic platforming control. Input is abstracted through Unity’s Input System to allow clean control reassignment during possession.
// Horizontal movement
float xInput = InputManager.instance.move.action.ReadValue<Vector2>().x;
rb.linearVelocity = new Vector2(xInput * moveSpeed, rb.linearVelocity.y);
Implemented dynamic input reassignment and camera retargeting. Clones are maintained in a managed list, allowing selection cycling without UI complexity.
// Camera retarget on possession
CameraController.instance.ChangeTarget(currentSelection.transform);
Clones merge when equal size states collide, increasing weight and altering physics interactions. This system integrates directly with puzzle weight calculations.
// Merge condition
if (_clone.sizeCount == sizeCount)
{
sizeCount++;
transform.localScale *= 2;
}
Clones can transition between dynamic and kinematic states for pickup/throw behavior, ensuring correct physics behavior during player interaction.
Implemented coroutine-driven behavior cycles for enemies instead of Animator-based state graphs.
Radius-based overlap detection identifies valid targets
implementing IHealth.
// Explosion damage check
foreach (Collider2D hit in Physics2D.OverlapCircleAll(transform.position, explosionRadius))
{
if (hit.TryGetComponent(out IHealth health))
health.ApplyDamage(gameObject);
}
Detection → Pursuit → Inflation Phase → Explosion → Reset. Collision layers and colliders are modified during attack phases to control interaction rules.
Designed a decoupled activation system enabling environmental elements to interact without direct references.
Pressure plates calculate cumulative weight from players and clones. Clone size state contributes dynamically.
weight += (clone.sizeCount + 1);
Activated objects implement IActivate, allowing new
puzzle components to integrate without modifying existing systems.
Replaced complex Animator state machines with a procedural animation controller that directly manages state transitions and one-shot clips.
// Non-looping animation handling
currentState = newState;
yield return new WaitForSeconds(clip.length);
currentState = previousState;
This reduced debugging complexity and simplified timing-sensitive sequences.
// Object pool usage
audioDictionary[_pool].GetAudioFromPool(position);
Implemented JSON-based save system storing progression, collectibles, and chapter state. Data serialized to disk and loaded on startup.
// Save to JSON
string json = JsonUtility.ToJson(data, true);
File.WriteAllText(savePath, json);