Role: Programmer
Tool: Unity
Team: CiGA Game Jam 2024 (Limited and Limitless), Team of 6
Timeline: 48 hours
Completion Date: July 7, 2024
Role: Programmer
Tool: Unity
Team: CiGA Game Jam 2024 (Limited and Limitless), Team of 6
Timeline: 48 hours
Completion Date: July 7, 2024
Role: Programmer
Tool: Unity
Team: CiGA Game Jam 2024 (Limited and Limitless), Team of 6
Timeline: 48 hours
Completion Date: July 7, 2024
Play ->
Play ->
Play ->
GAME DESIGN
GAME DESIGN
GAME DESIGN
design pillars
design pillars
design pillars
Lifespan as a Resource
Lifespan as a Resource
Lifespan as a Resource
The player's limited lifespan drives strategic decisions, making every second crucial for progress.
The player's limited lifespan drives strategic decisions, making every second crucial for progress.
The player's limited lifespan drives strategic decisions, making every second crucial for progress.
Failure as Experience
Failure as Experience
Failure as Experience
Each death leaves a meaningful impact on the game world, encouraging adaptation and new strategies.
Each death leaves a meaningful impact on the game world, encouraging adaptation and new strategies.
Each death leaves a meaningful impact on the game world, encouraging adaptation and new strategies.
Emergent Problem-Solving
Emergent Problem-Solving
Emergent Problem-Solving
Each death leaves a meaningful impact on the game world, encouraging adaptation and new strategies.
Each death leaves a meaningful impact on the game world, encouraging adaptation and new strategies.
Each death leaves a meaningful impact on the game world, encouraging adaptation and new strategies.
mechanics
mechanics
mechanics
PROGRAMMING
PROGRAMMING
PROGRAMMING
Level-Based Occlusion Culling
Level-Based Occlusion Culling
Level-Based Occlusion Culling
This code manages room visibility and camera focus dynamically in a multi-room environment, ensuring smooth transitions and optimized performance.
Room Visibility Control:
The
ShowActiveRoom
() method activates the visual components (MeshRenderer
,VisualEffect
,SpriteRenderer
,Canvas
) of the current room while deactivating them in others.It sets the virtual camera to follow the floor of the active room, keeping the player's focus on the relevant area and reducing distractions.
Room Initialization:
The
OnSceneLoad
() method initializes the room list from the environment and retrieves the virtual camera reference when the scene loads.
There are several advantages to having a custom occlusion culling:
Performance: Only the active room is rendered, reducing GPU load.
Focus: Keeps the player centered on the active area, avoiding distractions.
Dynamic Gameplay: Smooth transitions between rooms maintain immersion.
Scalability: Simplifies managing and expanding environments.
Below is the code snippet used for loading in the rooms when the level starts and rendering only the active room during runtime:
This code manages room visibility and camera focus dynamically in a multi-room environment, ensuring smooth transitions and optimized performance.
Room Visibility Control:
The
ShowActiveRoom
() method activates the visual components (MeshRenderer
,VisualEffect
,SpriteRenderer
,Canvas
) of the current room while deactivating them in others.It sets the virtual camera to follow the floor of the active room, keeping the player's focus on the relevant area and reducing distractions.
Room Initialization:
The
OnSceneLoad
() method initializes the room list from the environment and retrieves the virtual camera reference when the scene loads.
There are several advantages to having a custom occlusion culling:
Performance: Only the active room is rendered, reducing GPU load.
Focus: Keeps the player centered on the active area, avoiding distractions.
Dynamic Gameplay: Smooth transitions between rooms maintain immersion.
Scalability: Simplifies managing and expanding environments.
Below is the code snippet used for loading in the rooms when the level starts and rendering only the active room during runtime:
This code manages room visibility and camera focus dynamically in a multi-room environment, ensuring smooth transitions and optimized performance.
Room Visibility Control:
The
ShowActiveRoom
() method activates the visual components (MeshRenderer
,VisualEffect
,SpriteRenderer
,Canvas
) of the current room while deactivating them in others.It sets the virtual camera to follow the floor of the active room, keeping the player's focus on the relevant area and reducing distractions.
Room Initialization:
The
OnSceneLoad
() method initializes the room list from the environment and retrieves the virtual camera reference when the scene loads.
There are several advantages to having a custom occlusion culling:
Performance: Only the active room is rendered, reducing GPU load.
Focus: Keeps the player centered on the active area, avoiding distractions.
Dynamic Gameplay: Smooth transitions between rooms maintain immersion.
Scalability: Simplifies managing and expanding environments.
Below is the code snippet used for loading in the rooms when the level starts and rendering only the active room during runtime:
public void ShowActiveRoom(GameObject currentRoom)
{
foreach (GameObject room in rooms)
{
GameObject roomFloor = room.transform.GetChild(0).gameObject;
if (currentRoom == room)
{
virtualCamera.Follow = roomFloor.transform;
foreach (MeshRenderer mr in room.GetComponentsInChildren<MeshRenderer>())
{
mr.enabled = true;
}
foreach (VisualEffect vfx in room.GetComponentsInChildren<VisualEffect>())
{
vfx.enabled = true;
}
foreach (SpriteRenderer sr in room.GetComponentsInChildren<SpriteRenderer>())
{
sr.enabled = true;
}
foreach (Canvas canvas in room.GetComponentsInChildren<Canvas>())
{
canvas.enabled = true;
}
}
else
{
foreach (MeshRenderer mr in room.GetComponentsInChildren<MeshRenderer>())
{
mr.enabled = false;
}
foreach (VisualEffect vfx in room.GetComponentsInChildren<VisualEffect>())
{
vfx.enabled = false;
}
foreach (SpriteRenderer sr in room.GetComponentsInChildren<SpriteRenderer>())
{
sr.enabled = false;
}
foreach (Canvas canvas in room.GetComponentsInChildren<Canvas>())
{
canvas.enabled = false;
}
}
roomFloor.SetActive(true);
}
}
void OnSceneLoad(Scene scene, LoadSceneMode mode)
{
GameObject environment = GameObject.Find("ENVIRONMENT");
if (environment == null)
{
Debug.LogWarning("No environment found in scene: " + scene.name + " maybe this is not the game scene. Returning.");
return;
}
rooms.Clear();
for (int i = 0; i < environment.transform.childCount; i++)
{
rooms.Add(environment.transform.GetChild(i).gameObject);
}
virtualCamera = GameObject.Find("Virtual Camera").GetComponent<CinemachineVirtualCamera
public void ShowActiveRoom(GameObject currentRoom)
{
foreach (GameObject room in rooms)
{
GameObject roomFloor = room.transform.GetChild(0).gameObject;
if (currentRoom == room)
{
virtualCamera.Follow = roomFloor.transform;
foreach (MeshRenderer mr in room.GetComponentsInChildren<MeshRenderer>())
{
mr.enabled = true;
}
foreach (VisualEffect vfx in room.GetComponentsInChildren<VisualEffect>())
{
vfx.enabled = true;
}
foreach (SpriteRenderer sr in room.GetComponentsInChildren<SpriteRenderer>())
{
sr.enabled = true;
}
foreach (Canvas canvas in room.GetComponentsInChildren<Canvas>())
{
canvas.enabled = true;
}
}
else
{
foreach (MeshRenderer mr in room.GetComponentsInChildren<MeshRenderer>())
{
mr.enabled = false;
}
foreach (VisualEffect vfx in room.GetComponentsInChildren<VisualEffect>())
{
vfx.enabled = false;
}
foreach (SpriteRenderer sr in room.GetComponentsInChildren<SpriteRenderer>())
{
sr.enabled = false;
}
foreach (Canvas canvas in room.GetComponentsInChildren<Canvas>())
{
canvas.enabled = false;
}
}
roomFloor.SetActive(true);
}
}
void OnSceneLoad(Scene scene, LoadSceneMode mode)
{
GameObject environment = GameObject.Find("ENVIRONMENT");
if (environment == null)
{
Debug.LogWarning("No environment found in scene: " + scene.name + " maybe this is not the game scene. Returning.");
return;
}
rooms.Clear();
for (int i = 0; i < environment.transform.childCount; i++)
{
rooms.Add(environment.transform.GetChild(i).gameObject);
}
virtualCamera = GameObject.Find("Virtual Camera").GetComponent<CinemachineVirtualCamera
Dialogue Controller
Dialogue Controller
Dialogue Controller
The DialogueController
class manages NPC interactions and dialogue using Unity and the Ink storytelling system, developed by Inkle. When the player presses "Q," nearby NPCs are detected via a spherical overlap, and the closest one is selected. If dialogue is initiated, the system enters dialogue mode, displaying the NPC's name and dialogue while pausing gameplay and stopping the game timer.
The EnterDialogueMode
() method initializes the Ink story from the NPC's script and updates the dialogue panel. Conversations progress through ContinueStory
(), fetching the next segment of dialogue, and conclude when no further text is available, triggering ExitDialogueMode
(). This restores gameplay elements and clears the panel, ensuring a smooth and immersive interaction system.
The DialogueController
class manages NPC interactions and dialogue using Unity and the Ink storytelling system, developed by Inkle. When the player presses "Q," nearby NPCs are detected via a spherical overlap, and the closest one is selected. If dialogue is initiated, the system enters dialogue mode, displaying the NPC's name and dialogue while pausing gameplay and stopping the game timer.
The EnterDialogueMode
() method initializes the Ink story from the NPC's script and updates the dialogue panel. Conversations progress through ContinueStory
(), fetching the next segment of dialogue, and conclude when no further text is available, triggering ExitDialogueMode
(). This restores gameplay elements and clears the panel, ensuring a smooth and immersive interaction system.
The DialogueController
class manages NPC interactions and dialogue using Unity and the Ink storytelling system, developed by Inkle. When the player presses "Q," nearby NPCs are detected via a spherical overlap, and the closest one is selected. If dialogue is initiated, the system enters dialogue mode, displaying the NPC's name and dialogue while pausing gameplay and stopping the game timer.
The EnterDialogueMode
() method initializes the Ink story from the NPC's script and updates the dialogue panel. Conversations progress through ContinueStory
(), fetching the next segment of dialogue, and conclude when no further text is available, triggering ExitDialogueMode
(). This restores gameplay elements and clears the panel, ensuring a smooth and immersive interaction system.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Ink.Runtime;
using TMPro;
using UnityEngine;
public class DialogueController : MonoBehaviour
{
public GameObject player;
[Header("Dialogue")]
public GameObject dialoguePanel;
public TMP_Text npcText, dialogueText;
Story currentStory;
public TextMeshProUGUI TimeText;
void Start()
{
dialoguePanel.SetActive(false);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Q))
{
GameObject[] NPCs = Physics.OverlapSphere(player.transform.position, 1f, LayerMask.GetMask("NPC")).Select(collider => collider.gameObject).ToArray();
if (NPCs.Length == 0) {
return;
}
float closestDistance = Mathf.Infinity;
GameObject closestNPC = null;
foreach (GameObject npc in NPCs) {
float currentDistance = Vector3.Distance(player.transform.position, npc.transform.position);
if (currentDistance < closestDistance) {
closestDistance = currentDistance;
closestNPC = npc;
}
}
if (!dialoguePanel.activeSelf)
{
EnterDialogueMode(closestNPC.name, closestNPC.GetComponent<NPCDialogue>().inkJson);
}
else if (dialoguePanel.activeSelf
&& currentStory.currentChoices.Count == 0)
{
ContinueStory();
}
}
}
void EnterDialogueMode(string npcName, TextAsset inkJson)
{
currentStory = new Story(inkJson.text);
dialoguePanel.SetActive(true);
player.GetComponent<PlayerController>().enabled = false;
npcText.text = npcName;
TimeText.GetComponent<TimeTextUpdate>().StopTimer();
ContinueStory();
}
void ExitDialogueMode()
{
dialoguePanel.SetActive(false);
player.GetComponent<PlayerController>().enabled = true;
dialogueText.text = "";
TimeText.GetComponent<TimeTextUpdate>().ResumeTimer();
}
void ContinueStory()
{
if (currentStory.canContinue)
{
dialogueText.text = currentStory.Continue();
}
else
{
ExitDialogueMode
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Ink.Runtime;
using TMPro;
using UnityEngine;
public class DialogueController : MonoBehaviour
{
public GameObject player;
[Header("Dialogue")]
public GameObject dialoguePanel;
public TMP_Text npcText, dialogueText;
Story currentStory;
public TextMeshProUGUI TimeText;
void Start()
{
dialoguePanel.SetActive(false);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Q))
{
GameObject[] NPCs = Physics.OverlapSphere(player.transform.position, 1f, LayerMask.GetMask("NPC")).Select(collider => collider.gameObject).ToArray();
if (NPCs.Length == 0) {
return;
}
float closestDistance = Mathf.Infinity;
GameObject closestNPC = null;
foreach (GameObject npc in NPCs) {
float currentDistance = Vector3.Distance(player.transform.position, npc.transform.position);
if (currentDistance < closestDistance) {
closestDistance = currentDistance;
closestNPC = npc;
}
}
if (!dialoguePanel.activeSelf)
{
EnterDialogueMode(closestNPC.name, closestNPC.GetComponent<NPCDialogue>().inkJson);
}
else if (dialoguePanel.activeSelf
&& currentStory.currentChoices.Count == 0)
{
ContinueStory();
}
}
}
void EnterDialogueMode(string npcName, TextAsset inkJson)
{
currentStory = new Story(inkJson.text);
dialoguePanel.SetActive(true);
player.GetComponent<PlayerController>().enabled = false;
npcText.text = npcName;
TimeText.GetComponent<TimeTextUpdate>().StopTimer();
ContinueStory();
}
void ExitDialogueMode()
{
dialoguePanel.SetActive(false);
player.GetComponent<PlayerController>().enabled = true;
dialogueText.text = "";
TimeText.GetComponent<TimeTextUpdate>().ResumeTimer();
}
void ContinueStory()
{
if (currentStory.canContinue)
{
dialogueText.text = currentStory.Continue();
}
else
{
ExitDialogueMode