Menu

FreeCoTaskMem(dataPtrArray[i]); } Marshal

Search:.
Games.
Unity scripting environment runs C# Mono container which supports native C/C++ plugins with.
This allows easy integration of native library functions to both pass and receive data between Unity managed C# and native platform code.
extern “C” { #define PLUGINEX(rtype) UNITY_INTERFACE_EXPORT rtype UNITY_INTERFACE_API PLUGINEX(int) ReturnInt() { return 0xBABE; } PLUGINEX(void) AcceptArray1(char *arr, int length) { for (int i = 0; i < length; i++) { arr[i] = ''A'' + i; } } } [DllImport("ptest")] private static extern int ReturnInt(); [DllImport("ptest")] private static extern void AcceptArray1([In, Out] byte[] arr, int length); void TestIntegral() { // return int print(ReturnInt()); // accept byte array, uses marshaling to pass array back and forth byte[] arr1 = { 0, 0, 0 }; AcceptArray1(arr1, arr1. Length); for (int i = 0; i < arr1. Length; i++) { print("arr" + i + "=" + arr1[i]); } } extern "C" { #define PLUGINEX(rtype) UNITY_INTERFACE_EXPORT rtype UNITY_INTERFACE_API PLUGINEX(bool) AcceptStr(LPCSTR pStr) { return !strcmp(pStr, "FOO"); } PLUGINEX(LPSTR) ReturnDynamicStr() { LPSTR str = (LPSTR)CoTaskMemAlloc(512); strcpy_s(str, 512, "Dynamic string"); return str; } PLUGINEX(LPCSTR) ReturnConstStr() { return "Constant string"; } } [DllImport("ptest")] private static extern bool AcceptStr([MarshalAs(UnmanagedType. LPStr)] string ansiStr); // automatically deallocates the return string with CoTaskMemFree [DllImport("ptest")] [return: MarshalAs(UnmanagedType. LPStr)] private static extern string ReturnDynamicStr(); [DllImport("ptest")] private static extern IntPtr ReturnConstStr(); void TestStrings() { // accept string bool r1 = AcceptStr("BAR"); bool r2 = AcceptStr("FOO"); print("r1=" + r1); // r1=false print("r2=" + r2); // r1=true // return dynamically allocated string string s1 = ReturnDynamicStr(); print("s1=" + s1); // return constant string string s2 = Marshal. PtrToStringAnsi(ReturnConstStr()); print("s2=" + s2); } extern "C" { #define PLUGINEX(rtype) UNITY_INTERFACE_EXPORT rtype UNITY_INTERFACE_API PLUGINEX(void) AcceptArray1(char *arr, int length) { for (int i = 0; i < length; i++) { arr[i] = ''A'' + i; } } PLUGINEX(void) AcceptArray2(char *arr, int length) { for (int i = 0; i < length; i++) { arr[i] = ''A'' + i; } } PLUGINEX(int) AcceptStrArray(const char* const *strArray, int size) { int total = 0; for (int i = 0; i < size; i++) { auto str = strArray[i]; total += (int)strlen(str); } // return total length of the strings in the array to demonstrate that // it was passed correctly return total; } PLUGINEX(LPBYTE) ReturnDynamicByteArray(int &pSize) { pSize = 0xFF; LPBYTE pData = (LPBYTE)CoTaskMemAlloc(pSize); // fill with example data for (int i = 0; i < pSize; i++) { pData[i] = i + 1; } return pData; } PLUGINEX(LPSTR*) ReturnDynamicStrArray(int &pSize) { // Allocate an array with pointers to 3 dynamically allocated strings pSize = 3; LPSTR* pData = (LPSTR*)CoTaskMemAlloc((pSize)*sizeof(LPSTR)); pData[0] = (LPSTR)CoTaskMemAlloc(128); pData[1] = (LPSTR)CoTaskMemAlloc(128); pData[2] = (LPSTR)CoTaskMemAlloc(128); strcpy_s(pData[0], 128, "String 1"); strcpy_s(pData[1], 128, "String 2"); strcpy_s(pData[2], 128, "String 3"); return pData; } } [DllImport("ptest")] private static extern void AcceptArray1(IntPtr arr, int length); [DllImport("ptest")] private static extern void AcceptArray2(IntPtr arr, int length); [DllImport("ptest")] private static extern int AcceptStrArray(IntPtr array, int size); [DllImport("ptest")] private static extern IntPtr ReturnDynamicByteArray(ref int size); [DllImport("ptest")] private static extern IntPtr ReturnDynamicStrArray(ref int size); ///// Helper functions for marshalling ///// // Convert and copy a. Length]; for (int i = 0; i < strArr.

Length; i++) { dataArr[i] = Marshal

StringToCoTaskMemAnsi(strArr[i]); } IntPtr dataNative = Marshal

AllocCoTaskMem(Marshal.
SizeOf(typeof(IntPtr)) * strArr.
Length); Marshal.
Copy(dataArr, 0, dataNative, dataArr.
Length); return dataNative; } // Decodes string array from raw pointer private static string[] MarshalStringArray(IntPtr dataPtr, int arraySize) { var dataPtrArray = new IntPtr[arraySize]; var strArray = new String[arraySize]; Marshal.
Copy(dataPtr, dataPtrArray, 0, .

ArraySize); for (int i = 0; i < arraySize; i++) { strArray[i] = Marshal

PtrToStringAnsi(dataPtrArray[i]); Marshal

FreeCoTaskMem(dataPtrArray[i]); } Marshal.
FreeCoTaskMem(dataPtr); return strArray; } // Dellocates encoded string array private static void CleanUpNativeStrArray(IntPtr dataPtr, .

Int arraySize) { var dataPtrArray = new IntPtr[arraySize]; Marshal

Copy(dataPtr, dataPtrArray, 0, .

ArraySize); for (int i = 0; i < arraySize; i++) { Marshal

FreeCoTaskMem(dataPtrArray[i]); } Marshal.
FreeCoTaskMem(dataPtr); } void TestArrays() { // accept byte array, uses marshalling to pass array back and forth byte[] arr1 = { 0, 0, 0 }; AcceptArray1(arr1, arr1.
Length); for (int i = 0; i < arr1. Length; i++) { print("arr" + i + "=" + arr1[i]); } // accept byte array, passes no-copy raw memory pointer byte[] arr2 = { 0, 0, 0 }; GCHandle h = GCHandle. Alloc(arr2, GCHandleType. Pinned); AcceptArray2(h. AddrOfPinnedObject(), arr2. Length); for (int i = 0; i < arr2. Length; i++) { print("arr" + i + "=" + arr2[i]); } h. Free(); // return dynamically allocated byte array int arraySize = 0; IntPtr dataPtr = ReturnDynamicByteArray(ref arraySize); byte[] data = new byte[arraySize]; Marshal. Copy(dataPtr, data, 0, arraySize); Marshal. FreeCoTaskMem(dataPtr); // deallocate unmanaged memory print("data["+arraySize+"] = [" + data[0] + ", " + data[1] + ", " + data[2] + ",. ]"); // return dynamically allocated string array arraySize = 0; dataPtr = ReturnDynamicStrArray(ref arraySize); String[] strArray = MarshalStringArray(dataPtr, arraySize); print("strArray["+arraySize+"] = [" + String. Join(",", strArray) + "]"); // string array as parameter dataPtr = MarshalStringArray(new String[] { "foo1", "foo2", "foo3" }); int len = AcceptStrArray(dataPtr, arraySize); print("len=" + len); CleanUpNativeStrArray(dataPtr, arraySize); } extern "C" { #define PLUGINEX(rtype) UNITY_INTERFACE_EXPORT rtype UNITY_INTERFACE_API struct ExampleStruct { INT16 val1; INT32 array1[3]; INT16 array2len; INT32 array2[10]; LPSTR str1; }; PLUGINEX(int) AcceptStruct(ExampleStruct &s) { // Modify struct s.val1 -= 1111; for (int i= 0; i < 3; i++) { s.array1[i] += 1; } for (int i = 0; i < s.array2len; i++) { s.array2[i] += 10; } // return length of the string in the argument struct to demonstrate that // it was passed correctly return (int)strlen(s.str1); } struct ExamplePoint { FLOAT x; FLOAT y; FLOAT z; }; PLUGINEX(ExamplePoint *) ReturnArrayOfPoints(int &size) { size = 4; ExamplePoint *pointArr = (ExamplePoint*)CoTaskMemAlloc(sizeof(ExamplePoint) * size); // fill with some example data for (int i = 0; i < size; i++) { pointArr[i] = { i + 0.1f, i + 0.2f, i + 0.3f }; } return pointArr; } // this return type is blittable // https://stackoverflow.com/questions/10320502/c-sharp-calling-c-function-that-returns-struct-with-fixed-size-char-array // PLUGINEX(ExamplePoint) ReturnStruct() { return { 1, 2, 3 }; } } [StructLayout(LayoutKind. Sequential)] public struct ExamplePoint { public float x; public float y; public float z; // for debugging public override String ToString() { return "{" + x + ","+ y + "," + z + "}"; } } [DllImport("ptest")] private static extern IntPtr ReturnArrayOfPoints(ref int size); [StructLayout(LayoutKind. Sequential, CharSet = CharSet. Ansi)] public struct ExampleStruct { public UInt16 val1; [MarshalAsAttribute(UnmanagedType. ByValArray, SizeConst = 3)] public UInt32[] array1; public UInt16 array2len; [MarshalAsAttribute(UnmanagedType. ByValArray, .

SizeConst = 10)] public UInt32[] array2; [MarshalAs(UnmanagedType

LPStr)] public string str1; } [DllImport(“ptest”)] private static extern int AcceptStruct(ref ExampleStruct s); [DllImport(“ptest”)] private static extern ExamplePoint ReturnStruct(); void TestStructures() { // Structure as parameter ExampleStruct s = new ExampleStruct { val1 = 9999, array1 = new UInt32[3], array2 = new UInt32[10] }; s.array1[0] = 1; s.array1[1] = 2; s.array1[2] = 3; s.array2len = 5; s.array2[0] = 10; s.array2[1] = 11; s.array2[2] = 12; s.array2[3] = 13; s.array2[4] = 14; s.str1 = “Cat is a feline”; len = AcceptStruct(ref s); print(“s.val1=” + s.val1 + ” len=” + len); // return struct ExamplePoint p = ReturnStruct(); // Marshal array of point objects arraySize = 0; dataPtr = ReturnArrayOfPoints(ref arraySize); ExamplePoint[] pointArr = new ExamplePoint[arraySize]; // memory layout // |float|float|float|float|float|float|float|float|float|float.
// | ExamplePoint0 | ExamplePoint1 | ExamplePoint2 | int offset = 0; int pointSize = Marshal.
SizeOf(typeof(ExamplePoint)); for(int i=0; i < arraySize; i++) { pointArr[i] = (ExamplePoint)Marshal. PtrToStructure(new IntPtr(dataPtr. ToInt32() + offset), typeof(ExamplePoint)); offset += pointSize; } print("pointArr["+arraySize+"]=["+pointArr[0]+", "+pointArr[1]+",. ]"); Marshal. FreeCoTaskMem(dataPtr); } Many of these examples can be done in cleaner way using. Code above does show what is happening under the hood. Get full implementation ftom Filed under , , Tagged with , , ,. First, get the SQLite library by from Github: $ sqlite3 Assets/StreamingAssets/default.db SQLite version 3.8.8.3 2015-02-25 13:29:11 Enter ".help" for usage hints. sqlite> create table example (.
> name string,.
> dummy int.
> ); sqlite> .schema example CREATE TABLE example ( name string, dummy int ); sqlite> insert into example values (“hello world”, 1); sqlite> select * from example; hello world|1 sqlite>.quit $ ./Assets/Plugins/Android/libsqlite3.so ./Assets/Scripts/SQLiteUnityKit/DataTable.cs ./Assets/Scripts/SQLiteUnityKit/SqliteDatabase.cs ./Assets/StreamingAssets/default.db SqliteDatabase sqlDB; void Awake() { string dbPath = System.
IO.
Path.
Combine (Application.persistentDataPath, “game.db”); var dbTemplatePath = System.
IO.
Path.
Combine(Application.streamingAssetsPath, “default.db”); if (!System.
IO.
File.
Exists(dbPath)) { // game database does not exists, copy default db as template if (Application.platform == RuntimePlatform.
Android) { // Must use WWW for streaming asset WWW reader = new WWW(dbTemplatePath); while ( !reader.isDone) {} System.
IO.
File.
WriteAllBytes(dbPath, reader.bytes); } else { System.
IO.
File.
Copy(dbTemplatePath, dbPath, true); } } sqlDB = new SqliteDatabase(dbPath); } You can use the to ensure that this code is always executed first.
var result = sqlDB.
ExecuteQuery(“SELECT * FROM example”); var row = result.
Rows[0]; print(“name=” + (string)row[“name”]); print(“dummy=” + (int)row[“dummy”]); API is simple to use, check detailed documentation from the.
Filed under , Tagged with ,.
First part is defining function that looks up rectangle dimensions.
This function starts from a tile that will be left top corner of rectangle and returns its width and height.
The listings here are in.
FUNC get_rectangle_size( tile, tiles ) BEGIN # find rectangle width width = 1 WHILE contains( (tile.x + width, c.y, tiles ) width = width + 1 # find rectangle height height = 0 stop = false REPEAT height = height + 1 LOOP FROM x = tile.x TO tile.x + width IF NOT contains( (x, tile.y + height), tiles ) THEN stop = true BREAK ELSE x = x + 1 UNTIL stop RETURN (width, height) END FUNC find_tiles( tiles ) # the list of tiles as (x,y) tuples BEGIN rectangles = [] # list of found rectangles # sort with compare order by y and x.
sort(tiles, (a, b) => a.y == b.y.
a.x – b.x : a.y – b.y) WHILE length(tiles) > 0 c = head(tiles) # take first item from the sorted list # get rectangle size (width, height) = get_rectangle_size( c, tiles ) # remove tiles that can not be part of any larger rectangle LOOP FROM x = c.x TO c.x + width LOOP FROM y = c.y TO c.y + height IF NOT contains( (c.x – 1, y, tiles) AND NOT contains( (c.x + width, y), tiles) AND NOT contains( (x, c.y – 1), tiles) AND NOT contains( (x, c.y + height), tiles) THEN remove( (x, y), tiles ) # if big enough rectangle add it to list IF width > 1 AND height > 1 THEN add ( (c.x, c.y, width, height), rectangles ) # sort rectangles by area size sort( rectangles, (r1, r2) => r2.width * r2.height – r1.width * r1.height ) # remove overlapping rectangles i = 0 WHILE i < length(rectangles) - 1 LOOP FROM j = length(rectangles) - 1 TO i + 1 # descending order IF overlaps(rectangles[i], rectangles[j]) THEN remove(rectangles[j], rectangles) i = i + 1 RETURN rectangles END Here is example how the algorithm works, here white area presents the working set of tiles that algorithm works on, white presents the tiles that are in list tiles. Algorithm sorts first the tiles in top-to-bottom and left-to-right order so it starts from top left tile. The pink denotes the current working tile c. The green represents the tiles that belong to rectangle as determined by call to get_rectangle_size. Next step is to remove tiles from working set that can not be part of any other rectangle. This is determined by checking if the row and column of tile are bounded inside the current rectangle. The purple presents tiles that were removed from the working set tiles. Found rectangle is added in the list rectangles if it’s large enough (in this case larger than 2×2). Then loop is executed again with next corner tile c. Next iteration of the main loop returns another rectangle and closed tiles are removed and rectangle added to list. Final loop of the rectangle. There are no more tiles to work so main loop stops. Filed under ,. Games need to store some persistent data like high scores and progress between the game sessions. Fortunately gives us class that is essentially a persistent hash map. // saving data PlayerPrefs. SetInt("foobar", 10); PlayerPrefs. SetString("something", "foo"); PlayerPrefs. Save();. // reading data if(PlayerPrefs. HasKey("foobar")) { int foo = PlayerPrefs. GetInt("foobar"); } using System; using System. IO; using System. Runtime. Serialization; using System. Runtime. Serialization. Formatters. Binary; public static class SerializerUtil { static BinaryFormatter bf = new BinaryFormatter (); public static T LoadObject(string key) { if (!PlayerPrefs.
HasKey(key)) return default(T); try { string tmp = PlayerPrefs.
GetString(key); MemoryStream dataStream = new MemoryStream(Convert.
FromBase64String(tmp)); return (T)bf.
Deserialize(dataStream); } catch (Exception e) { Debug.
Log(“Failed to read “+ key+ ” err:” + e.
Message); return default(T); } } public static void SaveObject(string key, T dataObject) { MemoryStream memoryStream = new MemoryStream (); bf.
Serialize (memoryStream, dataObject); string tmp = Convert.
ToBase64String (memoryStream.
ToArray ()); PlayerPrefs.
SetString ( key, tmp); } } [Serializable] public class PlayerData { public int points; public string name; } var data = new PlayerData(); data.points = 50; data.name = “Teemu”; SerializerUtil.
SaveObject(“player1”, data); PlayerData data; data = SerializerUtil.
LoadObject(“player1”); // data.points is 50 // data.name is “Teemu” [Serializable] public class PlayerData : IDeserializationCallback { public int points; public string name; // serialization ignores this member variable [NonSerialized] Dictionary progress = new Dictionary(); // constructor is called only on new instances public PlayerData() { points = 0; name = “Anon”; } // serializable class can have member methods as usual public bool ValidName() { return name.
Trim().
Length > 4; } // Called only on deserialized classes void IDeserializationCallback.
OnDeserialization(System.
Object sender) { // do your init stuff here progress = new Dictionary(); } } void Awake() { Environment.
SetEnvironmentVariable(“MONO_REFLECTION_SERIALIZER”, “yes”);.
Filed under ,.
is a collection of images, all composed in single large image.
The game or HTML5 app can load this single image instead of wasting time for requesting every small image separately.
I wrote tool that can be used to package collection of images to single atlas and let web app use that conveniently both as or in HTML5.
Tool itself is script that uses commands to read and write images.
The output is the atlas image, CSS, raw JSON and Javascript import file.
CSS file defines CSS sprite for each image.
JSON is raw data of each images position in the atlas, Javascript import file can be included on HTML page and it loads the atlas position info into global variable.
Get script from here: $ chmod +x packer.py ~/work/blog/packer $ ./packer.py -h usage: packer.py [-h] [-o OUTFILE] [-jo JSONOUTFILE] [-jso JSOUTFILE] [-co CSSOUTFILE] [-p PAD] [-mw WIDTH] [-mh HEIGHT] FILE [FILE.
] Packs images to atlas.
Uses ImageMagick to parse and compose the images positional arguments: FILE Image file optional arguments: -h, –help show this help message and exit -o OUTFILE Output atlas file -jo JSONOUTFILE Output atlas json file -jso JSOUTFILE Output atlas import js file -co CSSOUTFILE Output atlas css file -p PAD Padding -mw WIDTH Maximum width -mh HEIGHT Maximum height Install ImageMagick easily on OS/X with or Homebrew.
$ sudo port install ImageMagick $ identify –version Version: ImageMagick 6.8.7-3 2013-10-28 Q16 http://www.imagemagick.org Copyright: Copyright (C) 1999-2013 ImageMagick Studio LLC Features: DPC Delegates: bzlib djvu fftw fontconfig freetype gslib jng jpeg lcms ltdl lzma png ps png tiff webp x xml zlib $ ./packer.py img/*.png -o sprites.png ~/work/blog/packer $ ./packer.py example/pics/* -o example/html/sprites.png -mw 512 Checking ImageMagick Found: Version: ImageMagick 6.8.7-3 2013-10-28 Q16 http://www.imagemagick.org =========================== Resolving file dimensions button_minus.png -> 53×43 button_plus.png -> 53×42 cannon_marker.png -> 43×28 pause.png -> 53×42 status_bar.png -> 496×74 =========================== fitting 5 images, padding 1 successfully fitted 5 images to 496×118 padding 1 Wrote: atlas to example/html/sprites.png Wrote json to example/html/sprites.json Wrote js to example/html/sprites.json.js Wrote css to example/html/sprites.css This atlas can be used now in two different ways on the web page.
Example

Example

Cannon – simple sprite with image

Status bar – sprite with text inside

Here is some text

Buttons – list

This renders following page: See example page here: Example

Canvas Example

This is the part where things get interesting, offline is tricky to test because of caching and browser reload logic.
See detailed lamentation about subject here in.
Second, if you develop the game from local server, do not use as host, but use real domain name that resolves to localhost.
In this example I’ve used that supports wildcard subdomain.
Any subdomain resolves to address 127.0.0.1.
$ nslookup anything-goes-here.hexxie.com Server: 192.168.1.254 Address: 192.168.1.254#53 Non-authoritative answer: Name: anything-goes-here.hexxie.com Address: 127.0.0.1 Note that at least Firefox asks each time if you allow offline content.
Code is available in.
Filed under , Tagged with , , ,.
See also In previous and I implemented simple slots machine purely in HTML5 with audio support.
After adding audio, the initial loading time increased a lot up to several seconds.
This slowdown is easy to miss during development, as developer has always primed browser cache and content is loaded from development server that is hosted locally or even in developers machine.
It’s very important to occasionally clear the browser cache and put the game to remote server and reload the game to get realistic estimation of new users loading time (i.e.
the empty cache experience).
Try out version with loading bar.

#progressbar { margin-top: 10px; background: black; border: 1px solid mediumaquamarine; width: 80%; height: 15px; margin-left: auto; margin-right: auto; } #progressbar #progress { height: 100%; position: relative; width: 0%; left: 0; background: #77e0fb; } #progressbar { background-image:url(data:image/png;base64,iAE28w0KGgoABBANSAxhEegBB54DIA.
);.
} .

.

var progressCount = 0; // current progress count var progressTotalCount = 0; // total count function updateProgress( inc ) { progressCount += (inc || 1); if ( progressCount >= progressTotalCount ) { // done, complete progress bar and hide loading screen $(''#progress'').css(''width'', ''100%''); $(''#loading'').slideUp(600); } else { // Update progress bar $(''#progress'').css(''width'', parseInt( 100 * progressCount / progressTotalCount) + ''%''); } } // Generic preloader handler, it calls preloadFunction for each item and // passes function to it that it must call when done.
function preloader( items, preloadFunction, callback ) { var itemc = items.length; var loadc = 0; // called by preloadFunction to notify result function _check( err, id ) { updateProgress(1); if ( err ) { alert(''Failed to load '' + id + '': '' + err); } loadc++; if ( itemc == loadc ) callback(); } progressTotalCount += items.length; // queue each item for fetching items.forEach(function(item) { preloadFunction( item, _check); }); } Merge and minify the javascript e.g.
with.
Merge and minify the CSS with.
Minify PNG image files with.
Use JPG where possible.
Convert audio files to mono to save half of the size.
Use e.g.
or.
Host asset files in CDN, such as Akamai or Amazon S3.
Note that if assets are loaded from different domain than the HTML document, you may encounter security problems if.
Use get idea what takes most time.
Code is available in.
Continue to Filed under , Tagged with , .

Simple Slot machine game using HTML5 Part 2: Audio

April 27, 2013 9 Comments See also Simple Slot machine game using HTML5 Part 3: Loading.
In part 1 I presented simple slots machine purely in HTML5.
This part extends the basic implementation with audio support.
The game itself is simple slot machine that has only one winning line and we add effects for roll start, reel stop and win/loss.
Try audio enabled game here.
Note that loading time is significantly longer in audio enabled version.
Debug text under button tells what audio system game uses for your browser.
Original non audio version from part 1 is here.
How to support Audio.
Web game can implement audio in 3 main ways.
1.
Flash player audio.
(e.g.
use SoundManager2 library) 2.

HTML5 Audio (Buzz is easy way to use it) 3

Web Audio API.
(See HTML5 Rocks for tutorial) Flash audio is pretty much deprecated now, as only older browsers will still need it that most of don’t support HTML5 canvas anyway.

HTML5 Audio works very well for desktop browsers

but has only nominal support for mobile (Android & iOS).
It’s generally too moody for tablets or mobile.
Web Audio API is supported only by latest browsers, but it works reliably e.g.
in Safari iOS 6.0.
Web Audio API has two implementations in wild, the WebKit (iOS, Safari, Chrome) and the Standard (latest Firefox).
Fortunately differences are small.
The libraries listed above simplify implementation a lot, but it’s easier to understand how these technologies work with simple examples.
So I implemented both methods 2 and 3 from scratch.
Some caveats with audio.
Game initial loading time will increase.
Audio files can be pretty large and they must be usually preloaded so they can be played on game start.
iOS (iPad/iPhone) does not allow autoplay for audio.
Audio must be enabled by playing some sound in click event handler.
Implementation.
Initialization function accepts array of objects that have required audio file name in id property and callback that is called after audio has been initialized and loaded.
First code checks if mp3 or ogg is supported.
Firefox requires .ogg and it’s easy to convert at least in OS/X or Linux with ffmpeg.
Exact command line depends little on ffmpeg version.
$ ffmpeg -i win.mp3 -strict experimental -acodec vorbis -ac 2 win.ogg When format is known, .

The code checks wether to use Web Audio API or normal HTML5 Audio

function initAudio( audios, callback ) { var format = ''mp3''; var elem = document.createElement(''audio''); if ( elem ) { // Check if we can play mp3, if not then fall back to ogg if( !elem.canPlayType( ''audio/mpeg;'' ) && elem.canPlayType(''audio/ogg;'')) format = ''ogg''; } var AudioContext = window.webkitAudioContext || window.mozAudioContext || window.
MSAudioContext || window.
AudioContext; if ( AudioContext ) { // Browser supports webaudio return _initWebAudio( AudioContext, format, audios, callback ); } else if ( elem ) { // HTML5 Audio return _initHTML5Audio(format, audios, callback); } else { // audio not supported audios.forEach(function(item) { item.play = function() {}; // dummy play }); callback(); } } Both initialization functions attempt to load the desired format of needed audio files and sets play function in objects that is used to play the effect.
If audio initialization fails, this play function is set to dummy empty function.
HTML5 Audio initialization function creates Audio objects and sets src to point to the audio file.
Downloading is handled automagically by the browser.

Function _initHTML5Audio( format

audios, callback ) { function _preload( asset ) { asset.audio = new Audio( ''audio/'' + asset.id + ''.'' + format); asset.audio.preload = ''auto''; asset.audio.addEventListener("loadeddata", function() { // Loaded ok, set play function in object and set default volume asset.play = function() { asset.audio.play(); }; asset.audio.volume = 0.6; }, false); asset.audio.addEventListener("error", function(err) { // Failed to load, set dummy play function asset.play = function() {}; // dummy }, false); } audios.forEach(function(asset) { _preload( asset ); });.
Web Audio initialization is bit more complicated, it needs to download the audio with XHR requests.
function _initWebAudio( AudioContext, format, audios, callback ) { var context = new AudioContext(); function _preload( asset ) { var request = new XMLHttpRequest(); request.open(''GET'', ''audio/'' + asset.id + ''.'' + format, true); request.responseType = ''arraybuffer''; request.onload = function() { context.decodeAudioData(request.response, function(buffer) { asset.play = function() { var source = context.createBufferSource(); // creates a sound source source.buffer = buffer; // tell the source which sound to play source.connect(context.destination); // connect the source to the context''s destination (the speakers) // support both webkitAudioContext or standard AudioContext source.noteOn.
source.noteOn(0) : source.start(0); }; // default volume // support both webkitAudioContext or standard AudioContext asset.gain = context.createGain.
context.createGain() : context.createGainNode(); asset.gain.connect(context.destination); asset.gain.gain.value = 0.5; }, function(err) { asset.play = function() {}; }); }; request.onerror = function(err) { asset.play = function() {}; }; request.send(); } audios.forEach(function(asset) { _preload( asset ); }); } NOTE: Chrome supports XMLHttpRequest only when loading pages over HTTP.
If you load the HTML file locally you’ll see erros like this in the error console: XMLHttpRequest cannot load file:///Users/teemuikonen/work/blog/slot2/audio/roll.mp3.
Cross origin requests are only supported for HTTP.
After audio has initialized, the game can play any effect simply by calling play function for the effect.
If audio initialization or loading failed, the play is simply a dummy function.
$(''#play'').click(function(e) { // start game on play button click $(''h1'').text(''Rolling!''); game.audios[0].play(); // Play start audio.
Code is available in Github.
Continue to Simple Slot machine game using HTML5 Part 3: Loading.
Filed under , Tagged with audio,.
In my two previous block entries I wrote about one possible ways to do simple and games on HTML5 technologies.
This writeup combines some of those methods and introduces new ones to implement Minesweeper game with a twist.
Try out the game here: this.slab = document.createElement(''canvas''); var ctx = this.slab.getContext(''2d''); this.slab.width = this.resolution; this.slab.height = this.resolution; ctx.fillStyle = ''grey''; ctx.fillRect(0, 0, this.resolution, this.resolution); ctx.beginPath(); ctx.fillStyle = ''white'' ctx.moveTo(0, 0); ctx.lineTo(this.resolution, 0); ctx.lineTo(this.resolution, this.resolution); ctx.lineTo(0, 0); ctx.closePath(); ctx.fill(); ctx.fillStyle = ''lightgrey''; ctx.fillRect(4, 4, this.resolution-8, this.resolution-8); var width = window.innerWidth; var height = window.innerHeight; GRID_W = Math.min( 12, ~~(width / GRID_RESOLUTION)); GRID_H = Math.min( 12, ~~(height / GRID_RESOLUTION)) - 1; var APPLE_COUNT = ~~((GRID_W * GRID_H) / 8); $(''#container'').click( function( e ){ var p = $(''#canvas'').offset(); game.click = { x:parseInt((e.pageX-p.left)/game.resolution), y:parseInt((e.pageY-p.top)/game.resolution)}; } ); Game.prototype.pos = function( x, y ) { return y*this.width+x; } Game.prototype.xy = function( pos ) { return { x:parseInt(pos%this.width), y:parseInt(pos/this.width) } } var SLAB_MASK = Math.pow(2, 16); var APPLE_MASK = Math.pow(2, 15); // grid location (5, 6) has slab and number 3 var pos = this.pos(5, 6); this.grid[pos] = 3; this.grid[pos] |= SLAB_MASK; this.grid[pos] &= ~SLAB_MASK; for ( var y=0; y < this.height; y++ ) { for ( var x=0; x < this.width; x++ ) { // Draw each tile var s = this.pos(x,y); if (s & SLAB_MASK) { // Still covered tile this.ctx.drawImage( this.slab, x * this.resolution, y * this.resolution ) } else if (s & APPLE_MASK) { // Uncovered apple this.ctx.drawImage( tile, x * this.resolution + 2, y * this.resolution + 2 ) } else if (s > 0) { // Neighbour number this.ctx.fillText( '' + s , x * this.reso.
Filed under , , Older posts Select category Algorithms c++ Experimental Games html5 Javascript Uncategorized Unity.
Post to.

Tags: , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *