A highly optimized 2D
AudioClip pooling system built from the ground up for awesomeness. Utilized by studios across a multitude of platforms, this is battle tested and ready to rock!
- Components
-
Hydrogen.Core
-
hAudioStack.cs
Overview
From as simple as handling UI sound effects to the managing of a complex ambient soundscape, the Hydrogen.Core.AudioStack can do it all at the same time! This system allows for anyone to easily play 2D AudioClips on demand or cached in the most optimal way possible. When an Hydrogen.Core.AudioStackItem is told to be played, the system intelligently picks an available AudioSource to play the Hydrogen.Core.AudioStackItem, and when completed returns the AudioSource back to the stack for future use.
The hAudioStack
Singleton creates an instance of Hydrogen.Core.AudioStack which can be globally accessed via hAudioStack.Instance
.
AudioStackItem
The Hydrogen.Core.AudioStackItem class is a representation of all information related to a particular AudioClip playing, both in and outside of the hAudioStack
.
When you use hAudioStack.Instance.Add
and pass it an AudioClip, a new instance of a Hydrogen.Core.AudioStackItem will be created on the fly for you related to the information passed through arguments to it and subsequently use it appropriately. The class has numerous fields and methods publicly accessible, however it should not often be managed directly as the hAudioStack
handles all of it automatically in most cases.
Usage
There really isn't much to using the hAudioStack
, it is one of those solutions that is meant to have a very minimal footprint code wise but can be easily expanded on to accommodate for much larger goals. Typically, you are just going to be using hAudioStack.Instance.Add
in a fire-and-forget manner to accomplish what you want.
Public Fields
Type |
Name |
Description |
int |
MaximumSources |
Maximum number of sources to have in total (Stack + Playing). |
int |
MinimumSources |
Minimum number of sources to have in total (Stack + Playing). |
bool |
Persistent |
Should the hAudioStack instance (and it's GameObject) survive scene switches by using GameObject.DontDestroyOnLoad. This will preserve all scripts on its GameObject as well, so please be aware of that behaviour. |
bool |
UsePriorities |
Should the stack look at the priority of the existing AudioStackItems playing, and if the stack is full stop the lowest and use it's newly available source to play the new item. It should be noted that this will ignore any playing sources that are set to loop. |
Properties
Public Methods
Does a
hAudioStack
instance exist?
None
// Let's see if we are using the hAudioStack component.
if (hAudioStack.Exists ()) {
// Yes - We are that awesome.
Debug.Log ("A AudioStack Already Exists");
}
Type |
Value |
Description |
bool |
true |
An instance of hAudioStack already exists. This can be handy to simply check from other classes if hAudioStack is being used. |
bool |
false |
An instance of hAudioStack does not exist, and therefore must be created or instantiated via a prefab. |
Add/Load an
Hydrogen.Core.AudioStackItem which is created dynamically from the passed
AudioClip.
// Example Data
public AudioClip ourClip;
string _ourKey;
// Let's just use Unity's function for fun.
void Awake ()
{
// Add (and by default AutoPlay) the clip.
_ourKey = hAudioStack.Instance.Add (ourClip);
// Show some output love!
Debug.Log ("Our Assigned Key: " + _ourKey);
}
Add/Load an
Hydrogen.Core.AudioStackItem which is created dynamically from the passed
AudioClip.
// Example Data
public AudioClip ourClip;
string _ourKey;
// Let's just use Unity's function for fun.
void Awake ()
{
// Add (and by default AutoPlay) the clip.
_ourKey = hAudioStack.Instance.Add (ourClip, true);
// Show some output love!
Debug.Log ("Our Assigned Key: " + _ourKey);
}
Add/Load an
Hydrogen.Core.AudioStackItem.
// Example Data
public Hydrogen.Core.AudioStackItem ourItem;
string _ourKey;
// Let's just use Unity's function for fun.
void Awake ()
{
// Fun Stuff -- Cause We Can
ourItem.PlayOnLoad = false;
ourItem.Loop = true;
// Add (and by default AutoPlay) the clip.
_ourKey = hAudioStack.Instance.Add (ourItem);
// Show some output love!
Debug.Log ("Our Assigned Key: " + _ourKey);
}
Add/Load an
Hydrogen.Core.AudioStackItem.
// Example Data
public Hydrogen.Core.AudioStackItem ourItem;
string _ourKey;
// Let's just use Unity's function for fun.
void Awake()
{
// Fun Stuff -- Cause We Can
ourItem.PlayOnLoad = false;
ourItem.Loop = true;
// Add (and by default AutoPlay) the clip.
_ourKey = hAudioStack.Instance.Add (ourItem, true);
// Show some output love!
Debug.Log ("Our Assigned Key: " + _ourKey);
}
Determines whether the
AudioClip is loaded via a
Hydrogen.Core.AudioStackItem in the
Hydrogen.Core.AudioStack.
// Example Data
AudioClip ourTarget;
// Let's check it out!
if (hAudioStack.Instance.IsLoaded (ourTarget))
{
// Yup we got it!
Debug.Log ("AudioClip Found");
}
Determines whether the
Hydrogen.Core.AudioStackItem is in the
Hydrogen.Core.AudioStack.
// Example Data
Hydrogen.Core.AudioStackItem ourTarget;
// Let's check it out!
if (hAudioStack.Instance.IsLoaded (ourTarget))
{
// Yup we got it!
Debug.Log ("AudioStackItem Found");
}
Determines whether something is loaded in the
Hydrogen.Core.AudioStack by reference to it's key.
// Let's check it out!
if (hAudioStack.Instance.IsLoaded ("myKey"))
{
// Yup we got it!
Debug.Log ("Item Found Using Key");
}
Determines whether the
AudioClip is playing via a
Hydrogen.Core.AudioStackItem in the
Hydrogen.Core.AudioStack.
// Example Data
AudioClip ourTarget;
// Let's check it out!
if (hAudioStack.Instance.IsPlaying (ourTarget))
{
// Yup we got it!
Debug.Log ("AudioClip Playing");
}
Determines whether the
Hydrogen.Core.AudioStackItem is playing in the
Hydrogen.Core.AudioStack.
// Example Data
Hydrogen.Core.AudioStackItem ourTarget;
// Let's check it out!
if (hAudioStack.Instance.IsPlaying (ourTarget))
{
// Yup we got it!
Debug.Log ("AudioStackItem Playing");
}
Determines whether something is playing in the
Hydrogen.Core.AudioStack by reference to it's key.
// Let's check it out!
if (hAudioStack.Instance.IsPlaying ("myKey"))
{
// Yup we got it!
Debug.Log ("Item Playing Using Key");
}
Removes the specified
AudioClip and it's associated
Hydrogen.Core.AudioStackItem from the
Hydrogen.Core.AudioStack.
// Example Data
AudioClip targetClip;
// Free up some resources!
hAudioStack.Instance.Remove (targetClip);
Nothing
Removes the
Hydrogen.Core.AudioStackItem from the
Hydrogen.Core.AudioStack.
// Example Data
Hydrogen.Core.AudioStackitem targetItem;
// Let's Add It For Kicks.
hAudioStack.Instance.Add (targetItem);
// Free up some resources! Bye bye!
hAudioStack.Instance.Remove (targetItem);
Nothing
Remove the
Hydrogen.Core.AudioStackItem associated to the specified key in the
Hydrogen.Core.AudioStack.
// Removing can be this easy - Sometimes.
hAudioStack.Instance.Remove ("someKey");
Nothing
No more building coroutines to handle your web postings, you give it a destination, payload, and a callback; it handles the rest. Now if only making a game could be this easy.
- Components
-
Hydrogen.Core
-
hWebPool.cs
Overview
Why does making a simple REST API (for example) call require more then a single line of code? No more we say! We have created a system that provides a "fire and forget" solution to Unity when it comes to web based interactions via GET, POST, and FORM data.
The hWebPool Singleton creates an instance of Hydrogen.Core.WebPool which can be globally accessed via hWebPool.Instance
.
Usage
The guiding principle is that you can fire off as many web calls as you would like (limited by the the pool size), and the Hydrogen.Core.WebPool will manage and handle the rest for you. Eventually we would like to see this being done entirely on a separate thread, however that would require including System.Net, which has a rather hefty distribution size.
Public Fields
Properties
Type |
Access |
Name |
Description |
hWebPool |
get |
Instance |
Returns the static hWebPool instance (Thread Safe Singleton). |
Public Methods
Does a
hWebPool
instance exist?
None
// Let's see if we are using the hWebPool component.
if (hWebPool.Exists ()) {
// Yes - We are that awesome.
Debug.Log ("A WebPool Already Exists");
}
Type |
Value |
Description |
bool |
true |
An instance of hWebPool already exists. This can be handy to simply check from other classes if hWebPool is being used. |
bool |
false |
An instance of hWebPool does not exist, and therefore must be created or instantiated via a prefab. |
HTTP POST Form to URI.
Type |
Name |
Description |
string |
URI |
The URI (aka URL) to which the form and it's data should be posted to. |
Dictionary<string,string> |
formStringData |
The form's data to be sent in the POST. |
// Example Data
var formData = new Dictionary<string, string>();
formData.Add ("username", "batman");
formData.Add("password", "robin");
// Send it on it's way!
hWebPool.Instance.Form("http://api.unityforge.net/", formData);
Nothing
HTTP POST Form to URI.
// Example Data
var _response;
// Let's just Unity's own legacy GUI system to display these things quickly.
public void OnGUI()
{
if ( GUI.Button(new Rect(10,10, 100, 30), "GO!") )
{
// Local Example Data
var formData = new Dictionary<string, string>();
formData.Add ("username", "batman");
formData.Add("password", "robin");
// Send it on it's way!
hWebPool.Instance.Form("http://api.unityforge.net/", formData, null, null, Finished);
}
// Show the response, always.
GUI.Label (new Rect (25, 90, 500, 500), _response);
}
// Our fancy Action (delegate) function that is called when the worker is finished.
// It is important that your Action function meet the same arguements as this one.
public void Finished (int hash, Hashtable responseHeaders, string responseText)
{
// Make sure that our hash is displayed for verification purposes.
_response = "CALL-HASH: " + hash + "\n\n";
// Let's also show the headers that came across in the response as they are very handy for
// figuring out if something is wrong. Look at the response code; your hoping for a 200 OK.
var headers = "HEADERS\n========\n\n";
foreach (var s in responseHeaders.Keys) {
headers += s + ": " + responseHeaders [s] + "\n\r";
}
// Append that header text to our response text
_response += headers;
// Add the actual response payload (if there is one)
_response += "\n\rRESPONSE-TEXT: \n\r==============\n\r" + responseText;
}
Nothing
HTTP GET Request to URI
Type |
Name |
Description |
string |
URI |
The URI (aka URL) which should be accessed. |
// Simply access the URI, handy for signalling an event via URL arguements
hWebPool.Instance.Get("http://api.unityforge.net/?honey=imhome", );
Nothing
HTTP GET Request to URI
// Example Data
var _response;
// Let's just Unity's own legacy GUI system to display these things quickly.
public void OnGUI()
{
if ( GUI.Button(new Rect(10,10, 100, 30), "GO!") )
{
// Let's access that URI and grab the response!
hWebPool.Instance.GET("http://api.unityforge.net/?honey=imhome", Finished);
}
// Show the response, always.
GUI.Label (new Rect (25, 90, 500, 500), _response);
}
// Our fancy Action (delegate) function that is called when the worker is finished.
// It is important that your Action function meet the same arguements as this one.
public void Finished (int hash, Hashtable responseHeaders, string responseText)
{
// Make sure that our hash is displayed for verification purposes.
_response = "CALL-HASH: " + hash + "\n\n";
// Let's also show the headers that came across in the response as they are very handy for
// figuring out if something is wrong. Look at the response code; your hoping for a 200 OK.
var headers = "HEADERS\n========\n\n";
foreach (var s in responseHeaders.Keys) {
headers += s + ": " + responseHeaders [s] + "\n\r";
}
// Append that header text to our response text
_response += headers;
// Add the actual response payload (if there is one)
_response += "\n\rRESPONSE-TEXT: \n\r==============\n\r" + responseText;
}
Nothing
HTTP GET Request to URI
// Example Data
var _response;
var _cookie = "name=value; name2=value2";
// Let's just Unity's own legacy GUI system to display these things quickly.
public void OnGUI ()
{
if (GUI.Button (new Rect(10,10, 100, 30), "GO!"))
{
// Let's access that URI and grab the response!
hWebPool.Instance.GET("http://api.unityforge.net/?honey=imhome", _cookie, Finished);
}
// Show the response, always.
GUI.Label (new Rect (25, 90, 500, 500), _response);
}
// Our fancy Action (delegate) function that is called when the worker is finished.
// It is important that your Action function meet the same arguements as this one.
public void Finished (int hash, Hashtable responseHeaders, string responseText)
{
// Make sure that our hash is displayed for verification purposes.
_response = "CALL-HASH: " + hash + "\n\n";
// Let's also show the headers that came across in the response as they are very handy for
// figuring out if something is wrong. Look at the response code; your hoping for a 200 OK.
var headers = "HEADERS\n========\n\n";
foreach (var s in responseHeaders.Keys) {
headers += s + ": " + responseHeaders [s] + "\n\r";
}
// Append that header text to our response text
_response += headers;
// Add the actual response payload (if there is one)
_response += "\n\rRESPONSE-TEXT: \n\r==============\n\r" + responseText;
}
Nothing
HTTP POST Request to URI.
Type |
Name |
Description |
string |
URI |
The URI (aka URL) which data should be posted too. |
string |
contentType |
The classification of the content (Example: "application/json"). |
string |
payload |
The data to be posted to the target URI. |
// Create new JSONObject for our payload
var jsonPayload = new Hydrogen.Serialization.JSONObject ();
// Initial setup of payload
jsonPayload.Fields.Add ("apikey", "398c1161326cef46dad0e0a340a38e6b-us7");
jsonPayload.Fields.Add ("id", "c0eaf9ec60");
// Lazy example for a sub JSONObject; you can create JSON objects from JSON.
jsonPayload.Fields.Add ("email", new Hydrogen.Serialization.JSONObject (
"{\"email\":\"" + "test@test.com" + "\"}"));
// Send POST to the WebPool
hWebPool.Instance.POST (
"https://us7.api.mailchimp.com/2.0/lists/subscribe.json",
"application/json",
jsonPayload.Serialized);
Nothing
HTTP POST Request to URI.
Type |
Name |
Description |
string |
URI |
The URI (aka URL) which data should be posted too. |
string |
contentType |
The classification of the content (Example: "application/json"). |
string |
payload |
The data to be posted to the target URI. |
string |
cookie |
Any previous cookie data to be used for authentication. |
Action<int, Hashtable, string> |
callback |
A callback function (int hash, Hashtable headers, string payload). |
// Example Data
var _response;
var _cookie = "name=value; name2=value2";
// Let's just Unity's own legacy GUI system to display these things quickly.
void OnGUI ()
{
if (GUI.Button (new Rect (25, 60, 90, 20), "Subscribe")) {
// Create new JSONObject
var jsonPayload = new Hydrogen.Serialization.JSONObject ();
// Initial setup of payload
jsonPayload.Fields.Add ("apikey", "398c1161326cef46dad0e0a340a38e6b-us7");
jsonPayload.Fields.Add ("id", "c0eaf9ec60");
// Lazy example for a sub JSONObject; you can create JSON objects from JSON.
jsonPayload.Fields.Add ("email", new Hydrogen.Serialization.JSONObject (
"{\"email\":\"" + "test@test.com" + "\"}"));
// Send POST to the WebPool
hWebPool.Instance.POST (
"https://us7.api.mailchimp.com/2.0/lists/subscribe.json",
"application/json",
jsonPayload.Serialized,
_cookie,
MailChimpCallback);
}
// Show Response
GUI.Label (new Rect (25, 90, 500, 500), _response);
}
// Our fancy Action (delegate) function that is called when the worker is finished.
// It is important that your Action function meet the same arguements as this one.
public void MailChimpCallback (int hash, Hashtable responseHeaders, string responseText)
{
// Make sure that our hash is displayed for verification purposes.
_response = "CALL-HASH: " + hash + "\n\n";
// Let's also show the headers that came across in the response as they are very handy for
// figuring out if something is wrong. Look at the response code; your hoping for a 200 OK.
var headers = "HEADERS\n========\n\n";
foreach (var s in responseHeaders.Keys) {
headers += s + ": " + responseHeaders [s] + "\n\r";
}
// Append that header text to our response text
_response += headers;
// Add the actual response payload (if there is one)
_response += "\n\rRESPONSE-TEXT: \n\r==============\n\r" + responseText;
}
Nothing
Public Structs
When dealing with binary data on a FORM submission, Hydrogen.Core.WebPool utilizes a handy struct to facilitate packaging of the corresponding data.
Type |
Name |
Description |
string |
FieldName |
The name of the form field. |
byte[] |
Data |
The byte data to populate that field with. |
string |
FileName |
The file name of the data that has been populated. |
string |
MimeType |
The internet media type of the data. |
An automatic installation paired with a robust implementation. What could be more awesome? OK fine, we will start to think about including a "Make Game" button.
- Components
-
Hydrogen.Plugins
-
hTestFlight.cs
Overview
TestFlight is a free platform used to distribute beta and internal iOS/Android applications to team members over-the-air. Developers can manage beta testing campaigns and receive feedback from their team with TestFlight's dashboard. With the TestFlight SDK implemented in an application, users can acquire more metrics on how and what testers are doing when testing an application on their device.
TestFlight Documentation
With that much awesomeness bundled into one package we felt the need to make sure that every Unity developer was able to easily integrate and deploy with TestFlight. Hydrogen's TestFlight component (and supporting scripts) work on the principle of handling as much of the grunt work as possible, allowing you to focus on more important tasks; like making an awesome game.
The hTestFlight
Singleton creates an instance of Hydrogen.Plugins.TestFlight which can be globally accessed via hTestFlight.Instance
. As always, this is merely one way of extending on the Hydrogen.Plugins.TestFlight class, as it is designed to be decoupled from everything, you can create your own implementation if you would like (and have the time!).
Installation
Preprocessor Directive
One of the great features about the implementation of TestFlight in Hydrogen is that even with platforms that TestFlight does not support, your code will still work unchanged and it won't include or reference anything that isn't going to be used. This nice treat requires that a preprocessor directive (HYDROGEN_TESTFLIGHT
) is used. The installation script automatically adds the directive to the Scripting Define Symbols found in Unity's Player Settings corresponding platform. If you are having problems with getting TestFlight to work, please make sure to double check that your desired platform has the directive added to it's Scripting Define Symbols.
iOS Checklist
Android Checklist
Usage
Now that you have gotten all the installation done, it's time to start rocking away with the actual component! For the sake of documentation it is assumed that you are using the provided hTestFlight component. If you have implemented your own version of the Hydrogen.Plugins.TestFlight class its safe to say that you may not really need to look over these next few sections.
Public Fields
Type |
Name |
Description |
bool |
Persistent |
Should the hTestFlight instance (and it's GameObject) survive scene switches by using GameObject.DontDestroyOnLoad. This will preserve all scripts on its GameObject as well, so please be aware of that behaviour. |
string |
TokenIOS |
The generated App Token for the iOS App registered with TestFlight. You can find this token mentioned in the above iOS Checklist Step 3. |
string |
TokenAndroid |
The generated App Token for the Android App registered with TestFlight. You can find this token mentioned in the above Android Checklist Step 3. |
Properties
Type |
Access |
Name |
Description |
hTestFlight |
get |
Instance |
Returns the static hTestFlight instance (Thread Safe Singleton). |
Public Methods
Does a
hTestFlight
instance exist?
None
// Let's see if we are using the hTestFlight component.
if (hTestFlight.Exists ()) {
// Yes - We are that awesome.
Debug.Log ("hTestFlight Already Exists");
}
Type |
Value |
Description |
bool |
true |
An instance of hTestFlight already exists. This can be handy to simply check from other classes if hTestFlight is being used, for piping debug output to TestFlight for example. |
bool |
false |
An instance of hTestFlight does not exist, and therefore must be created or instantiated via a prefab. |
Adds an entry into the Key-Value store for this TestFlight session.
Type |
Name |
Description |
string |
key |
The Key |
string |
data |
The Data |
// Example Data
string userName = "Batman";
// Somehow we've managed to detected that Batman is our user.
if (userName == "Batman") {
// We need to report this ... RIGHT NOW!
hTestFlight.Instance.AddCustomEnvironmentInformation ("User", userName);
}
Nothing
Send a message to TestFlight to appear in it's console.
Type |
Name |
Description |
string |
message |
The message to be sent to and logged by TestFlight. |
// Example Data
int batRepellent;
// Does Batman have repellent?
if (batRepellent <= 0) {
hTestFlight.Instance.Log ("Error! Batman Doesn't Have A Repellent For This!");
}
Nothing
void LogAsync (string message) iOS ONLY
Send a message to TestFlight asynchronously to appear in it's console.
Type |
Name |
Description |
string |
message |
The message to be sent to and logged by TestFlight. |
// Example Data
bool moreBatRepellent;
// Does Batman have more repellent?
if (!moreBatRepellent) {
hTestFlight.Instance.LogAsync ("Error! Batman Doesn't Have More Repellent!");
}
Nothing
void PassCheckpoint (string checkpoint)
Report to TestFlight that the session has passed a Checkpoint.
Type |
Name |
Description |
string |
checkpoint |
This is the checkpoint that should be reported to TestFlight. This can be any string that you want, but it is recommended that you stick to meaningful descriptive entries. |
// Example Data
int buttonCount;
bool buttonPressed;
// Loop for no reason whatsoever
while (buttonCount < 100) {
// Magic!
buttonPressed = true;
// Did Batman press the button?
if (buttonPressed) {
buttonCount++;
buttonPressed = false;
}
}
// Report that Batman has wasted time! and lots of it.
hTestFlight.Instance.PassChcckpoint ("Batman Pressed The Button 100 Times");
Nothing
void SubmitFeedback (string message) iOS ONLY
Submits a feedback message for the App to TestFlight.
Type |
Name |
Description |
string |
message |
User provided feedback on the current App, or your own if you want to have some fun. |
// Example Data
string userFeedback = "I loved your app! It's AWESOME! SHUT UP AND TAKE MY MONEY!";
// Do we have any feedback?
if (!string.IsNullOrEmpty (userFeedback)) {
hTestFlight.Instance.SubmitFeedback (userFeedback);
}
Nothing