DEV Community

Cover image for Dev Log 38 - The Christmas Resurrection (Kind - Of )
John
John

Posted on

Dev Log 38 - The Christmas Resurrection (Kind - Of )

šŸŽ„ Dev Log: The Christmas Resurrection
(a.k.a. ā€œStill No Halloween Resultsā€)

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 am 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 2nd 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 it's own coding language , a version of Lua, specifically a customized version known as 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

Then a 1‑day ban

Then 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 6 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 project. ( 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<AudioSource>();
            musicSource.loop = true;
            musicSource.playOnAwake = false;

            sfxLoopSource = gameObject.AddComponent<AudioSource>();
            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 < fadeDuration; t += Time.deltaTime)
            {
                musicSource.volume = Mathf.Lerp(startVolume, 0f, t / fadeDuration);
                yield return null;
            }

            musicSource.volume = 0f;
            musicSource.Stop();
        }

        public void PlayLoop(AudioClip clip, float volume = 1f)
        {
            if (clip == null)
            {
                Debug.LogWarning("[AudioManager] Tried to play null AudioClip.");
                return;
            }

            sfxLoopSource.clip = clip;
            sfxLoopSource.volume = Mathf.Clamp01(volume);
            sfxLoopSource.loop = true;
            sfxLoopSource.Play();
        }

        // Expose sources for external control
        public AudioSource MusicSource => musicSource;
        public AudioSource SFXLoopSource => sfxLoopSource;
    }
}

Enter fullscreen mode Exit fullscreen mode
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;
    [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);
    }
}

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? Because 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.

All 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 ..

and another 306 more !! ( i was still busy , sidetracked very much )

🧠 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

and a dev log that has risen 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

Top comments (0)