Specter Inspector
Project Specs:
Academic Project | Unity 2023.2.6 | Custom C++ Engine | 8-Person Interdisciplinary Team
Overview
Specter Inspector is a game that uses a unique window mechanic, allowing players to interact with the game through desktop windows. Developed during my sophomore year game course, our team consisted of three designers and five programmers. As the technical designer, I bridged the gap between design and programming by developing gameplay mechanics and tools. Using Unity, I prototyped core features to ensure functionality before they were integrated into our custom engine.
My Design/Prototyping process
- Starting out on this project, we brainstormed and started to plan what we wanted in the game! This helped give me a basis of where I should start prototyping.


Spacer
- Using Unity’s sprite mask feature, I prototyped a simple way to simulate a desktop with interactive windows.
- A core feature of our game was ghosts! Therefore, I designed three gameplay tools to help the player navigate around them.
- EMF Detector: Detects the proximity of a ghost using three indicator lights: green (far), yellow (near), and red (very close).
- In code, the tool finds the nearest ghost and uses gizmos to visualize detection ranges around the player. Below is an example of how I implemented these gizmos! ⬇️
private void OnDrawGizmosSelected()
{
//Radius for EMF
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, YellowEMF);
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, RedEMF);
}
- EMP: Stuns a ghost for a few seconds.
- The EMP activates if a ghost is within the red gizmo and a battery is loaded. When triggered, it stuns the ghost, setting it to a stun state in the Ghost Manager for the duration of the EMP timer.
- Batteries: Manually powers the EMP and enable its use.
- I designed the battery system to feel interactive, requiring players to drag and load batteries into the EMP manually. This system included multiple components: a Battery Manager to track inventory, a drag-and-drop mechanic, and a check to ensure batteries snap back to the holder if not placed inside the EMP.
// Code from Battery Manager script:
batteryCount = transform.childCount;
Debug.Log(batteryCount);
if (batteriesAdded == false)
{
for (int i = 0; i < batteryCount; i++)
{
batteryChildren.Add(transform.GetChild(i).gameObject);
}
batteriesAdded = true;
}
for (int i = batteryCount; i > maxBatteries; i--)
{
batteryChildren.Remove(transform.GetChild(i - 1).gameObject);
GameObject.Destroy(transform.GetChild(i - 1).gameObject);
}
Debug.Log(batteryChildren.Count.ToString());
// Code from the Battery Dragging script:
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("On Begin Drag");
canvasGroup.alpha = .8f;
canvasGroup.blocksRaycasts = false;
parentAfterDrag = transform.parent;
transform.SetParent(GetComponentInParent<Canvas>().transform);
transform.SetAsLastSibling();
}
public void OnDrag(PointerEventData eventData)
{
Debug.Log("On Drag");
transform.position = Input.mousePosition;
}
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("On End Drag");
canvasGroup.alpha = 1f;
canvasGroup.blocksRaycasts = true;
transform.SetParent(parentAfterDrag);
}
- We also needed a health system! Though we wanted this health system to feel metadiegetic in the way that it mimics the players actual desktop. With that in mind I designed the health system to work as cracks that appear on the players screen and progressively get worse the more the player takes damage by a ghost.
- A core functionality that every single one of these windows required was the ability to be dragged around to to help mimic the feel of a window on your desktop!
- I created this script with reusability in mind. Therefore I only had to make one script which was able to be added to every window and function as desired.


// Code from Draggable Window script
private void OnMouseDown()
{
Difference = (Vector3)Camera.main.ScreenToWorldPoint(Input.mousePosition) - (Vector3)transform.position;
}
private void OnMouseDrag()
{
GetComponent<Rigidbody2D>().MovePosition((Vector3)Camera.main.ScreenToWorldPoint(Input.mousePosition) - Difference);
//transform.position = new Vector3(transform.position.x, transform.position.y, -1f);
}
Spacer
- Iteration was a major part of my design process!
- Our systems for prototyping were always changing to make serialization easier for the custom engine.
- This means I heavily communicated with the programmers on the team to understand how systems should be changed to fit the requirements of the custom engine!
- An example of the iteration process was our windows soon had to change to function as cameras in Unity! I assisted in changing over all of our sprite masks to be cameras which helped make the programmer’s job of serialization a lot easier.


Reflection
Challenges
- This was my first big game project using C#!
- Previous knowledge I had was only using visual scripting in Unity to make a game.
- This was a unique challenge because I had to take my previous C# knowledge and apply it to our game concept to prototype it where at first I didn’t know how to tackle some of the coding tasks I received.
How I overcame those challenges and what I learned from them
- When I would get stuck programming and wanted feedback I would attend my Professor’s office hours to get some assistance with my code!
- This was always so helpful because it really helped me understand the code from another perspective and get feedback and tips to further improve what I was coding.
- Another resource that I learned about and utilized when I got stuck was YouTube and the Unity Scripting API.
- These resources were extremely useful when there was just one specific thing that I would get stuck on. For example, when I was trying to figure out how to drag objects in Unity. The Unity Scripting API told me about so many useful event functions to use and YouTube would show me visually how things can look.
- For the cases that I just wanted to have more eyes on my code I would talk to the programmers on my team! This also helped me learn to communicate with them better along with getting a better understanding of what’s going on in the design and programming sides of the team.
- Overall, I learned if you get stuck you should utilize the resources you have access to because talking to others and getting a better understanding helped me become a better programmer and get to where I am today. 🙂