The Mission of this devlog: To provide players with the power to hack various objects in the game world, unleashing a multitude of actions, from disabling cameras to causing explosions. In short: Causing Mayhem. So, let’s get right into it.
Drawing Inspiration from Watch Dogs
Watch Dogs, a game that allows players to hack into an interconnected city, served as my primary muse. I was captivated by the idea of giving players the ability to manipulate their surroundings and devised a plan to bring this level of interactivity into my own project. Also I am very inspired by its clean and functional UI.
Conceptualizing the System
My system revolves around a few core components:
1. HackableSO (Scriptable Object): At the heart of my system lies the HackableSO, a Scriptable Object representing objects in the game world that can be hacked. Each HackableSO carries critical information such as descriptions, sprites, battery costs (if applicable), and most importantly, a list of HackableActionSOs.
2. HackableActionSO (Scriptable Object): These Scriptable Objects hold the logic for actions that can be executed on a HackableSO. Actions like “Disable Camera” or “Trigger Explosion” are defined as HackableActionSOs. They encapsulate the execution logic for their respective actions.
3. Drag-and-Drop Interface: My system makes it possible to include as many interactions to one hackable as I want. Just to select a HackableSO and attach HackableActionSOs to it. Done.
4. Runtime Execution: When a player decides to hack a HackableSO, all the associated HackableActionSOs spring into action, executing their designated functions.
Code Examples
Here’s a sneak peek into the code that brings my dynamic hacking system to life. I’ll focus on the two key Parts: HackableSO
and HackableActionSO
. These just get called via a hacking ability like that: hackableSO.Execute();
// HackableSO.cs
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Hackable", menuName = "Scriptable Objects/Hackable", order = 0)]
public class HackableSO : ScriptableObject
{
// ... (other variables)
public List<HackableActionSO> hackableActions;
List<HackableActionSO> instances = new List<HackableActionSO>();
public void Initialize(Transform position, GameObject gameObject, ObjectHackable objectHackable)
{
// Initialization logic for HackableSO
// Instantiate HackableActionSOs and add them to the 'instances' list
}
public void Execute(Transform position, GameObject gameObject, ObjectHackable objectHackable)
{
// Execution logic for HackableSO
// Loop through 'instances' and execute associated HackableActionSOs
}
public void Deinitialize(Transform position, GameObject gameObject, ObjectHackable objectHackable)
{
// Deinitialization logic for HackableSO
}
}
In this code snippet, HackableSO
is responsible for initializing and executing HackableActions. It maintains a list of instances of HackableActionSOs
to ensure that each action can be executed independently.
// HackableActionSO.cs
using UnityEngine;
public class HackableActionSO : ScriptableObject
{
public virtual void Initialize(Transform senderTransform, GameObject senderGO, ObjectHackable senderObjectHackable)
{
// Initialization logic for the action
}
public virtual void Execute(Transform senderTransform, GameObject senderGO, ObjectHackable senderObjectHackable)
{
// Execution logic for the action
}
public virtual void Deinitialize(Transform senderTransform, GameObject senderGO, ObjectHackable senderObjectHackable)
{
// Deinitialization logic for the action
}
}
Meanwhile, HackableActionSO
serves as the base class for all hackable actions, providing methods for initialization, execution, and deinitialization, making it easy for me to create custom actions.
Here is an example for that Hackable Action that just instantiates Objects like particle effects and sound.
public class InstantiatePrefabAndSound : HackableActionSO
{
public bool stopAfterSeconds;
public float secondsToStop;
public List<GameObject> ObjectsToInstantiate;
public List<AudioClip> audioClips;
public override void Initialize(Transform sendertransform, GameObject senderGo, ObjectHackable senderobjectHackable)
{
// Additional initialization logic can be added here
}
public override void Execute(Transform sendertransform, GameObject senderGo, ObjectHackable senderobjectHackable)
{
foreach (var gameObject in ObjectsToInstantiate)
{
var go = Instantiate(gameObject, sendertransform.position, Quaternion.identity);
go.transform.parent = sendertransform;
go.SetActive(true);
if (stopAfterSeconds) Destroy(go, secondsToStop);
}
foreach (var audioClip in audioClips)
{
// Play sound once
AudioSource audio = senderGo.AddComponent<AudioSource>();
audio.clip = audioClip;
audio.spatialBlend = 1;
audio.Play();
if (stopAfterSeconds) Destroy(audio, secondsToStop);
}
}
public override void Deinitialize(Transform sendertransform, GameObject senderGo, ObjectHackable senderobjectHackable)
{
// Additional deinitialization logic can be added here
}
}