Dev Log 38 - The Christmas Resurrection (Kind - Of )
Source: Dev.to
Hello again, internet. Yes, Iām alive. Yes, itās almost Christmas. No, the Halloween Frontend Challenge results still havenāt been released. I assume the judges were eaten by CSS goblins. Happens.
Anyway, Iām here to talk about why I vanished.
š 1. The Great Dev Log Hiatus
I fully intended to keep posting dev logs. I really did.
Then the challenge results never came out and my brain went:
āAh. Time to hibernate.ā
And so I did.
š§Ŗ 2. The Mini Game That Became a Monster
I started building a tiny miniāgame inside my main project. I thought it was a āminiā game anywayāsome fun collectable cards, maybe playāagain AI in my project, just a little side thing. A distraction. A palate cleanser.
Naturally, it mutated into its own ecosystem, a fullāblown second project complete with:
- its own UI
- its own logic
- its own lore
- its own problems
- its own identity crisis
Classic me.
š¦ 3. The Roblox Arc (a.k.a. My Exile)
Then I decided to ālearn Roblox scripting for fun.ā
Turns out Roblox has its own coding language, a version of Lua called Luau. I had never even heard of it, but the promise of free server hosting and taking care of all the fiddly stuff was enough for me to give it a try.
This was a mistake.
Within days (or weeks) I got:
- a warning
- a 1āday ban
- a 3āday ban
All because the AI moderation system decided that any amount of visible skin is a war crime. I wasnāt even making anything weirdāI was making a card game. A card game where the main subject is ducks. Apparently elbows are forbidden, as are any ducks with sixāpacks.
So I left Roblox like a disgraced wizard and returned to Unity while I await the banishment, like a peasant thrown from the village.
š§± 4. The Birth of This New Project
Out of the ashes of my Roblox exile came this new Unity sideāproject.
It started simple:
- Title screen
- Credits panel
- Settings panel
Then I blinked and suddenly I had:
A Global Audio Engine
using UnityEngine;
using System.Collections;
namespace Audio
{
public class AudioManager : MonoBehaviour
{
public static AudioManager Instance;
[Header("Music")]
public AudioClip titleTheme;
private AudioSource musicSource;
[Header("SFX Looping")]
private AudioSource sfxLoopSource;
private bool hasFadedOut = false;
private float fadeDuration = 8f;
void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
// Create audio sources
musicSource = gameObject.AddComponent();
musicSource.loop = true;
musicSource.playOnAwake = false;
sfxLoopSource = gameObject.AddComponent();
sfxLoopSource.loop = true;
sfxLoopSource.playOnAwake = false;
ApplyVolumesFromOptions();
PlayTitleTheme();
}
public void ApplyVolumesFromOptions()
{
if (AudioOptionsManager.Instance != null)
{
AudioListener.volume = AudioOptionsManager.Instance.MasterVolume;
musicSource.volume = AudioOptionsManager.Instance.MusicVolume;
sfxLoopSource.volume = AudioOptionsManager.Instance.SFXVolume;
Debug.Log("[AudioManager] Volumes synced from AudioOptionsManager.");
}
}
public void PlayTitleTheme()
{
if (titleTheme != null && !hasFadedOut)
{
musicSource.clip = titleTheme;
musicSource.loop = true;
musicSource.Play();
}
}
public void StopMusic()
{
musicSource.Stop();
}
public void FadeOutMusic()
{
if (!hasFadedOut)
{
StartCoroutine(FadeOutRoutine());
}
}
private IEnumerator FadeOutRoutine()
{
hasFadedOut = true;
float startVolume = musicSource.volume;
musicSource.loop = false;
for (float t = 0; t musicSource;
public AudioSource SFXLoopSource => sfxLoopSource;
}
}
Audio Options Manager (partial)
using UnityEngine;
using UnityEngine.UI;
using Audio;
public class AudioOptionsManager : MonoBehaviour
{
public static AudioOptionsManager Instance { get; private set; }
[Header("UI")]
[SerializeField] private Slider masterSlider;
[SerializeField] private Slider musicSlider;
// ... (rest of the script)
}
Screenshot
Thatās where things stand. Stay tuned for more updates, and maybe next year the Halloween results will finally appear! š
[SerializeField] private Slider sfxSlider;
[SerializeField] private Button resetButton;
[SerializeField] private Button closeButton;
[Header("Audio Sources")]
[SerializeField] private AudioSource musicSource;
[SerializeField] private AudioSource sfxLoopSource;
public const float DefaultMasterVolume = 0.75f;
public const float DefaultMusicVolume = 0.75f;
public const float DefaultSFXVolume = 0.75f;
private float masterVolume = DefaultMasterVolume;
private float musicVolume = DefaultMusicVolume;
private float sfxVolume = DefaultSFXVolume;
public float MasterVolume => masterVolume;
public float MusicVolume => musicVolume;
public float SFXVolume => sfxVolume;
private void Awake()
{
Instance = this;
}
private void Start()
{
// Autoāassign sources from AudioManager
if (AudioManager.Instance != null)
{
if (musicSource == null)
musicSource = AudioManager.Instance.MusicSource;
if (sfxLoopSource == null)
sfxLoopSource = AudioManager.Instance.SFXLoopSource;
}
// Wire UI
masterSlider.onValueChanged.AddListener(OnMasterVolumeChanged);
musicSlider.onValueChanged.AddListener(OnMusicVolumeChanged);
sfxSlider.onValueChanged.AddListener(OnSFXVolumeChanged);
resetButton.onClick.AddListener(OnResetAudio);
closeButton.onClick.AddListener(OnClosePressed);
// Initialize sliders
masterSlider.value = masterVolume;
musicSlider.value = musicVolume;
sfxSlider.value = sfxVolume;
ApplyVolumes();
}
private void OnMasterVolumeChanged(float value)
{
masterVolume = Mathf.Clamp01(value);
ApplyVolumes();
}
private void OnMusicVolumeChanged(float value)
{
musicVolume = Mathf.Clamp01(value);
ApplyVolumes();
}
private void OnSFXVolumeChanged(float value)
{
sfxVolume = Mathf.Clamp01(value);
ApplyVolumes();
}
private void OnResetAudio()
{
masterVolume = DefaultMasterVolume;
musicVolume = DefaultMusicVolume;
sfxVolume = DefaultSFXVolume;
masterSlider.value = masterVolume;
musicSlider.value = musicVolume;
sfxSlider.value = sfxVolume;
ApplyVolumes();
}
private void ApplyVolumes()
{
AudioListener.volume = masterVolume;
if (musicSource != null)
musicSource.volume = musicVolume;
if (sfxLoopSource != null)
sfxLoopSource.volume = sfxVolume;
}
private void OnClosePressed()
{
// MenuManager handles showing/hiding the panel
gameObject.SetActive(false);
}
UI Controls
- Enter fullscreen mode
- Exit fullscreen mode
A clean UI architecture
- A proper menu flow
- A credits panel that actually scrolls (growth!)
Progress is faster this time because I actually know Unity now.
I no longer open the editor, panic, and close it for two days.
Character development
š§© 5. The PlayFab Signup
At some point I also signed up for PlayFab (today).
Why? Future me will need:
- Cloud saves
- Player data
- Inventory syncing
- Maybe multiplayer
- Maybe trading
- Maybe a full backend economy
Or maybe I just like dashboards. Hard to say.
š 6. The 310āCard Database
Yes. I imported 310 cards into Unity, each with:
- Names
- Stats
- Rarities
- Factions
- Lore
- Effects
- Spreadsheetādriven data

This is the moment I realised:
āOh. This isnāt a miniāproject anymore. This is a real game now.ā
A few examples
| Card 1 | Card 2 |
|---|---|
![]() | ![]() |
![]() | ![]() |
ā¦and another 306 more (I was still busy, sidetracked a lot).
š§ 7. The Present Day
Right now the project has:
- A clean title screen
- A working settings panel
- A scrolling credits panel
- A global audio system
- A database of 310 cards
- A PlayFab backend ready to be used
- A developer who is no longer afraid of Unity
- A dev log that has risen from the ashes
From the dead
š 8. Whatās Next
In no particular order:
- UI transitions
- Oneāshot SFX
- Scene loading
- Card binder
- Card inspection
- Gameplay prototype
- More lore
- More chaos
- And maybe ā just maybe ā the Halloween challenge results.
Merry Christmas





