GROCERY ODYSSEY: Technical Deep Dive


Highlights



Randomized attack zones indication generator

Hit Area Click & Visual Implementation

how it works

  • I used the Unity Canvas system used to spawn circles using GenerateHitAreas method called in HitArea script which calls rect.GetRandomPointOnImage();

  • Then I Instantiate a Hit prefab, creating a green circle in that random location within an Image component's bounds

Why

  • Attack generation is independent of enemy implementation, separating attacks from how enemies behave

  • Random-point generation within UI bounds creates high variability without manually creating attack patterns

how it works

  • I built a HitAvaliable script with a public Hit() function called by my PlayerCombat script.

  • The combat script uses the Canvas' GraphicRaycaster component to cast a ray using PointerEventData and the mouse's Input.MousePosition this is fed to a List<RaycastResult>, then parsed through using LINQ checking if something hit, then a null check, then calling Hit() on the reference.

  • This then calles a C_Hit() coroutine to fire fading out the alpha of the HitArea

why

  • SRP: PlayerCombat - manages what was hit HitAvaliable - handles how the indicator reacts (visual + state change)

  • Immediate visual feedback reinforces player action & player confidence


Re-usable ScaleChange object scaler

Type Based Polymorphic Enemy System

how it works

  • ScaleChange is a separate component with the sole responsibility of shrinking an object after enabled using Unity's OnEnable Monobehaviour override.

  • It uses my custom LerpScale Transform extension method in my custom extension method library.

Why

  • Continuous visual and mechanical pressure is applied forcing players to stay engaged to do damage.

  • Duration-based scaling allows axis of difficulty tweaking

  • Encapsulating logic into a standalone component keeps logic independent and modular

how it works

  • I kept all the orchestration on a Enemy class, and moved the differences between enemies to a EnemyType class.

  • The Enemy class composition 'has-a" EnemyType, Leaving orchestration to the Enemy class serving as a central functionality hub for enemies.

  • EnemyTypes implement a StartCombo, a foundation for later granular implementation differences for different enemy types.

  • Enemy central class can switch EnemyType without requiring a change of script, cutting down on Monobehaviour class definition explosion.

why

  • "has-a" relationship allows behaviours to be swapped without re-writing shared funcitonality

  • EnemyType encapsulates what an enemy does while Enemy controls how and when it happens avoiding tight coupling

  • New enemy variations only require new EnemyType definitions, keeping codebase maintainable as content scales.


Modular Attack System using Data-Driven Command Pattern

Modular Enemy Combo Framework

how it works

  • I designated an abstract Attack class and implemented it via a concreate Directional class to serve as the basic attack implementation for all Enemy

  • scripts.

  • All Enemy scripts who communicate with the Attack scripts only talk to the abstracted Attack and not the concretes through an abstract Execute()

  • Attack inheritors (this is Directional) implement the Execute()contract from the Attack base.

  • Although Directional is a Command Pattern user, it contains its own data-driven composition

Why

  • Abstract Attack contract allows new attack types w/out modifying existing systems increasing scalability

  • Inspector configuration enables rapid tuning without code changes

  • Enemy logic depends only on abstraction, not concretes, enforcing separation of concerns.

In the inspector I can easily tweak variables to rapidly create uniquely attacking enemies

how it works

  • I created a separate Combo class to serve as a data structure held by each EnemyType.

  • Combo holds a List<Attack> and traverses that list via implementation from the EnemyType

  • Each EnemyType can opt to traverse the list differently, as its an override serving as a foundation for later extension.

Why

  • Combo defines an attack sequence as data, while EnemyType controls how that data is executed

  • The same Combo can produce entirely different outcomes depending on the traversal applied by each EnemyType, allowing for simple diversification of combo attacks with with the same data.

  • New Enemies do not require new combo definitions, only using a traversal strategy, this speeds up iteration


Level Select Functionality - Greybox & Skinning

Scriptable Object Dungeon Configuration

Post Skinning:

how it works

  • I greyboxed usingTextMeshPro Button elements in Unity's Canvas in the hierarchy laying out where I wanted the buttons to be.

  • I used my own custom UIJitter script to make the buttons animate when hovered over using the IPointerEnterHandler and IPointerExitHandler interfaces

  • I hooked the UI Button's UnityEvent into my LevelSelect script to either increase or decrease the currently select level index, seen as the exposed variable CurrentPosIndex, and modified the transition with a TransitionTime

  • When then transition occurs, the player is lerped to the next index of the Positions array, while simulatenious calling Animate.Play() on my cam with the animation gobackcreating a smooth and interesting animation.

Why

  • UX Greyboxing to help design the User Journey, also helps make skinning go smoothly

  • Fast iteration using a simple design

how it works

  • I created a DungeonData script and inherited from ScriptableObject which holds all dungeon configuration data, like how many enemies and what enemies can appear and in what order.

  • I used the CreateAssetMenu editor attribute with a corrosponding filename and menuName variables to create the assets in the Project directory.

  • Each DungeonSO is utilized by a DungeonDB instance script in every level of the game.

  • The DungeonDB instance script is used to hook up all non-primary functionalites into one nice location, including the place where the Dungeon Data is stored and referenced from

  • That is depended on by other game objects for their functonality.

Why

  • Levels are adjustable at an asset level. This allowed me to alter levels without having to go into each individual scene, cutting down on iteration time.

  • This solution uses Data-Driven design and runtime immutability to decrease complexity in runtime scripts and separate concerns.


JSON Saved Level Progression w/ Locked Final Level

Procedurally Looping Encounter System - Performance Optimized

how it works

  • I used JSON in a custom DataSaver script to write to Unity's ApplicationPersistentDataPath file with the player's progress information.

  • The player's progress information is a separate data-structure used in this data write

  • To utilize the item saving, I turned it into the level progression mechanic. Meaning each item serves as the 'level complete' on the grocery list.

  • To program this, I created a separate storage class singleton class called GroceryItemStorage which uses the data from the DataSaver. The storage is referenced from the LevelSelect script in the Level Select scene. To check if the player has all items, if the player does it unlocks the final level

Why

  • I used a widely understood concept across all users to help the player understand their progression.

  • The player collects "Grocery Items" from a "Grocery List." They receive an item at the end of each level, checking it off their list

how it works

  • I created a RepeatWorld script that detects when an enemy dies and moves the entire level forward while the player stands still.

  • The world is split up into composable Encounters that contain enviormental assets and serve as the battleground between each enemy and the player.

  • The RepeatWorld script listens for an event fired by an enemy death and linearly interpolates all encounters forward, after moving, the most recent encounter which is now behind the player is re-positioned at the back of the encounters list.

Why

  • Encounters are discrete and composable modules that can be altered without changing the core system.

  • The world is procedurally repeated over and over, cutting down on draw calls if many encounters were loaded at once.

  • RepeatWorld listens for an enemy death event, avoiding polling which increases perfromance


Led Project Start & Initial Team Directing

Scheduled Team Meetings & Managed Team Sprints

Key production choices

  • I proactively stepped into a leadership role from the start.

  • I gathered everyone around a whiteboard asked every team member to brainstorm potential ideas.

  • I gave everyone a fair and equal chance to share their ideas on what we should work on.

  • I visually compiled all the ideas and organized them into genres for clarity.

  • I asked everyone to rank-rate all the ideas giving more points to ideas they liked more, and we chose the idea voted highest.

  • I then solicited more feedback and as a team decided to combine the top 3 Ideas together.

  • Assigned Roles based on specialty and confidence, asking each member what they were good at & their mindset. I didn't want to asign somebody to something they didnt want to do

Key production choices

  • Setup Team's Sprints via Miro

  • Scheduled Meetings & Managed Timeline, informed team of breaks and progress checks after a certain period of time

  • Led progress meetings being empathetic to conditions like burnout, breaks, or difficulties


Directed Art Assets & Created Art Approval System for Team Members

Managed Asset Handoffs between Team Members

Key production choices

  • Setup Asset Production Pipeline w/ Approval System

  • Our Production Pipeline:

  • Asset is assigned based on what we needed

  • Asset goes into ideation phase

  • After 1 to 2 ideations were complete, they would be handed off to me for approval of direction

  • If the art was going in the right direction I approved the layout & design and handed it back for it to be completed, I would also ask for how long they would think it would take to maintain a mental timeline for the entire project.

  • If it did not look like it matched the direction, I would ask for adjustments or alterations until it looked good.

Key production choices

  • Managed handoff between team members in order to coordinate asset production because some assets required multiple-role work.

  • Team members would eventually have a backlog of assets handing off between each other. Thus, I would take a more hands-off approach as our team worked better and better over the course of the jam

Hiring?
Want to get in touch?

LETS DISCUSS