Thursday, July 7, 2016

A Tale of 54 Game Jams: Presentation & Panel

Presentation Night
Recently the team and I visited our project’s “alma matar”, the DC Chapter of the International Game Developers Association (IGDA), and gave a presentation on what it was like to develop Tumbleweed Express over the course of 54 Game Jams. The presentation, available on SlideShare, was high-level and designed to provide the audience with an overview of the many events that occurred during the project’s 4.5 year history; I dare to say that the presentation went fairly well.

Matthew Mauriello presents an overview of
the team's development process and history
After the presentation, several members of the team were on hand for an open panel discussion and the audience did not disappoint. Questions ranged from how we handled team members various creative ideas, how we worked together with so many team members, and what each of us thought was the most challenging part of the process. We also discussed how we hired other developers (e.g., artists, programmers) and how we unfortunately did not really consider recruiting people with other valuable skills (e.g., marketing), which was a huge mistake. We promoted the idea that the game jam model was very good for development, communication, and balancing our commitments; however, we also had to mention that at various points maturing the project required members of the team to put in more time than one weekend a month which included committing to daily updates when external deadlines were looming. Probably the most important thing we discussed was the importance of having these external deadlines and how production suffered when we failed to schedule these milestones into our development timeline.

Team members (L-R) Jacob Clayman, Matthew Mauriello, David To, and David Weiss
participate in a panel discussion about the challenges faced and successes achieved during the project
Avoiding Derailment and Splitting the Loot
The panel continued through “Last Call” and even outside the venue after the event was officially over. Walking back to my car, another indie developer asked me an important question: now that we have released our game on Steam, how does our team handle the money? Obviously, this is a tough question and the developer explained to me that he had been unable to find a satisfying answer. After a brief discussion this developer encouraged me to write a blogpost on the subject, but unfortunately, there isn’t all that much tell from our team’s perspective.

We believed that it was important to establish with the team some kind of agreement that detailed the expected commitments from each member and the expected rewards for that commitment. Our agreement included how we handled disputes, what happened when a team member did not meet their commitments, and provided team members with points where they could opt-out of the project and still receive credit for their work. This might seem unnecessary in the beginning of a project and indeed it was a full year before we established a formal agreement, but at many points in our project having this agreement was (and remains) valuable with respect to the continued health of the team.

Every year team members were offered a one year contract. If they completed the contract then they earned a single immutable share in the game’s future profits and expenses (assuming there would be any). Every active member (i.e., those with an active contract) received a vote in any decisions being made by the team during the course of that year. If there was an important issue that couldn’t be resolved by discussion then a vote could be called by any active team member. Finally, any vote on important decision needed a 2/3rds majority vote to go through. While this process wasn’t perfect, it did allow our team to handle relatively minor issues that came up during the years that we have been working on the project. Additionally, having this sort of agreement worked out and going through the process for establishing an LLC did allow us to have concrete discussions with other developers about joining the team which may have contributed to our success when it came to hiring new people when others had left the team. Finally, when we released the game shares became locked.

What’s Next.
The other big question we get asked is: what’s next? And, this is an extremely difficult question to answer at the moment as were still answering this for ourselves. I can say for certain that development on and support for Tumbleweed Express will continue for a time. Over the next year a few new features are likely to come out along with bug and user experience fixes. I can also say that we are beginning to reinvestigate some of the experimental content that we dropped after our Kickstarter campaign (e.g., VR Support); however, it is really too early to tell if any of these experiments will yield content that reaches the current quality standard set by the product that is now available on Steam. I can tell you that some of the things we were working on at our last game jam (#54) were exciting and I am really hoping to be able to share details with you all in the near future. Finally, another option we are considering is starting a brand new project (or projects) though at this time all I can say on that is: stay tuned! 

Tuesday, May 17, 2016

Launching on Steam: May 31, 2016




Tumbleweed Express will be launching on Steam:
 May 31, 2016
Check out our Steam Store page and be sure to click "Follow":

Monday, January 4, 2016

Saturday, May 30, 2015

Steam Greenlight & Audio Contributors Needed!

Hello indie game enthusiasts!

The Dirigiballers are proud to announce that we have have recently been Greenlit by Steam! Thus, we are currently ramping up on production planning and execution in the expectation of hitting our internal release date. Obviously we are very excited about these recent events and we hope to make a formal announcement regarding our final plans in the coming months. However, we do find that we are in need of passionate people to provide audio contributions to the project. 

Do you have a desire to work on epic western steam punk music? Are you looking to boost your portfolio by contributing to an independent game development project? Do you want to work on rad stuff and help fellow indies get things done? We would love to hear from you!

Our team has several critical audio needs and were looking for an individual (or two) who can compose and execute several tracks that match the game's current score. Additionally, we're looking for someone interested in putting a critical ear to the game's audio portfolio (i.e., identifying where sound effects are needed, what's working, what's not, and executing on improvements for the game). If this sounds like something you (or someone you know) would be interested in doing, please send a resume and a portfolio to: dirigiballers@gmail.com

Our current score is available on SoundCloud:


Our most recent trailer is on YouTube:




Cheers, 

The Dirigiballers

Saturday, October 4, 2014

Post Mortem: How to (not) get funded on Kickstarter in 30 days



                On Thursday, October 2nd at 2:00p.m. EST the Tumbleweed Express Kickstarter campaign ended unsuccessfully having reached ~22% of its funding goal. Firstly, I want to express my sincerest gratitude towards everyone who supported us during our campaign. The help we received from total strangers was powerful and surprising and the support we received from friends and family was heartwarming and encouraging. We met lots of new people, formed friendships and connections, and strengthened our ties with the communities that we came from. However, due to the all-or-nothing nature of Kickstarter we unfortunately will receive none of the amount that was raised.

Mostly I felt relieved at the end of it, as the negative emotions of frustration and disappointment had already run their course leading up to the final hours of the campaign. Despite the technical failure of the campaign however I do want to stress that this past month was the most successful, intense, and positive marketing push that our project has had in the three years we've been working on it. That said, I want to evaluate the campaign and give my impressions on what happened using the following categories:

What I Know We Did Right: Actions we took that tangibly benefit our campaign
What I Think We Did Right: Actions we took that, while imaginably positive, did not appear to tangibly benefit our campaign
What I Know We Did Wrong: Actions we took that tangibly hurt our campaign
What I Think We Did Wrong: Actions we took that, while imaginably negative, did not appear to tangibly hurt our campaign

What I Know We Did Right

Event Outreach
Undoubtedly, our team's strongest area of marketing is event outreach. We make it a point to be out at conventions, festivals, and gatherings as much as possible to get in lots of face-to-face marketing and feedback. During the Kickstarter campaign we were at the Boston Festival of Indie Games (FIG) and the Baltimore Innovate App Arcade where we built mailing-lists with fans and connections to other developers leading to pledge support and cross-promotional opportunities.

Public, Playable Demo
Having a public demo was very meaningful for our campaign because it meant we could give something tangible to interested parties and potential customers so they could get a real taste of what our game is about and the direction we're going in to reach a high standard of quality.

Event Journalist Outreach
Leading up to Boston FIG I made it a point to do some background research and send a personal email out with our info and demo to everyone on the Press list for the event. By the time it was time to drive up to Boston we had lined up five interviews throughout the one-day event which lead to a nice handful of articles and podcasts promoting our project and campaign.

Twitter Outreach
Halfway through our campaign I stepped up to take the reins on our Twitter outreach efforts. What I ended up doing was using software to scum the whole of the Twitterverse using search terms to find anyone talking about the tools or styles relevant to our project. If anyone on Twitter mentioned Unity Terrain I would send them a public message linking them to our branded Unity Heightmap tutorial on our dev blog. Did you mention #Steampunk? Maybe you'll like this game! And so on... garnering follows and likes and whatnot building tangible interest one user at a time.

Youtube Outreach
In the last third of our campaign I went on a mailing spree using a list of game-related Youtube personalities linked to me by another developer. Over three days I sent out a couple hundred personalized emails, with downloadable demo links, to the entire spectrum of youtube gamer popularity to see if anyone would make a video about our game. In the end we were able to connect with a handful of Tubers who generously filmed and posted Let's Play videos of our Demo while linking back to our campaign.

What I Think We Did Right

Campaign Crafting
To craft our Kickstarter campaign we sourced other successful campaigns and post mortems for inspiration while playing to the strengths of our theme. What we came up with was an art heavy campaign page with thematic designs for our sections, headers, graphics, and rewards. When comparing ourselves to other campaigns I would say that our biggest advantages were with our visuals, our video, our budget and the demo we put up.
We've seen other campaigns go by and get several times more than our funding goal while barely even having concept art to show for themselves. We thought to ourselves, "We have an actual playable project and a sweet trailer, we've guaranteed that this is a real project being worked on in earnest by real developers".
At the end of the day I can say we're damn proud of the campaign we built, but it's hard to say how effective it was without getting access to more exposure.

Update Crafting
Over the course of our campaign we put out 12 updates total, amounting to a little more than one update for every three days that the campaign was running. We made it a point to generate as much content as possible during the campaign to give as much interest to our backers as possible while also ensuring to them that we're serious about what we do and that we're working constantly to bring them new and valuable content.

IGM Partnership
Before our campaign started we actually partnered up with Indie Game Magazine, a digital publication which had previously featured our project in one of their issues. The deal was that in exchange for giving them a commission on specific reward tiers they would provide us with free subscriptions to distribute to our backers as well as run cross promotional efforts for them. Despite being hesitant we ultimately took the deal. During the campaign they promoted us on their social media accounts and wrote a couple of articles about us (despite misspelling our project name...). However, at the end of the day, it doesn't seem like much if any funding at all can be traced back to the cross promotion they ran for us.

Web Journalist Outreach
In the month before our campaign launched we put together a list of as many publications as we could think of and sent out a ton of personalized emails with our press release, some visual content, and a link to download a desktop version of our demo. Despite our efforts though we were unable to hook any big fish before or during our campaign.

What I Know We Did Wrong

Late Outreach
Despite our net positive response to our outreach efforts it seems that the majority of them came too late. Most successful campaigns seem to have had an effective  following before launching. In fact, you're supposed to have the first 30% of your funding ready to be pledged in the first week of your campaign. Obviously we were never able to hit that mark. Perhaps we over-estimated our support before launching, but we definitely didn't hit our stride in reaching out through Twitter and YouTube for example until after our campaign was in full swing.

Underutilizing Facebook
I believe that as a team we probably underutilized certain aspects of Facebook as a tool for funding. While we obviously did lots of 'Sharing' and 'Liking', I think it's safe to say that we didn't fully appreciate the power of simply messaging our friends for support. It wasn't until the final week of the campaign that I went and manually messaged everyone on my friendslist and in that process secured a handful of new backers while simultaneously getting the chance to reconnect with friends I haven't talked to in a while and who wanted to support our project. If more of our team was open to that process and if we had put our weight behind it earlier on then we would have been able to better frontload our initial funding amount.

What I Think We Did Wrong

Ignoring All Solicitors
We got a LOT of solicitors during and at the beginning of our campaign. I'm sure that there are companies that either scum Kickstarter manually or have automated systems that email their pitch to new campaigns. Regardless, we did some research early on but shortly after began to systematically ignore them. A lot of them offered services that seemed to overlap with work we'd already done, such as crafting content or creating press releases. On the other hand, they also made grand promises about the number of customers they were able bring our pitch to. Without formal marketing training it was hard to evaluate whether or not we would be taken advantage of , but I imagine that there are legitimate services that we passed up.


What We'll Do Now

As they say, the show must go on! We've received a majority of positive responses about our game leading up to and during the Kickstarter campaign and the continued support from strangers has bolstered our confidence in our design and art decisions.  Despite our lack of funding the team will continue to move into production with the intention of bringing the project into beta and wrapping up for release. The tools we need and our overhead expenses will continue to be paid for out of pocket and the team will contribute the time that's needed to make our project a reality.


Our continued marketing efforts will go towards improving and marketing our Steam Greenlight campaign so that we have a strong outlet to release from once the game is complete.

Saturday, September 20, 2014

Unity Heightmap Tutorial

Hey guys!

Jake here. So we've been working with Unity terrain for a while and have run into plenty of hurdles and challenges. One issue we had was that our heightmaps for a long time were imported as segments instead of solid pieces, meaning that each piece needed it's own texture pallet; which is fine for a once-over but quickly became very tedious when needing to update textures or terrain data. The solution was to combine the pieces into a single height map, but that was easier said than done. In fact it took a lot of digging and some experimentation to really get a grip on the import/export process for height maps with Unity. Below is a video tutorial I recorded to demonstrate the method I settled on so I could pay it forward with anyone who might be running into the same problem!



Tuesday, September 2, 2014

Kickstarter Has Launched: Show Your Support!

We are very excited to officially launch our Kickstarter:

https://www.kickstarter.com/projects/2014686147/tumbleweed-express-the-steampunk-railshooter-tower

It would be great for the team if you could please show your support during this time! Once you have checked out our Kickstarter page, we have also re-launched on Greenlight with a full project moving forward on Steam; show us your thumbs:

http://steamcommunity.com/sharedfiles/filedetails/?id=309156049

Thanks again for all your support!

Sunday, August 10, 2014

Video Gamers United & Kickstarter

It has been a long 3 year journey to get this far, but I am happy to report that The Tumbleweed Express will be back on the road! Our team is really excited to announce that our latest build we be presented at Video Gamers United:


Our build will present a number of new features, which include:
  • New enemies & fancier graphics.
  • The beginning of our open world level system.
  • Scaling difficulty of levels through side contracts.
  • Improved weapons and interfaces.
  • General usability improvements.
We're also proud to announce that we will launching our Kickstarter in September and we're inviting you all to be apart of the final stages of the production of our project. Finally, if you can't make it to VGU then you will have another chance to play the game at the Boston Festival of Indie Games also in September. Looking forward to seeing you take control of the Tumbleweed and supporting our team at these upcoming events!

Wednesday, October 2, 2013

New Alpha Trailer!

We just posted a new trailer showing off all our recent progress!

Check it out!




We also revamped out Greenlight concept page with new screenshots, descriptions, and of course the shiny new trailer. Please take a look and rate/favorite us :D

or Click Here

Wednesday, August 14, 2013

Another day. Another new level.

After several Jams of tidying up old levels for various showcases, we have finally had the chance to create a new one!

We went from this sketch in MS Paint:


To this first draft in L3DT:


And ended with this final landscape in Unity3D:                         
                               

We are very excited for the mission we have planned for this level. Here the Tumbleweed will come face to face with a new foe. Who is she? What does she want? And what's with all the sweet talk she's sending on over to dear old Sneaky Pete? Not quite sure I want to know the answer to that, but more to come soon!

Wednesday, July 3, 2013

Ride The Rails: With Oculus Rift!

Needless to say, here at Dirigiballers, we're excited about the Oculus Rift and the new opportunities it brings for immersive gaming... and our development kit finally arrived! This past weekend, we imported the Unity plugin and took it for a spin. If you've got your own headset, swap this video to fullscreen and check it out!



From a development standpoint, dropping the prefab into the scene was a breeze. After some selective renaming of cameras and connecting a few scripts, we were right there in it - riding the Tumbleweed Express. You can see from the footage it threw off our projectile launchpoint to a space above the caboose's turret (probably the result of interaction between our script calculating projectiles from a third-person mouse orbit and the "neck-height" magic of the Oculus prefab) - and of course our reticle disappeared since it's still keyed to the center of the screen and the GUI is pretty much lost. But the resulting feel? AMAZING. The depth really brings out the beauty of the Tumbleweed world and integrates well with our aiming mechanic (some of us don't want to go back to a mouse). The "aim-with-your-eyes" approach does make it difficult to switch back and forth between the front and back of the train so we'll get Sneaky Pete working on some sort of lever to bring the turret around faster than your neck can move.

Like many backers, we were disappointed to learn that "Unity Support" means "Unity Support (with Pro License)" but are grateful for the extension of the pro-trial to four months. Since upgrading the team to full licenses is somewhere in the range of college tuition or a new car, we experimented a bit with rolling our own dual-camera prefabs. Could we write our own scripts to read Oculus as a controller and simply output a 1280x800 view from two cameras? Splitting the screen for dual cameras is a pretty simple trick: under the Inspector Camera settings, use the "Normalized Rect View" to set the left camera W to 0.5 with X set to 0 right camera W to 0.5 with X set to 0.5 (i.e. make the cameras half as wide as the screen, and have the right camera view start at the midpoint of the screen). It's easy to see how this could be used in lots of games for handling multi-player on a single screen. With some key-commands to adjust the distance and rotation of the cameras, we were able to calibrate certain parts of the field of view into focus manually, but getting the whole thing in sync seems to depend on the "fisheye" lens-distorting you can see in the increasingly recognizable Oculus-view videos. The difference between the plugin and the roll-our-own dual screen was striking so much appreciation to the Oculus team for doing the heavy lifting! If you're reading this: thanks for keeping the SDK builds coming - we're hoping you're in talks with Unity to find a solution that will make it possible for indie devs to use it without breaking the bank.

Tuesday, June 18, 2013

Too Many Games: The Results



This past weekend a few of the Dirigiballers took our latest build to “Too Many Games”, which is an awesome convention just outside of Philadelphia. This was a major milestone for our team for many reasons. First, we hired two new developers (Duy Le and Ben Heard) this year and this build represents the first major release of their work alongside the continued efforts of our more senior team members. We saw the release of: (1) Track Switching, (2) Activate-able Bridges, (3) improved Dirigible behaviors and capabilities, (4) polished new menus centered around the improved Battle Menu that provides micromanagement of the players train, and (5) further integration of story and tutorial elements. I could go on about all the improvements, but let’s just say that we set our goals earlier this year and we exceed expectations.

Too Many Games: Pushing builds and playtesting!

The audience at the convention really seemed to enjoy all the new features and they provided ample feedback that we hope to incorporate into our next build shortly. It was a real pleasure to interact with the Philadelphia crowd, you guys really make game development worth it! I wanted to mention a few of the awesome indie developers that we got to speak with. The crew behind “Default Dan” was awesome to network with and their game is excellent. There was also a neat student project by the “The Automatic Gentlemen”, which was a great throwback to the platformer days of old though with an updated modern look and cheeky characters! Finally, a quick shout out to Horizon’s End and Broken Crown Games, LLC. Please check out these titles as well as all the other great Indies that attended the convention.

Too Many Games: A portion of our awesome audience and playtesters!

You might ask, what is next for the Dirigiballers? Well were still looking for support on our recently launched Steam Greenlight Concept page. We have an upcoming jam in two weeks where we will be setting our priorities for the rest of the year. We should also be working on updating our game trailer to show off the new content. We are really hoping to make it back to MAGFest and demo our planned co-op experience for the audience. In the immediate future, we will be bringing an intermediate build to “Indie Con DC”. This is a free and open to all ages event that is hosted by “Gaming in Public” and features many great Indie developers from around the DC area. The event will take place on August 3rd, 2013 at PUBLIC tenley.  Additionally, Indie developers can get in touch with the organizers to get their games into the event. Please come out and support all the organizations that are making this unique event possible!

Tuesday, May 21, 2013

Terp Indie Games Fest [Video]

Back in April we had a chance to talk about our game and what it was like on our team. We gave a presentation to the club and then demoed the game at their Indie Showcase with some other great local indie developers: e4Software, Omiya Games, and the guys from Broalition. The presentation was recently made available and is actually a pretty good summary of where we are at and where we think we're going at this point. If you have the time I highly suggest watching the video and checking out some neat indie projects here in the DMV area.


We would also thank the University of Maryland's Game Development Club for giving us the opportunity to speak with them. Good luck on your projects because we can't wait to check them out!

Sunday, May 5, 2013

The Dirigiballers are Going Full Steam Ahead!

The team is still hard at work making improvements to the game on all fronts. More updates will be coming soon, but for now heres what our awesome art team has been working on:

First, those crafty Dirigibles have a new "Dirigibomb" in their arsenal in an effort to squash that pesky train from meeting its destination:

  
Don't worry though, the train has also had some improvements, getting a tougher look and different compartments on the sides for marshals to help protect the cargo:



It will still be tough travelling the Badlands with all sorts of new challenges in front of the Tumbleweed Express, including a new heavily guarded draw bridge.


We are continuing to add new content every week, including new shootable props and environmental upgrades:



The Dirigiballers are still doing full steam ahead! Stay tuned for updates from the Code and Design teams. They are also working on some awesome stuff that'll take this game to a whole new level! :D

Friday, April 19, 2013

Presenting Badlands

After a lot of hard work, the Tumbleweed heads north. Presenting, 
Act 2:The Badlands!


Thursday, March 28, 2013

Unity C# AudioManager Tutorial - Part 4 (Music, Pausing, & Voice Over)

This is part 4 of a 4 part tutorial to create an audio manager for unity. This tutorial focuses on playing music, pausing fx while keeping music playing, and controlling sound volume including fading.

Part 1 | Part 2 | Part 3 | Part 4

Playing Music

We'll need a couple more key concepts to play music, but the first is easy. We want music to loop, so we can just call the PlayLoop method with our music clip. In order to distinguish the music from other sounds playing, we will create a member variable m_activeMusic in the AudioManager that will hold a reference to this particular AudioSource. Unless otherwise noted, all code in this tutorial is in our AudioManager class.
    private AudioSource m_activeMusic;
    public AudioSource PlayMusic(AudioClip music, float volume) {
        m_activeMusic = PlayLoop(music, transform, volume);
        return m_activeMusic;
    }
Notice that no transform parameter is passed in to identify the music position. Here I am assuming that in game music always plays at its given volume, and is not affected by any object movement in the game. To achieve this, we can to attach the music sound to the transform of the AudioManager, which, as explained in part 1, is attached to the camera, giving it a constant distance from the audio listener of 0.

The PlayLoop method adds the music sound to our m_activeAudio, but with m_activeMusic we can track this particular sound special, which we will utilize in the next section, fx pause control.

Pause Control

Now that we have a list of active sounds in m_activeAudio, we can pause them on command. With our reference to the music, we can indicate that the pause function only pauses all active sounds that are NOT the music:
public void pauseFX() { 
    foreach (var audioClip in m_activeAudio) {
        try {
            if (audioClip.source != m_activeMusic) {
                audioClip.source.Pause();
            }
        } catch {
            continue;
        }
    }
}

public void unpauseFX() {
    foreach (var audioClip in m_activeAudio) {
        try {
            if (!audioClip.source.isPlaying) {
                audioClip.source.Play();
            }
        } catch {
            continue;
        }
    }
}
Fairly straight forward- when AudioManager.Instance.pauseFX() is called, the method cycles through each of the active sounds, and if the current iterator is not the music, it calls Unity's built in 'AudioSource.pause' function. The try/catch block is to catch any timing issues where the audio source is destroyed while this is iterating through the list. If there are any issues on a given element in the list, the method catches the error and simply skips that element, and next time we pause or unpause, the ClipInfo object that errored out on this pass should have been removed in our updateActiveAudio method from part 3.

When the unpauseFX function is called, it goes through the active sounds, and if any are not playing, it plays them. If the audio is not paused and not playing, Unity automatically resumes them from where they paused. If you have any issues where sounds are restarting unexpectedly, this is the place to check first.

Voice Overs

To make sure we can clearly hear a voiceover, we're going to make all sounds that are currently playing quieter while the voiceover is playing. Similar to music, we create a member variable that tracks the active voiceover that is playing. Also like music, we assume that the voiceover is full volume regardless of object positions in the world, so we attach the voice over to the AudioManager transform (the camera.) We're also going to need a volumeMod variable that holds the amount to reduce all the other sounds' volume, and set it low when the voice over is started.
private AudioSource m_activeVoiceOver;
private float m_volumeMod = 1.0f;
public AudioSource PlayVoiceOver(AudioClip voiceOver, float volume) {
    AudioSource source = Play(voiceOver, transform, volume);
    m_activeVoiceOver = source;
    m_volumeMod = 0.2f;
    return source;
}
Now that we have m_volumeMod, we can revisit our updateActiveAudio method to add volume modification to all sounds. Before we loop through the sounds, we will check if m_activeVoiceOver is set, and if it is not, we will set the volume mod to 1.0 (normal volume.) Otherwise, we will keep it's current value, which should be set at 0.2 (as in the PlayVoiceOver method above). Then, when iterating through the active audio, we will check if the audio is our m_activeVoiceOver, and if it is not, we will make the current volume its default volume multiplied by the volume modifier. This makes all sounds but the voice over affected by the volume mod:
private void updateActiveAudio() {
    var toRemove = new List<ClipInfo>();
    try {
        if (!m_activeVoiceOver) {
            m_volumeMod = 1.0f;
        }
        foreach (var audioClip in m_activeAudio) {
            if (!audioClip.source) {
                toRemove.Add(audioClip);
            } else if (audioClip.source != m_activeVoiceOver) {
                audioClip.source.volume = audioClip.defaultVolume * m_volumeMod;
            }
        }
    } catch {
       Debug.Log("Error updating active audio clips");
       return;
    }
    //cleanup
    foreach (var audioClip in toRemove) {
        m_activeAudio.Remove(audioClip);
    }
}
There you have it. You can extend this type of pausing/volume modification functionality to additional AudioSource variables if you have other types of sound events in your game. You can also use the concept behind this volumeMod variable to create an in-game master volume slider for the player.

Fading Sound

As a final bonus feature, we can make the voice over modification fade the other sounds in and out instead of just snapping to the volume modification. This is a more minor trick, and it requires a few new variables, but it has a pretty significant effect, though subtle in execution.

First we'll have to add two new variables. A min volume variable to establish how low the volume should go during voice overs, and a voice over fade bool to indicate if we are currently fading for the voice over or not. We'll initialize these new variables in our Awake method.
private float m_volumeMod, m_volumeMin;
private bool m_VOfade; //used to fade to quiet for VO

void Awake(){
    Debug.Log("AudioManager Initializing");
    try {
        transform.parent = GameObject.FindGameObjectWithTag("MainCamera").transform;
        transform.localPosition = new Vector3(0, 0, 0);
    } catch {
        Debug.Log("Unable to find main camera to put audiomanager");
    }
    m_activeAudio = new List<ClipInfo>();
    m_volumeMod = 1;
    m_volumeMin = 0.2f;
    m_VOfade = false;
    m_activeVoiceOver = null;
    m_activeMusic = null;
}
We will update our Update method to gradually decrease the volumeMod to our min volume if our VOfade bool is true, and gradually increase the volumeMod to 1 if our VOfade bool is false. We will have the PlayVoiceOver method activate our VOfade bool, and have the updateActiveAudio method check the m_activeVoiceOver AudioSource and deactivate VOfade if it is null (if the voiceover is not currently active):
void Update() {
    //fade volume for VO
    if (m_VOfade && m_volumeMod >= m_volumeMin) {
        m_volumeMod -= 0.1f;
    } else if (!m_VOfade && m_volumeMod < 1.0f) {
        m_volumeMod += 0.1f;
    }
    updateActiveAudio();
}

public AudioSource PlayVoiceOver(AudioClip voiceOver, float volume){
    AudioSource source = Play(voiceOver, transform, volume);
    m_activeVoiceOver = source;
    m_VOfade = true;
    return source;
}

private void updateActiveAudio() { 
    var toRemove = new List<ClipInfo>();
    try {
        if (!m_activeVoiceOver) {
            m_VOfade = false;
        }
        foreach (var audioClip in m_activeAudio) {
            if (!audioClip.source) {
                toRemove.Add(audioClip);
            } else if (audioClip.source != m_activeVoiceOver) {
                audioClip.source.volume = audioClip.defaultVolume * m_volumeMod;
            }
        }
    } catch {
        Debug.Log("Error updating active audio clips");
        return;
    }
    //cleanup
    foreach (var audioClip in toRemove) {
        m_activeAudio.Remove(audioClip);
    }
}
Note that I am adjusting m_volumeMod by 0.1 each update. This struck a nice balance for me between having it happen quickly enough to still be effective, but slow enough to keep a smoothness in the transition.

Conclusion

You've reached the end of the tutorial! You should now have a straightforward audio manager that acts as a singleton, allows clip playing and control from anywhere, tracks active sound clips, pauses select sounds, fades select sounds, and is simple to modify for further functionality. I hope this provided insight into a way to more easily manage audio in your projects. If you have any questions or feedback, please leave it in the comments, or feel free to contact me at baheard@gmail.com. Special thanks to Herman Tulleken and Daniel Rodriguez for their related posts.

Friday, March 22, 2013

Unity C# AudioManager Tutorial - Part 3 (Control)

This is part 3 of a 4 part tutorial to create an audio manager for unity. This tutorial builds on the structure developed in part 1, expanding the functionality described in part 2. It describes new Play options, and how to manage tracked audio clips.

Part 1 | Part 2 | Part 3 | Part 4

Overloading Play

The next step is to create a play method that plays a sound on top of an existing object, so when the object moves, the sound moves with it. This is useful for things such as a car with a siren, or a character who is walking while they talk. This is another core method, and similar to our previous play method. Again, the concept is referenced from Daniel Rodriguez's helpful post. Except where specified otherwise, all methods in this section of the tutorial are within our AudioManager class.
    
public AudioSource Play(AudioClip clip, Transform emitter, float volume) {
    var source = Play(clip, emitter.position, volume);
    source.transform.parent = emitter;
    return source;
}
Note that instead of a Vector3 parameter, we pass in the GameObject Transform that is emitting the moving sound. This method calls the default play method, creating an object at the emitter position and playing the sound there, but it then attaches the transform of the AudioSource (which is shared by its GameObject) to the emitter object by making the emitter its parent. Now the sound will follow the emitter where it goes. Remember that the original Play method registers the new sound object with the m_activeSounds collection, and destroys the GameObject after the length of the sound has played.

Looping Sound

Since the original play method destroys the sound object after the length of the clip, to loop we need a new method that won't destroy it. It also has to set the audiosource "loop" property to true. I assumed that any sound that loops will have an emitter, but if you needed a looping sound that played in one position in space, you could just remove the line that sets the transform.parent. This method is almost identical to the original except for these changes.
public AudioSource PlayLoop(AudioClip loop, Transform emitter, float volume) {
    //Create an empty game object
    GameObject movingSoundLoc = new GameObject("Audio: " + loop.name);
    movingSoundLoc.transform.position = emitter.position;
    movingSoundLoc.transform.parent = emitter;
    //Create the source
    AudioSource source = movingSoundLoc.AddComponent<AudioSource>();
    setSource(ref source, loop, volume);
    source.loop = true;
    source.Play();
    //Set the source as active
    m_activeAudio.Add(new ClipInfo{source = source, defaultVolume = volume});
    return source;
}
The setSource method becomes valuable now, because if we want to change any default settings of all the AudioSources that are created through the AudioManager, we only have to change it in the setSource method, and it will updated both this and the original Play method.

Stopping the Looping Sound

Now we've got a looping sound going on endlessly, we need to be able to stop it! This is a simple method that requires you to track the sound you'll want to stop at start time, and pass it in as a parameter when you're ready to stop it.
public void stopSound(AudioSource toStop) {
    try {
        Destroy(m_activeAudio.Find(s => s.source == toStop).source.gameObject);
    } catch {
        Debug.Log("Error trying to stop audio source "+toStop);
    }
}
This highlights why it is useful to have all the Play methods return the AudioSource created. Here is an example of a new Init class using the loop and stop methods:

public class Init : MonoBehaviour {
    //testSound is public in order to set in the inspector
    public AudioClip testSound; 
    private AudioSource playingSound;

    void Start(){
        playingSound = AudioManager.Instance.PlayLoop(testSound, m_enemy.transform, 1)
    }

    void Update(){
        if(Input.GetKeyDown(KeyCode.Space)){
            AudioManager.Instance.stopSound(playingSound);
        }
    }
}
Note that since loop sounds are assigned to a parent on creation, if that parent is destroyed for any reason, the sound will be destroyed with it automatically.

Cleaning Up the Audio List

For the final section of this tutorial, I will describe the mechanism for cleaning up inactive ClipInfo objects from our m_activeAudio list. This method will also be key later to control the sounds dynamically.
private void updateActiveAudio() { 
    var toRemove = new List<ClipInfo>();
    try {
        foreach (var audioClip in m_activeAudio) {
            if (!audioClip.source) {
                toRemove.Add(audioClip);
            } 
        }
    } catch {
        Debug.Log("Error updating active audio clips");
        return;
    }
    //cleanup
    foreach (var audioClip in toRemove) {
        m_activeAudio.Remove(audioClip);
    }
}
The method creates an empty list of ClipInfo to remove. We can't remove them in the foreach loops since it would illegally modify the iterator. The method then goes through all the ClipInfos in m_activeAudio and checks each one's AudioSource. In the Play method the AudioSource was set to destroy after playing, so after the sound is finished, the AudioSource source of the ClipInfo will be null. If it is null, we add it to the toRemove list to be removed. After going through all active sounds, we go through all the toRemove ClipInfos and remove them from the m_activeAudio list. Since the ClipInfo list is not directly tied to the AudioSource it holds, this will work no matter how the AudioSource is destroyed (time or parent death.)

Lastly we have to call this in the AudioManager Update() method so that it will be called continuously.
void Update() {
    updateActiveAudio();
}

Next time...

We now have a working AudioManager that will play a sound at a location, play a sound at an emitter, play a looping sound, stop a looping sound, track all active sounds, and clean them up as they finish. In part 4, I will show how this setup allows for dynamic centralized control, including implementing music, pausing non-music sounds, global volume control, and select volume control (for things like voice overs.)

Friday, March 15, 2013

Unity C# AudioManager Tutorial - Part 2 (Play)

This is part 2 of a 4 part tutorial to create an audio manager for unity. This tutorial focuses on creating the main methods that will play sounds, as will as tracking and managing active sounds.

Part 1 | Part 2 | Part 3 | Part 4

AudioManager - ClipInfo Object

We will first create a ClipInfo object as a new data type to hold all the relevant data about each sound that is handled by the audio manager. Right now it contains only variables for the sound clip to play and its initial volume, but it could be expanded to hold any information you want to keep track of, such as the time the clip started playing, or how many times it has looped. We will add a member variable List of this ClipInfo to our AudioManager to track all audio playing, and we will update our Awake method to initialize this List.
public class AudioManager : Singleton<AudioManager> {
    class ClipInfo
    {
       //ClipInfo used to maintain default audio source info
       public AudioSource source { get; set; }
       public float defaultVolume { get; set; }
    }

    private List<ClipInfo> m_activeAudio;

    void Awake(){
       m_activeAudio = new List<ClipInfo>();
       
       [see previous tutorial]
    }
}
First note that the ClipInfo class is inside the audio manager. This allows it to be accessed only by the audio manager, hiding the data. You can read the rules on nesting classes here.

Second, notice that ClipInfo holds the AudioSource directly, ignoring the GameObject that the source must be attached to.

Lastly, we create a 'defaultVolume' property so that we can change the volume of the audio source dynamically, but remember what volume it was created at. More on this in part 4.

The Play Method

For the core functionality of this AudioManager, I’ve referenced Daniel Rodriguez for the basic idea (I also recommend his post on setting up managers for your unity projects.) The Play method of the AudioManager will:
  1. Create a new GameObject.
  2. Set its AudioSource to be our given sound clip.
  3. Play the AudioSource.
  4. Destroy the object.
The beauty of this is that we can play a given audio clip at any time by writing AudioManager.Instance.Play(parameters). We don’t have to worry about creating the AudioManager since it is a singleton, we don’t have to worry about creating and destroying the accompanying GameObjects because everything is handled within the method.
    public AudioSource Play(AudioClip clip, Vector3 soundOrigin, float volume) {
       //Create an empty game object
       GameObject soundLoc = new GameObject("Audio: " + clip.name);
       soundLoc.transform.position = soundOrigin;

       //Create the source
       AudioSource source = soundLoc.AddComponent<AudioSource>();
       setSource(ref source, clip, volume);
       source.Play();
       Destroy(soundLoc, clip.length);
       
       //Set the source as active
       m_activeAudio.Add(new ClipInfo{source = source, defaultVolume = volume});
       return source;
    }
For sound data, the method takes an audio clip and a volume value as parameters, but could be extended to take other parameters to modify the sound, such as pitch.

The other parameter here is the Vector3 that indicates the position of the sound. This is where the sound is playing from, and uses Unity’s built in sound management to determine the panning and volume of the sound based on the position of the audio listener component in your scene and the rolloff for the audio source (more in rolloff in a little bit.)

This isn’t a long method, and it is the most critical one, so I will step through the code.
   //Create an empty game object
   GameObject soundLoc = new GameObject("Audio: " + clip.name);
   soundLoc.transform.position = soundOrigin;
This creates an empty GameObject in the world. When running the game, this will show up in the inspector as “Audio: [whatever your clip name is as listed in the Unity assets]”. It then moves this newly created object to the Vector3 position passed in.

Next we attach an audio source to the fresh GameObject we just created:
   //Create the source
   AudioSource source = soundLoc.AddComponent<AudioSource>();
   setSource(ref source, clip, volume);
   source.Play();
   Destroy(soundLoc, clip.length);
After creating the AudioSource, we pass a reference of it to ‘setSource’, which I will outline shortly. Basically this method takes the values passed in and applies them to the AudioSource of our newly created GameObject. We start the AudioSource playing, then call the Destroy method on the new GameObject, passing clip.length as the parameter of how long to wait before destroying the object. Since clip.length is the time it takes to play the clip, this will destroy our AudioSource GameObject immediately after finishing the sound. This is essentially like playing a one shot of an AudioClip, but since it is an AudioSource, we can do more things with the clip, such as changing it’s volume while it is playing, or pausing it, which we will take advantage of later.

Lastly, we register this audio source with the AudioManager as a ClipInfo object which we created earlier. This way the AudioManager tracks the sound and can handle it as it is playing.
   //Set the source as active
   m_activeAudio.Add(new ClipInfo{source = source, defaultVolume = volume});
   return source;
Since ‘source’ and ‘defaultVolume’ are public properties of our ClipInfo object, we can assign them using object initializers (using the curly braces), which allows us to avoid the need for an explicit constructor. We add the new ClipInfo object to our AudioManager member variable List m_activeAudio, which maintains references to all the sounds that are playing.

setSource Method

For the final part of this section of the tutorial, we will create the “setSource” method referenced above in the “Play” method. This method sets the properties of our new audio source.
private void setSource(ref AudioSource source, AudioClip clip, float volume) {
   source.rolloffMode = AudioRolloffMode.Logarithmic;
   source.dopplerLevel = 0.2f;
   source.minDistance = 150;
   source.maxDistance = 1500;
   source.clip = clip;
   source.volume = volume;
}
The AudioSource is sent in as a ‘ref’, so that all changes made to the AudioSource parameter passed in will be kept after this method is finished and our ‘Play’ method resumes.

Rolloff mode is the way that Unity makes the sound decrease in volume as its distance from the AudioListener in the scene increases. In the inspector you can see a graph that models this.

The Linear mode means that the sound decreases at an equal rate as you get further from the listener. The Logarithmic mode means that the sound decreases sharply as you begin to move away from the listener, but the farther you get away from the listener, the less the sound decreases. I opted for Logarithmic sound because it more closely matches the way you hear sounds in reality- a noise made at 10ft away will sound much louder than the same noise at 100ft away, but a noise at 500ft away will sound only slightly louder than the same noise at 800ft away.

The dopplerLevel is related to how the sound is affected by movement (like sirens on a passing ambulence.) I set this to .2 because I found that the default value of 1 can create strange sound anomalies when rotating the camera quickly.

minDistance and maxDistance are important, particularly for Logarithmic rolloff.

minDistance is how far the sound must be from the listener before the volume starts to decrease. This is especially important because if this is set to its default 0, the sound will start to drop off sharply at any distance from the listener. This means that a sound made at 1ft away will be significantly louder than a sound made at 2 ft away, which just does not feel right in-game. For Tumbleweed, a distance of 150 seems to be working for now- all sounds less than 150 game ‘units’ away will play at max volume, and after that the sound dropoff will begin.

maxDistance is important because it determines both the maximum distance at which a sound will play, as well as the curve of sound dropoff. With logarithmic rolloff, you can make this number really huge, and sounds that play in the far distance and middle distance will still sound natural.

Finally, we set the sound clip to our clip, and the default volume to our volume.

We covered a lot here. If set up correctly, you should now be able to play any AudioClip by calling “AudioManager.Instance.Play(AudioClip, Vector3, float)” with your desired clip, position, and volume.

Here is a my project setup and in-game hierarchy in Unity so far:

Next time...

In part 3 of the AudioManager tutorial, I will cover placing moving sounds on moving objects, pausing sounds, and changing volumes.