Horse Bungee

Info coming soon!

Advertisements

Big Game Project – Last Blog Post

The project is now over. There are plans to keep working on the game, but nothing is decided. I have learned a lot from making this game and I am proud of what we managed to accomplish in eight weeks. I worked a lot with audio which is something the education hasn’t covered at all and I think it turned out great considering the limited time I put into it. I also learned a lot more about Unity which seems like useful knowledge considering how widespread it is.

Big Game Project – Gotland Game Conference

The game was in pretty good shape to show at the Gotland Game Conference. We had a lot of of bug fixes and balancing to do in the last days, but we planned it all well enough that we never had to do any major crunching. During the conference, it turned out that we had one big bug left. It had to do with the tutorial and made it impossible to create more buildings if the player did one very specific action when creating the first building. It occurred quite a few times during the conference, but still not frequently enough that we felt we needed to fix it. Instead we kept an eye out and if it happened we explained with an apology that the player needed to restart and replay the first few minutes of the game.

For the most part, the game worked very well and people seemed to enjoy it. Most players stayed for a long play session, and while that was the most positive feedback we could get, it also meant that some never got the chance to try the game during the conference.

Overall, I am very happy with the game we displayed.

Big Game Project – Last Week

We’re on the last week of production but I think the game is in an okay state to showcase. I am implementing more sound effects and fixing bugs. Today I’ve fixed the building mode in which the player could place a new building anywhere, not considering other colliders. The actual bug was not that colliders weren’t checked, that was more a quick fix left from earlier to make sure that buildings could be built despite other problems with the collision checking.

I use the Unity function “Physics.CheckBox” to check if there are other colliders overlapping the collider of the building that’s being placed. The function takes the box as a Vector3 which I create using the variable “size” in the BoxCollider and dividing it by two since Physics.CheckBox takes in half the size of the box to check. I also pass in the rotation of the building to make sure the object rotates properly. The relevant part of the script can be seen below.

 

checkBuildPos = position + Vector3.up * 2;
Vector3 colliderSize = m_currentBuilding.constructionSiteCollider.size / 2;
bool hit = Physics.CheckBox(checkBuildPos,
     colliderSize,
     m_currentBuilding.transform.rotation,
     m_buildingLayerMask);
return !hit;

Big Game Project – Static Sound Player

I have continued making and implementing sound effects. Implementing them is tedious because every sound effect needs coding, and version control is annoying since changes to Unity prefabs can’t be merged and I have to change something in at least one C# file and one Unity prefab for every single sound I implement.

Anyway, all the structures for the audio are starting to come together and implementing a new sound doesn’t always create problems anymore. Today I’ve worked on another helpful class for managing audio. It contains sounds that don’t really belong anywhere and they can be accessed through static functions. Each sound has an enum value for easy access without having to compare strings every time. I have similar systems for entities (playable characters and enemies) in the game except the sounds can’t be accessed from static methods, since it makes sense that they belong to a specific instance. Because several entities can play the same sound simultaneously and the sounds should always play at the entity’s position. This script can be seen in this post.

 

So far the whole thing only contains two sounds, but I expect several more to be added later. Below is the full script:


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Audio;

public enum MiscSounds
{
    UI_CLICK,
    CONSTRUCTION_FINISHED,
}

[System.Serializable]
public class MiscAudioSource
{
    public MiscSounds soundIdentifier;
    public List<AudioClip> audioClips;
    public AudioMixerGroup audioMixerGroup;
    [Range(0, 1)]
    public float spatialBlend = 1f;
    private AudioSource audioSource;
    private int currentIndex = 0;

    public void Initialize(AudioSource _audioSource)
    {
        audioSource = _audioSource;
        audioSource.playOnAwake = false;
        audioSource.clip = audioClips[0];
        audioSource.spatialBlend = spatialBlend;
        audioSource.outputAudioMixerGroup = audioMixerGroup;
        audioSource.volume = 1f;
    }

    public AudioSource RandomSound()
    {
        int random = Random.Range(0, (audioClips.Count - 1));
        currentIndex = random;
        audioSource.clip = audioClips[currentIndex];
        return audioSource;
    }

    public AudioSource NextSound()
    {
        audioSource.clip = audioClips[currentIndex];
        currentIndex++;
        if (currentIndex >= audioClips.Count)
            currentIndex = 0;
        return audioSource;
    }

    public AudioSource CurrentSound()
    {
        return audioSource;
    }
}

public class StaticAudioPlayer : MonoBehaviour
{
    static StaticAudioPlayer instance;
    public List<MiscAudioSource> audioSources;

    void Awake()
    {
        if (instance == null)
            instance = this;

        for (int i = 0; i < audioSources.Count; i++)
        {
            AudioSource audioSource = gameObject.AddComponent<AudioSource>();
            audioSources[i].Initialize(audioSource);
            audioSource.transform.SetParent(transform);
        }
    }

    public AudioSource GetAudioSource(MiscSounds identifier)
    {
        for (int i = 0; i < audioSources.Count; i++)
        {
            MiscAudioSource source = audioSources[i];
            if (source.soundIdentifier == identifier)
                return source.RandomSound();
        }

        return null;
    }

    public static void PlaySound(MiscSounds identifier, Vector3 pos, float minPitch = 1f, float maxPitch = 1f)
    {
        AudioSource audioSource = instance.GetAudioSource(identifier);
        AudioManager.PlaySourceAtPoint(audioSource, pos, minPitch, maxPitch);
    }

    public static void PlaySound(MiscSounds identifier, float minPitch = 1f, float maxPitch = 1f)
    {
        AudioSource audioSource = instance.GetAudioSource(identifier);
        AudioManager.PlayAudioSource(audioSource, minPitch, maxPitch);
    }

    public static void PlayClickSound()
    {
        AudioSource audioSource = instance.GetAudioSource(MiscSounds.UI_CLICK);
        AudioManager.PlayAudioSource(audioSource);
    }
}

Big Game Project – Managing ambient sounds

Our game world consist of two areas different in mood. One is an island floating above the clouds and the other a dangerous jungle on the planet below. The island has a windy ambience and the jungle has recordings of a real jungle. Day and night is also important for gameplay, so I wanted different ambiences for those. It was difficult to find two wind recordings that were distinctively different enough, so I chose to always have the same for the island during day and night, but two different recordings for the island.

I wanted a system from which it is easy to change the ambience, and changing the ambience would mean fading out the current ambience while fading in the new ambience. The duration of the fades can be passed in as a parameter because I wanted a fast transition when the player manually switches between the island and jungle levels with a keypress, and a slower transition when the ambience changes automatically with the time of day.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Audio;

public enum AmbientSounds
{
    ISLAND,
    JUNGLE_DAY,
    JUNGLE_NIGHT,
    NONE,
}

public class Ambience
{
    public AudioSource audioSource;
    private float volume = 0f;
    public AmbientSounds identifier;
    private float currentFadeTime = 0f;
    private float fadeStartVolume;
    private float fadeTime = 0.2f;
    private bool fading = false;

    public void Initialize(AmbientAudio ambientAudio, AudioSource _audioSource, AmbientSounds _identifier)
    {
        identifier = _identifier;
        audioSource = _audioSource;
        audioSource.volume = 0f;
        volume = 0f;
        SetActive(false, 0f);
        audioSource.loop = true;
    }

    public void UpdateLayer(float deltaTime)
    {
        if (!fading)
            return;

        if (fadeTime == 0)
        {
            fading = false;
            audioSource.volume = volume;
            return;
        }

        currentFadeTime += deltaTime;
        if (currentFadeTime > fadeTime)
        {
            fading = false;
            currentFadeTime = fadeTime;
        }
        audioSource.volume = Mathf.Lerp(fadeStartVolume, volume, currentFadeTime / fadeTime);
    }

    public void SetActive(bool active, float fadeDuration = 0)
    {
        fadeTime = fadeDuration;
        fading = true;
        currentFadeTime = 0;
        fadeStartVolume = audioSource.volume;
        volume = active ? 1f : 0f;
    }
}

public class AmbientAudio : MonoBehaviour
{
    static AmbientAudio instance;
    public AudioSource jungle_daySource;
    public AudioSource jungle_nightSource;
    public AudioSource islandSource;
    public List<Ambience> ambiences;
    public AmbientSounds current;

    void Awake()
    {
        if (instance == null)
            instance = this;

        ambiences = new List<Ambience>();

        Ambience ambience1 = new Ambience();
        ambience1.Initialize(this, jungle_daySource, AmbientSounds.JUNGLE_DAY);
        ambiences.Add(ambience1);

        Ambience ambience2 = new Ambience();
        ambience2.Initialize(this, jungle_nightSource, AmbientSounds.JUNGLE_NIGHT);
        ambiences.Add(ambience2);

        Ambience ambience3 = new Ambience();
        ambience3.Initialize(this, islandSource, AmbientSounds.ISLAND);
        ambiences.Add(ambience3);
    }

    void Start()
    {
        for (int i = 0; i < ambiences.Count; i++)
        {
            ambiences[i].audioSource.Play();
            ChangeAmbience(AmbientSounds.NONE, 0f);
        }
        ChangeAmbience(AmbientSounds.ISLAND, 0);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha1))
            ChangeAmbience(AmbientSounds.ISLAND, 2f);
        if (Input.GetKeyDown(KeyCode.Alpha2))
            ChangeAmbience(AmbientSounds.JUNGLE_DAY, 2f);
        if (Input.GetKeyDown(KeyCode.Alpha3))
            ChangeAmbience(AmbientSounds.JUNGLE_NIGHT, 2f);

        for (int i = 0; i < ambiences.Count; i++)
        {
            ambiences[i].UpdateLayer(Time.deltaTime);
        }
    }

    public static void ChangeAmbience(AmbientSounds newSound, float fadeDuration)
    {
        instance.current = newSound;
        for (int i = 0; i < instance.ambiences.Count; i++)
        {
            Ambience ambience = instance.ambiences[i];
            if (ambience.identifier == newSound)
                ambience.SetActive(true, fadeDuration);
            else
                ambience.SetActive(false, fadeDuration);
        }
    }

    public static AmbientSounds GetCurrent()
    {
        return instance.current;
    }
}

Big Game Project – Sound Effect container script

I have been implementing some sound effects. Our playable characters, the settlers, contain a bunch of different sound effects and I had to invent a system for handling them. The things I had in mind:

  • Allow to later add more sound effects fairly easily in the editor
  • Adding several AudioClips for each sound
  • A simple way to get a random variation of a sound without dealing with the randomization where the sound is played from
  • The AudioSource components should be children of the settler object that plays them, to make use of Unity’s built-in system for 3D space audio
  • Not spending too much time on any of this shit, since we have a tight schedule

I made a wrapper class for the AudioSource to include several AudioClips and the ability to set a random one through a function. The main script has a list of instances of this class and this list can be changed in the editor. The whole system is meant to be expanded; as can be seen in the Enum, there is currently only four different sounds. And the NextSound function is not in use yet. I prepared it because I figured you may sometimes want to go through the variations of a sound in order rather than using a random one every time. The code can be seen below:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public enum SettlerSounds
{
    SHOT,
    PICKUP_STONE,
    PICKUP_WOOD,
    MELEE_HIT,
}

[System.Serializable]
public class SettlerAudioSource
{
    public SettlerSounds soundIdentifier;
    private AudioSource audioSource;
    public List<AudioClip> audioClips;
    private int currentIndex = 0;

    public void Initialize(AudioSource _audioSource)
    {
        audioSource = _audioSource;
        audioSource.playOnAwake = false;
        audioSource.clip = audioClips[0];
        audioSource.spatialBlend = 0.8f;
    }

    public AudioSource RandomSound()
    {
        int random = Random.Range(0, (audioClips.Count - 1));
        currentIndex = random;
        audioSource.clip = audioClips[currentIndex];
        return audioSource;
    }

    public AudioSource NextSound()
    {
        audioSource.clip = audioClips[currentIndex];
        currentIndex++;
        if (currentIndex >= audioClips.Count)
            currentIndex = 0;
        return audioSource;
    }

    public AudioSource CurrentSound()
    {
        return audioSource;
    }
}

public class SettlerAudioContainer : MonoBehaviour
{
    public List<SettlerAudioSource> audioSources;

    void Awake()
    {
        for (int i = 0; i < audioSources.Count; i++)
        {
            AudioSource audioSource = gameObject.AddComponent<AudioSource>();
            audioSources[i].Initialize(audioSource);
            audioSource.transform.SetParent(transform);
        }
    }

    public AudioSource GetAudioSource(SettlerSounds identifier)
    {
        for (int i = 0; i < audioSources.Count; i++)
        {
            SettlerAudioSource source = audioSources[i];
            if (source.soundIdentifier == identifier)
                return source.RandomSound();
        }

        return null;
    }
}