Fairy Hollow

Game Download

About the Game

You play a female protagonist with either controller or keyboard who has been captured by fairies and told to grow the the Mana Tree to escape. You need to complete puzzles and solve the maze to open the exit portal.

About the Jam

This game was created during the Remote Global Game Jam 2021 using Unity and the theme was “Lost and Found”. I put together a team of 4 3D artists(Mohamed "Nour" Hajar, Dale Skribins, Chris Murillo, Ghan Satjipanon) and a programmer(Peppermint Games) and I took on the roles of Project Manager, Programmer, Lead Art director and Game Designer. I organised both the game design and making sure that the source control was working for the artists and that they were happy doing their jobs while I was putting the game together. We used an artbible to get a clear idea on art direction on how the game should feel. We also used Trello to make a list of all assets needed and who had which job. On the last day of the Game Jam we also managed to find a sound designer(Ben "Badams" Adams) who made the Soundtrack in a few hours I’d hoped to get sounds into the game and easily implement his songs at the last minute.

There was a break down in communication mid project with the other programmer. They coded the puzzle solution code and implemented a save function which didn’t end up in the final game. Because of this communication break down the puzzle solution code was not working as intended in the game jam version they had created it with a sequence needed in a puzzle area so each puzzle would unlock the next where I had asked for and designed the levels with each section being able to do the puzzles in any order. We found a quick fix which worked at the time but we were also missing meshes for puzzle keys because of scene corruption as we were using git so the game was basically unsolvable unless you knew where the keys were. Game Jam Page

About the Project

I reused my code from several previous projects to make the Player Movement, Pickup function, Item detection, Sound Manager and used Unity’s New Input System. I made the rainbow Door shader using Unity Shader Graph from scratch in a previous project it changes colour depending how far away the camera is.

I tried implementing local Co-op but realised that I hadn’t designed the levels to give other players something to do and that when only one puzzle type was functional that multiplayer was not going to work in this game. I also tried making a shader for water colour textures I tried following a devlog which used shader code and did not work in URP. I ran out of time to tinker with it.

After the Jam

After the game jam I spent some time on the game fixing the puzzle code and adding the models that the artists made that I hadn’t had time to add to the final build. I also implemented:

  • Maze Normals were fixed(Nour fixed them the day after the jam)

  • Itch.io title image(made by Nour)

  • camera controls using CinemaMachine

  • control mapping UI menu

  • blending animations using layers and animation masks(pickup with all the other animations)

  • run animation and input, side step movement and animation

  • firefly/fairy particles

  • terrain and plants assets and fine tuned their Material settings - fixed all the file import errors(most of the textures had been imported as Cubes)

  • a mini tree in the First area which changes the same as the Big tree in the middle of the maze to make it easier to see

  • text alerts to doors opening and key information and particles at each doorway to make them easier to find

  • emissive Textures added to mushrooms and some flowers

  • sounds added to Keys and Lock to make them easier to find (Ben made them after the game jam)

  • player blink animation

  • when each area unlocks it loads the puzzles so that the player doesn’t accidentally pick up keys through walls

What I would like to implement

  • Button puzzle

  • Controller support for UI

  • Cut scene for the end and beginning of the level

  • cut scene or camera target look at when a door opens

  • local co-op

  • Water colour shader

  • wall collider improvements

  • Animation Rigger player animation(look towards item that can be picked up or point of interest(Open door, lock location))

  • push/pull animation

Thankyou’s

I’d like to thank Dale for his patience and adaptability throughout the project and completing some great environment assets I loved the Lamp post, Chris for completing 3 trees in time for the deadline, Ghan for the sheer amount of work he ended up completing and for understanding my vision for the main character so well, Nour for chugging away and completing the maze with just enough time to spare and helping me delegate jobs when the last few hours loomed, and Ben for miraculously appearing and completing an amazing soundtrack in just a few hours. I’d also like to thank Peppermint games for joining us I understand that they may have had technical difficulties and were taking on several other projects at the same time and hope that they are better with their next project.

Code Example

using UnityEngine; using System.Collections; using UnityEngine.UI; using UnityEngine.SceneManagement; /* Created by Emily Cochrane * Date: Sep 2017 */ public class SoundManager : MonoBehaviour { public AudioSource sfxSource; //Drag a reference to the audio source which will play the sound effects. public AudioSource ambientSource; //Drag a reference to the audio source which will play the music. public static SoundManager instance = null; //Allows other scripts to call functions from SoundManager. public float lowPitchRange = .95f; //The lowest a sound effect will be randomly pitched. public float highPitchRange = 1.05f; //The highest a sound effect will be randomly pitched. public float randomPitch = 1.0f; //current pitch public bool soundToggle = true; //For Mute button public bool musicToggle = true; //For mute button public AudioClip[] soundTracks; public int nextTrack = 0; void Awake() { //Check if there is already an instance of SoundManager if (instance == null) //if not, set it to this. instance = this; //If instance already exists: else if (instance != this) //Destroy this, this enforces our singleton pattern so there can only be one instance of SoundManager. Destroy(gameObject); //set music to loop if (ambientSource != null) { ambientSource.loop = true; ambientSource.volume = 0.5f; } if (soundTracks != null) { PlaySingleLoopAmbient(SetRandAudioClip(soundTracks)); } //Set SoundManager to DontDestroyOnLoad so that it won't be destroyed when reloading our scene. DontDestroyOnLoad(gameObject); } public void PlayRandomTrack() { PlaySingleLoopAmbient(SetRandAudioClip(soundTracks)); } public void NextTrackNum(int num) { nextTrack = num; } public void SetSFXAudioSource(AudioSource source) { sfxSource = source; } public void SetSoundTrackAudioSource(AudioSource source) { ambientSource = source; } //Used to play single sound clips for Soundtrack or Background sound. public void PlaySingleAmbient(AudioClip clip) { //reset pitch after sound has played ambientSource.pitch = 1; //Set the clip of our efxSource audio source to the clip passed in as a parameter. ambientSource.clip = clip; ambientSource.loop = false; //Play the clip. ambientSource.Play(); //for playing multiple sounds at once //efxSource.PlayOneShot(clip); } //Used to play single sound clips on Loop for Soundtrack or Background sound. public void PlaySingleLoopAmbient(AudioClip clip) { //reset pitch after sound has played ambientSource.pitch = 1; //Set the clip of our efxSource audio source to the clip passed in as a parameter. ambientSource.clip = clip; ambientSource.loop = true; //Play the clip. ambientSource.Play(); //for playing multiple sounds at once //efxSource.PlayOneShot(clip); } //Used to play single sound clips. public void PlaySingleSFX(AudioClip clip) { //reset pitch after sound has played sfxSource.pitch = 1; //Set the clip of our efxSource audio source to the clip passed in as a parameter. sfxSource.clip = clip; //Play the clip. sfxSource.Play(); //for playing multiple sounds at once //efxSource.PlayOneShot(clip); } //RandomizeSfx chooses randomly between various audio clips and slightly changes their pitch. public void RandomizeSFX(params AudioClip[] clips) { //reset pitch sfxSource.pitch = 1; //Generate a random number between 0 and the length of our array of clips passed in. int randomIndex = Random.Range(0, clips.Length); //Choose a random pitch to play back our clip at between our high and low pitch ranges. //ChoosePitch(); //Set the pitch of the audio source to the randomly chosen pitch. //efxSource.pitch = randomPitch; //Set the clip to the clip at our randomly chosen index. sfxSource.clip = clips[randomIndex]; //Play the clip. sfxSource.Play(); //efxSource.PlayOneShot(clips[randomIndex]); } // for the SFX mute button to toggle between mute and unmute // randomly chooses an audio clip and returns that clip public AudioClip SetRandAudioClip(AudioClip[] sound){ int randomIndex = Random.Range(0, sound.Length); AudioClip clip = sound[randomIndex]; //Choose a random pitch to play back our clip at between our high and low pitch ranges. //ChoosePitch(); //Set the pitch of the audio source to the randomly chosen pitch. //sfxSource.pitch = randomPitch; return clip; } //plays a single SFX clip and sets a random pitch public void PlaySingleSFXRndPitch(AudioClip Aclip){ //Set the pitch of the audio source to the randomly chosen pitch. ChoosePitch(); sfxSource.pitch = randomPitch; //Set the clip to the clip at our randomly chosen index. sfxSource.clip = Aclip; //Play the clip. sfxSource.Play(); //set the Aclip to play with other sounds at the same time //sfxSource.PlayOneShot(Aclip); //reset pitch after sound has played //sfxSource.pitch = 1; } //set the pitch value - this is used in PauseGame script to set a single value pitch once(if this was called in the PlaySignleRndPitch() funtion it would change the pitch every time) public void ChoosePitch(){ //Choose a random pitch to play back our clip at between our high and low pitch ranges. randomPitch = Random.Range(lowPitchRange, highPitchRange); } // for the SFX mute button to toggle between mute and unmute public void ToggleMuteSFX() { soundToggle = !soundToggle; if (soundToggle) { sfxSource.volume = 1.0f; //PlaySingleSFX(mute); //soundBtn.GetComponent<Image>().sprite = soundOnImg; } else { //PlaySingleSFX(mute); sfxSource.volume = 0.0f; //soundBtn.GetComponent<Image>().sprite = soundOffImg; } } // for the Music mute button to toggle between mute and unmute public void ToggleMuteMusic() { musicToggle = !musicToggle; if (musicToggle) { ambientSource.volume = 0.5f; //PlaySingleSFX(mute); //musicBtn.GetComponent<Image>().sprite = musicOnImg; } else { //PlaySingleSFX(mute); ambientSource.volume = 0.0f; //musicBtn.GetComponent<Image>().sprite = musicOffImg; } } }

Level Design

I designed these levels where they slowly progressed in complexity and I tried to make the levels unique and stand out from each other to help the maze feel less repetitive. Button keys were not added the game jam version of the game and the jump puzzle in Area 5 was not added as my drawing were not in the same scale as the others and the artist ran out of room to add them next time I do this I will make the design in the same scale.

UI Design Progression

I wanted to design a UI button because I wanted the main menu to feel more customized. I designed this with Nour who suggested a seed spouting on its side. Ghan converted my pencil drawings into 2D sprites.

2D Art

After the game jam I created some emissive textures based on the textures the the artists submitted for the mushrooms and flowers and I made the fairy sprite sheet for the particle I’d added right after the game jam had finished. I created the Shader for the Doors between areas it uses the camera position in the world to make the colours change. I also made the shader on interactable objects, if you have more then one object nearby it highlights the closest one to avoid confusion.