[{"content":"","externalUrl":null,"permalink":"/tags/3/","section":"Tags","summary":"","title":"3","type":"tags"},{"content":"These are other projects I have shipped. Some may receive full write-ups in the future.\nCupid FPS # Event: BYU Spring Game Jam 2026\nTheme: Falling In Love\nEngine: Unreal Engine 5 Role: Director / Gameplay Programmer\nProject Length: 1 week\nOverview # A fast-paced first-person game where the player takes on the role of Cupid, navigating a shopping mall to break up incompatible couples and correctly pair individuals under time pressure.\nResponsibilities # Led team planning meetings and ensured milestones were met Designed and implemented core movement mechanics and level layouts Iterated quickly on movement and level design inspired by Ultrakill Integrated UI elements: toxic/healthy couple counters and a speedrun timer Built the gameplay loop, allowing players to reset and attempt faster runs Supported other programmers on weapon feel and combat as a resource Assisted both artists and programmers to fill in gaps, ensuring a complete game Bonevoyage # Event: BYU Fall Game Jam 2025\nTheme: Spooky\nEngine: Unreal Engine 5\nRole: Director / Gameplay Programmer\nProject Length: 1 week\nPrevious Next Overview # A humorous first-person visual novel where you play as a train conductor on the way to heaven. Passengers are skeletons, and you check tickets, those without are tossed out the window.\nResponsibilities # Led team planning meetings and ensured milestones were met Designed and implemented throwing mechanics with screen shake, FX, and ribbon trails Architected file system and project workflow ensuring a cohesive production pipeline Acted as technical lead, guiding programmers on gameplay systems (e.g., CSV dialogue management) Directed art style and art integration, importing assets and setting up scenes Built the main gameplay loop, tracking skeleton interactions for player progress Press Escape to Escape # Event: BYU Summer Game Jam 2025\nTheme: Escape\nEngine: Godot\nRole: Solo Developer\nProject Length: 1 week\nPrevious Next Overview # A rhythm-based first-person platformer inspired by Geometry Dash, where you escape a cyber city, dodging signs, billboards, cars, and trucks while sprinting through the streets.\nResponsibilities # Implemented core movement mechanics: wall running, variable jumps, and fast falling Whiteboxed and iterated level design using feedback to ensure fair and fun gameplay Created a checkpoint system for seamless resets Integrated responsive UI and music feedback Built editor scripts to optimize obstacle hitboxes and background construction Lego Fighter # Event: CS 455 Computer Graphics: Video Game Project Engine: Unreal Engine\nRole: Director / Game Programmer\nProject Length: 2.5 weeks\nOverview # Players control a LEGO sword fighter charging a castle to save the princess. Combat relies on simple block-and-attack mechanics, defeating all guards to win.\nResponsibilities # Designed core gameplay loop and combat systems Implemented AI using Unreal’s Behavior Trees, with turn-based attack patterns using tokens and nav-meshes inspired by Ghost of Tsushima Built level layouts and directed FX, animation, and art integration Developed UI systems integrated into gameplay Gimbo The Great # Event: BYU Fall Game Jam 2024\nTheme: Keep it Up\nEngine: Godot\nRole: Director / Game Programmer\nProject Length: 1 week\nPrevious Next Overview # A rhythm-based juggling clown simulator. Players control a clown catching objects to the beat, balancing on a ball while keeping the audience engaged. Tempo increases as juggling continues.\nResponsibilities # Designed core gameplay loop and juggling mechanics Balanced systems including stage stability, audience interactions, and BPM progression Directed and implemented all art and level setups Created UI, start screen, menu navigation, and high score save system Managed overall project systems to ensure cohesive gameplay and player feedback ","externalUrl":null,"permalink":"/blog/otherprojects/","section":"Blogs","summary":"Short-form projects exploring mechanics, rapid iteration, and gameplay systems.","title":"Additional Projects","type":"blog"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/blog/","section":"Blogs","summary":"","title":"Blogs","type":"blog"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/tags/combat/","section":"Tags","summary":"","title":"Combat","type":"tags"},{"content":"","externalUrl":null,"permalink":"/tags/combat-design/","section":"Tags","summary":"","title":"Combat Design","type":"tags"},{"content":"I\u0026rsquo;d love to hear from you. Feel free to reach out with questions, employment opportunities or just to chat.\nIan DeJesus\nEmail: gamedevdejesus@gmail.com\nPhone: (801) 645-1255\nI\u0026rsquo;ll do my best to respond within 1–2 business days.\n","externalUrl":null,"permalink":"/contact/","section":"Ian DeJesus Portfolio","summary":"","title":"Contact Me","type":"page"},{"content":"","externalUrl":null,"permalink":"/tags/cozy/","section":"Tags","summary":"","title":"Cozy","type":"tags"},{"content":"","externalUrl":null,"permalink":"/tags/cutesy/","section":"Tags","summary":"","title":"Cutesy","type":"tags"},{"content":" Summary # Role Technical Game Designer / Systems Designer Team Solo Engine Godot Target Platform Anbernic RG-40XXH (budget Linux handheld) Repo GitHub Duration 3 Months Design Doc About # Emily\u0026rsquo;s Interior Design Co. is an Animal Crossing–inspired interior design game built for my wife, an interior design student. Developed solo over three months alongside full-time studies, this project serves as the technical systems and optimization showcase in my portfolio. The focus was two-fold: creating a modular, designer-facing pipeline for rapid level iteration, and aggressive optimization to push an unplayable 2–4 FPS build to ~55 FPS on a budget $70 Linux handheld (Allwinner H700 / Mali-G31).\nProject Focus: Low-level Optimization, Asset Pipelines, and Custom Editor Tooling (For more design based breakdowns see my case studies on Snowball Showdown and Lantern of Souls.)\nTarget Hardware # Console Anbernic RG-40XXH CPU Allwinner H700, quad-core ARM Cortex-A53 @ 1.5GHz GPU Mali-G31 MP2 RAM 1GB Display 4\u0026quot; 640x480 (4:3) Storage 64GB microSD System 64-bit Linux I chose the RG-40XXH because it let me give Emily a physical, portable game she could play without a powerful PC. At around $70, it was affordable and already built for handheld play.\nThe tradeoff is that it\u0026rsquo;s designed for retro games, not modern 3D. Performance became a design constraint from day one, shaping everything from the renderer to how rooms are built. My goal was a stable 30 FPS at native resolution.\nFor details on how I acheived this goal look at Optimization.*\nRoom Decoration Logic # Getting the Objects into the Game # Early on it was clear I wouldn\u0026rsquo;t have time to model every decorative object, so I used an asset pack and hand-picked the objects I wanted. The pipeline:\nOBJ asset pack → Python export script (OBJ → GLB) → Godot import script → game-ready object\nThe import script sets up everything an object needs: collision, multiple skins, and game-specific attributes. For example, wall objects need a wall-raycast node to work with the placement system.\nI imported the first few objects by hand to learn what each model needed. Then I estimated the time to process hundreds of objects that way: around six months. Instead, I spent about two weeks building the export and import scripts, with AI assistance where needed. After that, any selected object could be brought into the game with minimal manual setup.\nExample: encoding game rules into the import script\nThis function moves an object\u0026rsquo;s collision onto the layers the game\u0026rsquo;s placement system expects.\nfunc change_collision_layers(scene_root_path: String): var inst = load(scene_root_path).instantiate() var collision_node: StaticBody3D = find_collision_node(inst) if collision_node: collision_node.set_collision_layer_value(1, false) collision_node.set_collision_layer_value(3, true) collision_node.set_collision_layer_value(8, true) collision_node.set_collision_mask_value(1, false) collision_node.set_collision_mask_value(3, true) collision_node.set_collision_mask_value(8, true) # ... pack the scene and save it back to disk Trimmed for readability. The full script is in the repo.\nSelecting Objects Logic # Each room has its own UI overlay, which controls which objects are available there. A bathroom can offer different furniture than a bedroom, and the player never scrolls through items that don\u0026rsquo;t fit.\nInventories are stored as Resource files, one per object type (floor, wall, table, and so on). The exception is rugs, which share a single inventory across every room. Each inventory is a dictionary mapping an object\u0026rsquo;s thumbnail path to its scene path. The UI loads only lightweight thumbnails up front, and the full 3D object is loaded only when the player selects it (see Soft Object References).\nPer-room inventories shown in the UI overlay extends Resource class_name SittingRoomWallInventory # Key: thumbnail path | Value: scene path @export var objects := { \u0026#34;res://Models/Final Models/Bedrooms/FtrSconceWall/Textures/FtrSconceWall_icon.png\u0026#34;: \u0026#34;res://Models/Final Models/Bedrooms/FtrSconceWall/FtrSconceWall.tscn\u0026#34;, \u0026#34;res://Models/Final Models/Tower/FtrArtStarryNight/Textures/mBody_Alb.png\u0026#34;: \u0026#34;res://Models/Final Models/Tower/FtrArtStarryNight/FtrArtStarryNight.tscn\u0026#34;, # ... } Tabs Per Object Type # The overlay is built around a TabContainer with one tab per category: floor, table, wall, wallpaper, and flooring. When a room loads, it hands each tab its matching inventory, and each tab populates itself.\nfunc assign_inventory_scripts(floor_inventory, placeable_inventory, wall_inventory, wallpaper_inventory, flooring_inventory): floor_furniture_tab.assign_inventory(floor_inventory) placeable_furniture_tab.assign_inventory(placeable_inventory) wall_furniture_tab.assign_inventory(wall_inventory) wallpaper_tab.assign_inventory(wallpaper_inventory) flooring_tab.assign_inventory(flooring_inventory) Because each room only passes in the inventories it needs, adding a new room means filling out a few resource files rather than writing new UI code.\nWall Mode Camera # The tab container also tells the camera when the player is on the wall tab, so the camera can shift to a better angle for placing wall objects.\nfunc update_is_wall_state() -\u0026gt; void: var changed = is_wall is_wall = (get_current_tab_control().name == \u0026#34;Wall Objects\u0026#34;) if changed != is_wall: camera.wall_update(is_wall) is_wall_change.emit(is_wall) Building the Grid # Each tab loops through its inventory and creates a panel per object with the thumbnail as its icon. The scene path is stored as metadata on the panel, so the object itself isn\u0026rsquo;t loaded until it\u0026rsquo;s needed.\nfunc load_objects(dict: Dictionary) -\u0026gt; void: # ... clear old children for key in dict.keys(): var panel = Panel.new() # ... size and style the panel var icon = TextureRect.new() icon.texture = load(key) # thumbnail only panel.add_child(icon) # Store the path, not a preloaded scene panel.set_meta(\u0026#34;scene_path\u0026#34;, dict[key]) add_child(panel) Controller Navigation # The target device has no mouse, so the entire overlay is navigated with the D-pad. The grid tracks a selected row and column, highlights the current item, and scrolls to keep it in view. A short input delay stops a held direction from skipping across the grid, and each move plays a UI sound for feedback.\nfunc _unhandled_input(event: InputEvent) -\u0026gt; void: # ... guard clauses (visibility, input cooldown) if event.is_action_pressed(\u0026#34;Down\u0026#34;): selected_row = min(row_count - 1, selected_row + 1) moved = true # ... Up, Left, Right if moved: selected_col = clamp(selected_col, 0, grid[selected_row].size() - 1) highlight_selection(grid[selected_row][selected_col]) _update_scroll_position(row_count) # ... play sound, start nav cooldown Selecting an Object # When the player presses Accept, the tab decides what to do based on its type. Wallpaper and flooring swap the room\u0026rsquo;s material directly. Everything else is loaded from its stored path and handed to the placement system, and the game switches into edit mode so placement logic takes over.\nfunc _handle_accept_action() -\u0026gt; void: var scene_path: String = selected_object.get_meta(\u0026#34;scene_path\u0026#34;, \u0026#34;\u0026#34;) # ... wallpaper and flooring branches swap room materials var packed_scene: PackedScene = get_resource(scene_path) placement_system.assign_selected_object(packed_scene, is_wall, in_resource_cache) game_state.switch_to_edit_mode() This system let me build many rooms with different inventories without manually populating each grid.\nPlacement Logic # Once an object is selected, the player moves it around the room, and the game gives instant feedback: the object turns green when it can be placed and red when it can\u0026rsquo;t. There are four placement modes:\nFloor objects Wall objects Table-placeable objects Rugs Floor Placement # Each frame, a ray is cast from the player camera through the cursor position. A collision mask makes the ray hit only the floor, never the walls, by setting matching layers on the ray in code and on the floor collider in the editor.\nfunc placing_object(): var query = create_ray() query.collision_mask = (1 if !selected_object.is_on_wall else 2) to_place_move_label.text = \u0026#34;To Place\u0026#34; var collision = camera.get_world_3d().direct_space_state.intersect_ray(query) if selected_object: if collision: selected_object.visible = true selected_object.transform.origin = collision.position can_place = selected_object.check_placement() else: selected_object.visible = false The moving object then uses its Area3D node to check for overlaps with other floor objects. Wall objects skip this check against floor objects.\nfunc check_placement() -\u0026gt; bool: if overlaps.is_empty(): placement_green() return true else: placement_red() return false This is simplified. The real function also handles the different object types, covered below.\nWall Placement # Wall objects use the same approach with a different collision mask, so the ray only hits walls and never the floor.\nquery.collision_mask = (1 if !selected_object.is_on_wall else 2) Once the ray hits a wall, a little linear algebra rebuilds the object\u0026rsquo;s basis from the wall\u0026rsquo;s normal so the object sits flush and faces the right way.\nif selected_object.is_on_wall: var basis = Basis.IDENTITY basis.z = collision.normal basis.y = Vector3(0, 1, 0) basis.x = basis.y.cross(basis.z) selected_object.basis = basis Y (Orange): always points straight up. Z (Purple): points out of the wall and matches the wall\u0026rsquo;s normal. X (Red): points right and is the cross product of Y and Z. Wall objects run the same overlap check as in check_placement(), but only against other wall objects.\nTable-Placeable Objects # Table-placeable objects (lamps, plants, and so on) behave like floor objects, with one addition. When they overlap a Table object, a ray is cast down from above them to find the table\u0026rsquo;s top surface, and the object snaps to that height.\n# ... continuation of check_placement() if object_type == 2: var ray_start = global_transform.origin + Vector3(0, 10, 0) # start above the object var ray_end = global_transform.origin + Vector3(0, -10, 0) # end below the object var space_state = get_world_3d().direct_space_state var query = PhysicsRayQueryParameters3D.new() query.from = ray_start query.to = ray_end query.collide_with_bodies = false query.collide_with_areas = true query.exclude = [area.get_rid()] # ignore the object itself var result = space_state.intersect_ray(query) if result and \u0026#34;object_type\u0026#34; in result.collider.get_parent().get_parent() \\ and result.collider.get_parent().get_parent().object_type == 1: position.y = result.position.y placement_green() return true Raycast to Table In retrospect, finding the item through node groups or a dedicated component lookup would be more robust than climbing parent nodes, which breaks if the scene tree changes. I would change this first with more time.\nRug Objects # Rugs are floor objects with special overlap rules. A rug can be placed anywhere, including under furniture, and other objects can be placed on top of a rug, but not on top of each other.\n# ... continuation of check_placement() # placing a rug: always allowed if object_type == 3: placement_green() return true # ... other type checks # placing any other object: only rugs may overlap var non_rug_found := false for overlap in overlaps: var object = overlap.get_parent().get_parent() if \u0026#34;object_type\u0026#34; in object and object.object_type != 3: non_rug_found = true break if not non_rug_found: placement_green() return true Wallpaper and Flooring Logic # Wallpaper and flooring are shaders that tile based on the length of the wall or floor they cover. Since room sizes vary (see Room Creation), the tiling has to adapt so the pattern always fits correctly.\nEach wall gets its own duplicate of the material so its tiling can match its own length.\nfunc change_wallpaper(instance: ShaderMaterial, instance_path: String): back_wall.material_override = instance.duplicate() front_wall.material_override = instance.duplicate() right_wall.material_override = instance.duplicate() left_wall.material_override = instance.duplicate() Flooring works the same way.\nRoom Creation # I had 14 rooms to build, each with different dimensions, entrances, and in some cases custom logic, and I was balancing coursework and a part-time job. I needed a scalable workflow, so I built editor-facing tools that made creating and customizing rooms quick.\nBase Room and Its Parameters # Every room starts from a reusable room.tscn made of six inward-facing planes. This takes advantage of Godot\u0026rsquo;s backface culling to create an enclosed space the player can still see into.\nVertical and Horizontal Size # The base room is a small box, but every room needs different dimensions, and resizing each wall by hand would not scale.\nI exposed horizontal and vertical size parameters in the Godot Inspector, plus an editor script that updates the room\u0026rsquo;s geometry in real time so I can preview dimensions while designing. At runtime, the same parameters are reapplied so the room matches what I set in the editor.\n# --- Setters for inspector/runtime --- func set_horizontal_size(size: int) -\u0026gt; void: if horizontal_size != size: horizontal_size = size update_walls() func set_vertical_size(size: int) -\u0026gt; void: if vertical_size != size: vertical_size = size update_walls() # --- Wall updates --- func update_walls() -\u0026gt; void: if not is_inside_tree(): return # Floor + ceiling floor.scale.x = horizontal_size + 8 floor.scale.z = vertical_size + 8 ceiling.scale = floor.scale # Horizontal scaling left_wall.position.x = floor.scale.x right_wall.position.x = -floor.scale.x front_wall.scale.x = right_wall.position.x - left_wall.position.x back_wall.scale.x = right_wall.position.x - left_wall.position.x # Vertical scaling front_wall.position.z = floor.scale.z back_wall.position.z = -floor.scale.z left_wall.scale.x = front_wall.position.z - back_wall.position.z right_wall.scale.x = front_wall.position.z - back_wall.position.z func _ready() -\u0026gt; void: update_walls() Navigating Between Rooms # Exploring the house is non-linear, so I needed to add entrances between rooms quickly and reliably. I built two tools: doors and open hallways.\nDoors # A reusable door scene handles room transitions. When the player enters a doorway, a transition shader plays and the player is moved to a designated entry point in the destination room. Because each door has its own spawn marker, a room can have multiple entrances without needing a separate room for each.\nThe destination scene and door are exposed as editable parameters, so I wire up connections in the Inspector instead of hardcoding them, and a GameSingleton handles the transition. This made it easy to set up and change connections across all 14 rooms.\nextends Node3D @export var marker: Marker3D @export var next_scene: String @export var next_door: String @export var transition_screen: Node3D func enter_portal(): GameSingleton.door_name = next_door transition_screen.exit = true transition_screen.enter = false transition_screen.next_scene = next_scene Hallways and Portals # For open entrances, I wrote a simple vertex shader that displaces part of a wall\u0026rsquo;s geometry, creating an opening the player can walk through.\nshader_type spatial; render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx; uniform vec3 center = vec3(0, 0, 0); uniform float scale = 1; // ... additional material and texture parameters void vertex() { // ... vertex and triplanar texture setup if (length(center) \u0026gt; 0.001) { if (abs(VERTEX.x - center.x) * 1. / scale \u0026lt; 0.2) { VERTEX.z -= 4.0; } } } // ... triplanar texture and fragment shader functions The shader displaces vertices near a center point, effectively cutting an opening in the wall.\nTo sell the effect, I placed several white planes around the opening with increasing opacity to simulate light bleeding through. The shader and planes only activate when a marker is assigned to the wall, and that marker controls both the position and width of the opening. That let me place an entrance anywhere a level needed one.\nfunc check_marker(): var mat := material_override as ShaderMaterial if not mat: return if marker and is_instance_valid(marker): # Only update when the marker actually moves if old_probe_position != marker.position: old_probe_position = marker.position fade_away.visible = true if name == \u0026#34;Left Wall\u0026#34; or name == \u0026#34;Right Wall\u0026#34;: fade_away.global_position.z = marker.global_position.z else: fade_away.global_position.x = marker.global_position.x fade_away.scale.x = marker.scale.x mat.set_shader_parameter(\u0026#34;center\u0026#34;, to_local(marker.global_position)) mat.set_shader_parameter(\u0026#34;scale\u0026#34;, marker.scale.x) else: # Revert to the default wall when no marker is assigned mat.set_shader_parameter(\u0026#34;center\u0026#34;, Vector3.ZERO) fade_away.visible = false The solution is simple, computationally cheap, and effective at communicating an open passage between rooms.\nOptimization # The target hardware shaped most of my technical decisions. Here are the changes, in order of impact.\nPerformance Results # The first playable build ran at 2-4 FPS on the RG-40XXH. By the end of development it ran at roughly 55 FPS, well past my 30 FPS target.\nOptimization Approx. gain Baked lighting (LightmapGI) ~20 FPS Vertex shading instead of fragment ~10 FPS Shadows removed ~5 FPS Anti-aliasing disabled ~5 FPS Figures are approximate, recorded during development, and not measured in a fixed order, so they don\u0026rsquo;t sum exactly to the final result. Soft object references and smaller tweaks account for the rest.\nBaked Lighting (~20 FPS) # Dynamic lighting was the primary bottleneck, and the first playable build ran at just 2-4 FPS. I used Godot\u0026rsquo;s LightmapGI to bake each room\u0026rsquo;s lighting ahead of time, so almost none of it is computed at runtime. After baking and iterating on the results, the game gained roughly 20 FPS.\nBecause room sizes vary, I baked using the largest possible room (10 × 10). That let me resize rooms during development while keeping the baked lighting, instead of re-baking for every configuration.\nVertex Shading Instead of Fragment (~10 FPS) # I forced vertex shading in the project settings for both the player character and all room objects. This gave roughly 10 FPS and let me render many more objects without worrying about shading overhead. The tradeoff is less precise lighting, which is hard to notice on a 4\u0026quot; screen.\nShadows Removed (~5 FPS) # I removed shadows from every object, including the player character. This gained about 5 FPS and is barely noticeable at this screen size.\nCompatibility Renderer and Anti-Aliasing (~5 FPS) # I built the game on Godot\u0026rsquo;s Compatibility renderer, its OpenGL-based option designed for low-end hardware. It was the only renderer I could realistically run on this device. The cost is losing some of Godot\u0026rsquo;s more advanced rendering features, which I worked around with baked lighting and stylized visuals.\nI also turned off anti-aliasing, which gave about 5 FPS at the cost of slightly rougher edges.\nSoft Object References # A room can offer hundreds of objects, and loading all of them at runtime was too expensive. Instead, the inventory UI holds only each object\u0026rsquo;s thumbnail and a path to its scene. When the player selects an object, the scene is loaded on demand and added to a cache, so adding more of the same object later is instant.\nClosing # Reception # Emily loved it. She pointed out the small details, enjoyed unlocking new content each day, and kept coming back for the cute aesthetic. More than once she told me it was the part of her day she looked forward to most.\nTakeaways # This write-up doesn\u0026rsquo;t cover everything. Saves, in-game notes, and sound design took many more hours.\nThe biggest lessons were:\nTooling pays off. Replacing a six-month manual import with a two-week pipeline made the rest of the project possible. It\u0026rsquo;s important to know what your designing for and who you are designing for. Because I knew the screen was 4\u0026quot; and I knew Emily would enjoy a more cartoony game I was able to optimize things that wouldn\u0026rsquo;t be readily noticible and kept with the artistic design of the game Short timelines reward scoping. I shipped something Emily loved in three months while working and studying. With more time, I would replace the hardcoded parent-node lookups (get_parent().get_parent() \u0026amp; objectType == 2) with exported references and enums, so the object system doesn\u0026rsquo;t break when the scene tree changes and it\u0026rsquo;s easier to understand while looking at code.\n","externalUrl":null,"permalink":"/blog/emilysinteriordesignco/","section":"Blogs","summary":"A cozy handheld interior design game I built for my wife","title":"Emily's Interior Design Co.","type":"blog"},{"content":"","externalUrl":null,"permalink":"/tags/experimental/","section":"Tags","summary":"","title":"Experimental","type":"tags"},{"content":"","externalUrl":null,"permalink":"/tags/fighting/","section":"Tags","summary":"","title":"Fighting","type":"tags"},{"content":"","externalUrl":null,"permalink":"/tags/fps/","section":"Tags","summary":"","title":"FPS","type":"tags"},{"content":"","externalUrl":null,"permalink":"/tags/game-jam/","section":"Tags","summary":"","title":"Game Jam","type":"tags"},{"content":"","externalUrl":null,"permalink":"/tags/horror/","section":"Tags","summary":"","title":"Horror","type":"tags"},{"content":"","externalUrl":null,"permalink":"/","section":"Ian DeJesus Portfolio","summary":"","title":"Ian DeJesus Portfolio","type":"page"},{"content":"","externalUrl":null,"permalink":"/tags/interior-design/","section":"Tags","summary":"","title":"Interior Design","type":"tags"},{"content":" Summary # Role Game Designer / Systems Designer Team 20 (8 designers/programmers, 12 artists) Engine Unreal Engine 5 Target Platform PC Repo Perforce Development Cycle April 2026–April 2027 About # Lantern of Souls is a stealth horror game developed as the 2027 capstone project for Brigham Young University\u0026rsquo;s Animation and Games program.\nYou play Thiago, a Soul Master guiding a lost soul through the ruins of an underworld temple toward its next life. Restless souls haunt the halls, forcing you to use light, stealth, and supernatural abilities to evade them, light the braziers, and illuminate the way home.\nOur 20-person team, consisting of eight designers and programmers and 12 artists, is developing this game over this school year.\nProject Focus: Long-term game design · Iteration · Finding the fun\nWarning! This article is still a work in progress, you may look at it, but know this isn\u0026rsquo;t the finished product. Timeline # Five months into development, we had gone from bad adventure stealth game to a great horror stealth game built around fear, uncertainty, and dangerous encounters. Here\u0026rsquo;s how we got there.\nA note on \u0026ldquo;we\u0026rdquo;: Design and programming decisions were highly collaborative. As design lead, I contributed across the project, and I will use \u0026ldquo;we\u0026rdquo; for shared decisions while calling out my individual contributions where relevant.\nFinding the Fun (Month 1-3.5) # Where We Started # Lantern of Souls began as a pitch from Mailen Cruz, later our project director. (Read the original pitch deck.) Its pillars:\nLight management: Light is a constantly running timer and the core risk-versus-reward mechanic. Bright light is safer for pathfinding but attracts enemies. Dim light offers stealth but risks the soul\u0026rsquo;s extinction. Stealth: Players sneak so wandering spirits can\u0026rsquo;t see them. Tone: \u0026ldquo;Dark, mysterious, atmospheric.\u0026rdquo; First Prototype: April 2026\nProblem: The pitch said the game was about retaining light, but we didn\u0026rsquo;t know what that would feel like to play.\nSolution: The team built a rough demo where light was a bar the player had to preserve. Sprinting and crouching drained it, light stations recharged it, and cube enemies patrolled. If the player\u0026rsquo;s light radius overlapped one, the game reset.\nOutcome: It was decently fun but too punishing. Sprinting could drain light almost instantly, so it felt like a trap instead of a tool.\nTakeaway: Light drain had to feel like a resource to manage, not a penalty for acting.\nThe earliest screenshot of the project, showing the player and the light radius. First Meeting In Class: May 4, 2026\nWe played the demo with our teacher and landed on a vision: make stealth fun through quick thinking, suspense, and intelligent choices, with a player who feels proactive instead of powerless. (Meeting minutes.)\nThe whiteboard from our first in-class meeting. What I changed: I wanted sprint and crouch to feel good before anything else. Crouching now shrinks the light radius and slows movement at no light cost, since waiting already drains light. Sprinting no longer drains light either, because being more detectable is its own drawback.\nOutcome: The game improved. Enemies gained a perception component instead of detecting players on overlap, and players weren\u0026rsquo;t punished for using their tools. The team tried many abilities and scrapped most, keeping two: a Distraction to lure enemies, and the Shadow Dash.\nEarly soul abilities: the Shadow Dash, Distraction, and a scrapped Light Pulse. Research\nTo help us develop our game we studied Aragami, Splinter Cell: Chaos Theory, Thief 2, Hitman, Metal Gear Solid V, Dishonored, Metroid Dread, and Mark of the Ninja. Three findings shaped us:\nOperating in the dark didn\u0026rsquo;t feel good. Players wanted information to make informed choices. Enemies had states. They patrolled, then detected the player after a grace period, so players could watch, then act. Slow, methodical play mattered even more given our no-killing rule. The Shadow Dash\nI\u0026rsquo;ll walk through the Shadow Dash which was one of our earliest promising abilities, to show how I iterated on core features at this time in development.\nPhase 1 (May 18): The goal was a movement option that makes the player undetectable. With good timing, the player could phase through an enemy unseen. Players liked it, but it felt uncontrollable. They were just hitting a button to speed away, which isn\u0026rsquo;t methodical.\nThe early Shadow Dash. Phase 2 (May 25): Inspired by the teleports in Aragami and Dishonored, I let players aim the teleport and release it to a spot of their choosing. Players loved the control.\nPhase 3 (June 2-8): I added teleporting down, and Becca Ellington added teleporting up, so players could teleport anywhere they could see. It quickly became a core element, and I spent the next week polishing it, for example keeping the player from clipping into walls mid teleport.\nThe Shadow Dash was core to this build, but it was later replaced. I\u0026rsquo;ll cover why in a later section.\nOther things I worked on: Ontop of The Shadow Dash I worked with the team to implement and iterate upon various parts of the game including: Sprint, Crouch, General Detectability, Enemy AI and Levels.\nEnd of month three: At this point in development we had gotten to where we had planned from the beginning the core loop existed, players could learn our abilities, and we could build ten-minute levels. But one problem was nagging the team. The most important feature: the Player-Enemy Interaction wasn\u0026rsquo;t working.\nThe Turning Point # In the beginning of August, we were moving to Unreal Engine 5.7.4 when Perforce went down. With no access to the project, we took a break and played some games.\nStumbling onto something: Some teammates wanted to play The Legend of Zelda: Breath of the Wild. As a joke, we tried playing it as a stealth game.\nIt was surprisingly fun. The Guardians and Yiga Clan created unexpected threats without relying on the familiar vision cones and detection indicators we had been building. Sneaking between ruins and boulders felt tense because we couldn\u0026rsquo;t always predict where danger would come from.\nWe started asking a different question: What if we gave players less information about enemies instead of more?\nFinding the word: We started brainstorming how our enemies, who are lost souls, could feel better. Mailen said she wanted something that would \u0026ldquo;scare the player.\u0026rdquo; She had said it before, but we\u0026rsquo;d always treated it as secondary to making the player feel powerful as a Soul Master. This time it clicked. We realized we enjoyed the instant-kill moments in games like Aragami because they fed a feeling of fear that now seemed important to our game.\nThe realization: At the end of the second day without Perforce, we were talking again and during our conversation I had an epiphany. I asked our producer, Jacob, to boot up Outer Wilds: Echoes of the Eye, and asked Mailen whether its creepy atmosphere was what she was going for. It was exactly what she wanted.\nThat was when we realized this wasn\u0026rsquo;t an adventure stealth game. It was a horror stealth game.\nThat explained why the relationship between the Guard and the player felt wrong. In adventure stealth, you\u0026rsquo;re the assassin, so guards have to be dumb to make you feel powerful. But our player couldn\u0026rsquo;t attack, so they weren\u0026rsquo;t powerful, they were bored, just bypassing enemies. In stealth horror, not being able to fight back is the experience. The player is the victim.\nRebuilding: We stopped planning for endless simple enemies and started planning a few powerful, terrifying ones, studying Alien: Isolation, Echoes of the Eye, and Five Nights at Freddy\u0026rsquo;s: Secret of the Mimic. Andrew, whom I would later make lead level designer, designed a new map with help from me and a few others. It was a playground for testing our ideas about fear and stealth, built on two core ideas:\nOpen-ended levels with many ways for both you and the enemy to move. Not knowing where an enemy might be felt much scarier. Waypoints, not tracks: Enemies now move between waypoints instead of following a fixed route, so every encounter carries more uncertainty. We also held a full meeting dedicated to redesigning the enemy from the ground up. I came in with a structure: walk through each detection state (patrol, investigate, attack) and ask what the player should feel in each one, then how we\u0026rsquo;d make them feel it. Most of what I contributed were the questions that turned vibes into design decisions:\nHow does the enemy actually see the player? We settled on enemies that see only in front of them but hear you behind them unless you crouch. What does \u0026ldquo;slow and methodical\u0026rdquo; actually mean? I pushed for enemies about twice the player\u0026rsquo;s size, slow enough that you can\u0026rsquo;t rush past but not so slow it\u0026rsquo;s painful. We want insta-kill deaths: Should we have a cutscene on death, or a chase? I narrowed the options to three paths: instant-death cutscene, inevitable death, or a chase with a possible escape. I decided after the meeting to have the inevitable death option, which everyone was leaning towards. I also argued for moving away from rigid vision cones toward a more nebulous sense of detection as was brought up by Jacob and Becca during our previous discussions, and I called the shadow dash into question with our new enemies, despite it being something I worked onas it made the player too powerful and didn\u0026rsquo;t jive well with our new version of stealth. (Meeting minutes.)\nPaper prototypes: Andrew, Jacob, and I wrote new enemy rulesets, drew the map on paper, and recruited people outside the team to play it. Watching them showed us which enemy behaviors made encounters scary and which didn\u0026rsquo;t.\nThe stealth playground we used for paper prototypes. The pink triangles are crevices the player can hide in, much like the lockers in Alien: Isolation: they give protection but limit visibility and movement. We added a crevice check the enemy could perform, which forced the player into a quick time event to hide the soul. Players liked it. The blue squares contain a black square representing a box the player can hide behind. Boxes give more visibility and movement, but no protection from enemies approaching from the same side as the player. Changing my mind on the watcher: The meeting also raised the idea of revamping the watcher, our second enemy, to fit the new horror direction. I was against it. We were on a tight schedule for mid-September and I didn\u0026rsquo;t want more moving parts. Then a teammate suggested the watcher could alert the guards instead of just draining the player\u0026rsquo;s life, and the more I thought about it, the more I liked it. So I made the case in writing, as a list of pros the team could weigh. The watcher would scream when it spotted you and give the guards a point to investigate, and the player would have seconds to break line of sight before a guard closed in.\nThe arguments I leaned on most:\nSimple: Changing their behavior from looking around and draining to sending out an event dispatch that alerts guards is incredibly easy and doesn\u0026rsquo;t change things like movement, animation, or general design.\nPuts the focus back on stealth: The player is instantly put into the panic mode we wish to put them in when the guard is investigating (\u0026ldquo;If I don\u0026rsquo;t do something now, I could die\u0026rdquo;).\nAugments our already interesting design for the guard: Like in a prison escape movie: the spotlights don\u0026rsquo;t in themselves carry fear, but rather only cause the fear of what\u0026rsquo;s going to happen next if you are caught.\nI also pointed out that the watcher could absorb part of the proposed bird enemy\u0026rsquo;s proximity-alarm role, so we could test that kind of design before committing to a whole new enemy type. The team went with it, and the alerting watcher is still in the game today.\nmy original discord proposal for the alerting watcher The playtest: Perforce came back on day four, and over the next few days we rebuilt our enemies and levels to match the new designs. I was the primary implementer, building the enemy behavior in Unreal with State Trees, with Jacob and Becca assisting. One night, as we neared the finish line, I stayed late adding placeholder sounds and a simple jump scare.\nThe next day, we put the build on the TV, and it was a hit. One developer literally fell out of their chair when the enemy appeared. There were screams and laughter, and people from other labs wandered over to see what the fuss was about. They played it and loved it too.\nWe had finally struck gold. Now it was time to do the actual mining.\nIterating on The Vertical Slice (Month 3.5-Present) # The BYU Capstone Project works a little differently from other programs: we don\u0026rsquo;t try to make a full game. Instead, we build a vertical slice, a demo polished enough that someone can pick it up and understand the essence of the game within a ten-minute experience.\nWith the hiding, stealth, and enemy behavior validated, we moved on to building the full slice. That meant creating and refining:\nPlayer abilities A fully fleshed-out level Sub-objectives to give players reasons to stay in danger Both enemies: the Guard and the Watcher Anything else the player needed to enjoy and understand the core of our game Ongoing documentation # I\u0026rsquo;ll be adding more breakdowns of design decisions, reasoning, and player experience here soon.\n","externalUrl":null,"permalink":"/blog/lanternofsouls/","section":"Blogs","summary":"Stealth Horror Game with Mystical Theming","title":"Lantern of Souls","type":"blog"},{"content":"","externalUrl":null,"permalink":"/tags/level-design/","section":"Tags","summary":"","title":"Level Design","type":"tags"},{"content":" About # I have always loved 3D 1vMany fighting games. Many of my favorite games come from studios like Sloclap, with standout titles such as Absolver and Sifu; Sucker Punch Productions, with the Ghost series (Ghost of Tsushima and Ghost of Yōtei); and Ubisoft Montréal, with For Honor.\nI am also a practitioner of HEMA (Historical European Martial Arts) and have spent a long time studying the KDF tradition of fencing. My passion for martial arts, combined with my love for combat design, led me to create Lichtenauer, a game that puts you in the shoes of the historical figure Johannes Lichtenauer, the legendary grandmaster associated with the German martial arts tradition that flourished from the 14th through 16th centuries.\nEverything about Lichtenauer is designed around one central fantasy:\nYou are the Grandmaster. # Ongoing Documentation # This write-up is still in progress. I’ll be expanding this section soon with deeper design and technical breakdowns.\n","externalUrl":null,"permalink":"/blog/lichtenauer/","section":"Blogs","summary":"Medieval German Longsword Combat","title":"Lichtenauer","type":"blog"},{"content":" Role Technical Game Designer / Combat Designer Development Solo Developer Art Provided by Class Engine Unreal Engine 5 Duration 2.5 Weeks Design Doc About # Radioactive Rumble is a 2D fighting game developed over two weeks, where I was responsible for all gameplay systems and animation implementation. The game\u0026rsquo;s fighting style is inspired by Muay Thai, a martial art I practice, and I aimed to translate its rhythm, pressure, and attack variety into gameplay. I handled the full motion capture pipeline: recording, cleaning, refining, and integrating animations directly into a responsive combat framework. Beyond animation, I implemented core fighting game systems including a super meter, move-specific properties, and combat state management. Different attacks were designed with unique attributes such as super armor and variable pushback, requiring careful tuning of hit detection, recovery frames, and player feedback to maintain both balance and impact.\nCombat Design and Implementation # Overview # The game draws its stylistic roots from Muay Thai, a martial art I practice. I chose this style because as a martial artist I am interested in integrating the real-life combat techniques and strategy I find endlessly fun in sparring into games in a way that more people can enjoy. I also am deeply interested in how older fighting games like \u0026ldquo;Virtua Fighter\u0026rdquo; and \u0026ldquo;Fighters Destiny\u0026rdquo; on the N64 used more limited movesets yet managed to create a very complex fighting experience. Because of this I built the combat around two design pillars:\nA focus on High-Low combat found in Muay Thai. A smaller, more realistic moveset that encourages intelligent combat tool usage. Offense # Offensively the character has four categories of moves, each with high-low variants:\nQuick Attacks Push Attacks Medium Attacks Close Range Attacks Each move is defined using a shared attack data structure:\nAttack Struct: Defines all shared attack properties such as startup frames, active frames, recovery, damage, and hit reactions. Attack variables stored in Fighter_BP. These allow the character to reference the currently active attack and apply its properties during combat. Jab attack implementation using the shared attack struct. The blueprint pulls timing and damage values from the struct. Teep attack example. Shows reuse of the attack struct while customizing animation, knockback, and hitbox behavior. Centralizing these attributes into a single struct allowed me to iterate quickly. I could swap animations, adjust frame timing, tweak hit stun, or modify impact feedback without restructuring the combat logic. This made it significantly easier to refine game feel by simply adjusting values in the editor.\nQuick Attacks # Fast, low damage attacks designed for gauging distance, interrupting combos, and setting up attacks of your own. While safe for the most part, their low damage makes them easy to exploit; the heavy hitting, hyper-armored close range attacks blast through them, and their mid-range hitboxes struggle against a well timed push attack.\nThese attacks come in two forms:\nThe Jab (Executed by pressing Y on the Xbox controller) The Low Kick (Executed by pressing X on the Xbox controller) Push Attacks # Sometimes an opponent gets too close for comfort, or you need to back them into a corner, that\u0026rsquo;s what push attacks are for. With the longest hitboxes in the game, medium damage, and a knockback effect, these attacks are built for keeping opponents at arm\u0026rsquo;s length. They are specifically designed to counter close range attacks by making it impossible for those attacks\u0026rsquo; small hitboxes to connect. However, their longer recovery means timing is critical, a well executed parry, block, or dash from your opponent can leave you wide open for devastating return fire.\nThese attacks come in two forms:\nThe Straight (Executed by pressing Y and tapping forward on the Xbox controller) The Teep (Executed by pressing X and tapping forward on the Xbox controller) Medium Attacks # Slower, mid-ranged, higher damage attacks used to mix up combo timing and act as heavy hitters. These attacks are meant to keep your opponent guessing and punish misreads and mistiming. They shine as mid-combo inclusions and as punishments for whiffed push attacks and close range attacks. However, if not used correctly they are vulnerable to all other attacks especially quick attacks, which can exploit their slower startup.\nThese attacks come in two forms:\nThe Hook (Executed by pressing Y and L2 on the Xbox controller) Body Kick (Executed by pressing X and L2 on the Xbox controller) Close Range Attacks # Devastating, high damage attacks equipped with hyper-armor, these are built for getting in your opponent\u0026rsquo;s face and dealing catastrophic damage. Their small hitbox and slow startup are offset by the sheer punishment they deliver, depleting a significant portion of your opponent\u0026rsquo;s health if they land. Best used as accent pieces in combos or as combo breakers against opponents trying to overwhelm you, they blast through weak quick attack and medium attack strings but struggle against the spacing of push attacks. Their slowness can also be turned into an advantage, punishing overly aggressive parry-chasers.\nThese attacks come in two forms:\nThe Elbow (Executed by pressing Y and R2 on the Xbox controller) The Knee (Executed by pressing X and R2 on the Xbox controller) High-Low Mixups # To reinforce the high-low design pillar, lower attacks are given longer hitboxes while high attacks deal more damage. This creates a persistent tradeoff for the player, lower attacks are safer and better for controlling space, but high attacks punish harder when they land. In practice this means players can\u0026rsquo;t rely on a single attack height and must actively mix up their approach to keep opponents guessing, mirroring the rhythm of real Muay Thai where switching between levels is key to breaking down an opponent\u0026rsquo;s defence.\nDefense # Defensively the character has three categories of maneuvers, each with their own strengths and weaknesses:\nBlock Parry Dash Block # The safest defensive option, but the most passive. Blocks mitigate incoming damage but offer no counter-stun and no avenue to turn the tide. You\u0026rsquo;re simply absorbing the hit. Best used as a last resort when no better option is available.\nThere are two types of blocks:\nHigh Block (Executed by pressing L1 on the Xbox controller) Low Block (Executed by pressing R1 on the Xbox controller) Parry # High risk, high reward. By tapping and releasing a block button within a tight timing window, a parry reverses the stun your opponent was about to impose on you, and doubles it back on them, opening up a punish window. Excellent for shutting down repetitive combo strings or opponents over-relying on close range attacks. Miss the timing though, and you\u0026rsquo;ll eat the full hit.\nThere are two types of parries:\nHigh Parry (Executed by tapping L1 on the Xbox controller) Low Parry (Executed by tapping R1 on the Xbox controller) Dash # Versatile and usable both offensively and defensively, the dash lets you blitz in and out of range to avoid attacks and create punish opportunities. Particularly effective in the midrange or when your opponent is cornered, keeping them guessing on your next move. Bad when you yourself are against the wall.\nDash (Executed by double tapping the left stick left or right on Xbox) Animation and Other Artistic Implementation # While I was not in charge of constructing the characters or backgrounds, I did capture, refine, and implement all of the rigs, animations, and FX found in the game. In this section I will walk through my workflow.\nStep 1: Capturing the Mocap # For this project my school used XSens, a popular motion capture suit and software. I instructed someone who could fit the suit how to perform the Muay Thai moves and reactions I wanted the fighters to execute. I then recorded and captured all of the movements and exported them as an FBX, which I pulled into Maya.\nStep 2: Retargeting the Mocap in Maya # Once in Maya I used Human IK, one of Maya\u0026rsquo;s rigging tools, to retarget the animations onto a standard Mixamo rig. I chose the Mixamo rig for two reasons: I wanted the option to pull additional animations from Mixamo, such as emotes, into Unreal without import issues, and I wanted to ensure both the Scientist and Old Man Phil shared the same rig. Running both characters through Mixamo was the simplest solution.\nStep 3: Fixing the Mocap Information in Maya # After retargeting, I opened Maya\u0026rsquo;s animation timeline to clean up inconsistencies in the mocap data; things like open hands that needed to be closed. I also addressed a common beginner error where the performer punches sideways instead of forward, which is a difficult habit to correct in someone who hasn\u0026rsquo;t trained the movement before. Once the data was clean I made the animations slightly more stylized. While it was important to me to keep the fighting style grounded in real Muay Thai, video game attacks still need to be readable, so I exaggerated certain movements just enough to make them clear to the player. I then exported the animations as FBX files.\nStep 4: Making Final Touches in Unreal # Once in Unreal I imported the animations and verified everything looked correct on the characters, playing through the game and making real time edits directly in Unreal\u0026rsquo;s animation editor. Once I was happy with the animations I created FX and embedded them directly into Unreal\u0026rsquo;s Anim Montages, a system I hadn\u0026rsquo;t worked with before. Anim Montages allowed me to attach effects like hit, block and parry sparks to specific frames of an animation, which gave me much tighter control over the timing of combat feedback than I had anticipated.\nImplementing a Niagara Particle System in a hit reaction AnimMontage. Other Things I Learned in the Process # On top of combat design and getting animations to reflect the moves and reactions I wanted players to see, one of the most important things I learned during this project was Combat Feel.\nIt’s one thing to give players the ability to throw punches and kicks and react to attacks. It’s another thing entirely to make those attacks feel Crunchy, Impactful, and Satisfying, and that’s what Combat Feel is really about.\nSo here are the things I did to make combat feel good:\nHitstop and Camera Shake: The bread and butter of any good combat experience. Hitstop pauses the game for just a moment when an attack connects, giving players time to actually see the impact. Camera shake makes the damage felt for both for the attacker and the defender, (Which is one of the things I think make \u0026ldquo;Super Smash Bros: Melee\u0026rdquo; so good, their almost comical overuse of camera shake makes the game feel really weighty). By tuning these together, I was able to make every attack feel Crunchy and Satisfying. This project is where I learned Unreal’s approach to implementing both techniques.\nCutting the Reaction Animation in Half: When I first implemented “taking damage” animations, they felt disconnected from the attack even though they were lined up perfectly. It didn’t make sense until I played \u0026ldquo;Street Fighter 2\u0026rdquo; on the SNES and noticed most reactions were essentially one frame, usually at the apex. During hitstop, you don’t need the wind-up, you need the result. Trimming reactions to their most exaggerated frame helped sell the Impactfulness of every strike.\nSlow In and Out: Some punches and kicks felt loose even though they moved at realistic speeds. The issue wasn’t the full animation, it was the apex frames lacking Crunch, Speed, and Power. By adding anticipation, accelerating the impact frames, and letting recovery breathe a bit more using Slow In and Slow Out, each punch suddenly had much more umph behind it.\nFX: Sparks and bursts go a long way after everything else is implemented. They add explosive power and make the game feel like a game. Layered with hitstop, camera shake, and tightened reactions, they complete the feedback loop and make every hit feel like it matters.\nConclusion # I had a lot of fun with this project. As someone who is extremely interested in Combat Design and fighting in general, it was exciting to make my first real fighting game and build systems around something I actively practice.\nI loved learning how to design moves with different abilities and use cases, implement them inside a game engine, and most importantly make them feel great. I also thought it was interesting to learn how to collect, clean, retarget, and integrate motion capture animations using other DCCs and getting them into Unreal. It is something I definitely want to keep exploring in future projects.\nMore than anything, this project made me feel confident and excited to continue creating, studying, and refining my combat systems so I can give players fun, diverse tools that make every fight feel awesome.\n","externalUrl":null,"permalink":"/blog/radioactiverumble/","section":"Blogs","summary":"Radioactive Themed Fighting Game","title":"Radioactive Rumble","type":"blog"},{"content":"","externalUrl":null,"permalink":"/tags/relaxing/","section":"Tags","summary":"","title":"Relaxing","type":"tags"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":" About # A game about penguins fighting each other in a snowball fight. The Story Behind The Game I have always loved local multiplayer games. Some of my fondest memories growing up were going to a friend’s house after school or during the summer and gaming it up until dinner time. Those memories were filled with laughs, feuds, strategy, and the feeling that you could beat your friends if you just kept playing.\nAs online multiplayer became more common, many of those experiences moved online. While I feel like online multiplayer has given me some of those experiences, I believe something special is lost when you aren\u0026rsquo;t sitting next to the people you\u0026rsquo;re playing with. There are still games that capture that feeling, though. Games like Gang Beasts, Mario Party, and Party Animals bring us together in the same room and create the kind of chaotic, memorable moments I loved growing up.\nSo when my friend asked me to be the gameplay programmer and designer for a local multiplayer game about penguins battling in snowball fights, I jumped at the opportunity. I wanted to create something that captured that same feeling: chaotic matches, friendly rivalries, plenty of laughs, and the feeling that you could beat your friends if you just played one more round.\nAnd I think we captured some of that magic.\nOngoing Documentation # This write-up is still in progress. I’ll be expanding this section soon with deeper design and technical breakdowns.\n","externalUrl":null,"permalink":"/blog/snowballshowdown/","section":"Blogs","summary":"Competitive Party Game with Snowball Weilding Penguins","title":"Snowball Showdown","type":"blog"},{"content":"","externalUrl":null,"permalink":"/tags/system-design/","section":"Tags","summary":"","title":"System Design","type":"tags"},{"content":"","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"}]