Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

4. June 2012

 

This tutorial we are going to look at SpriteList and show how it can result in a massive increase in performance.  Along the way, we are going to create two custom Scene classes, one that performs poorly, and one the does not.  Along the way we are going to demonstrate dynamically generating a UI and how UI scenes and GameEngine2D scenes can co-exist.

 

A lot of the following code we have covered in previous tutorials, so I am not going to go into a great deal of detail.

 

 

First lets take a look at our AppMain.cs

 

using System; using System.Collections.Generic; using Sce.Pss.Core; using Sce.Pss.Core.Environment; using Sce.Pss.Core.Graphics; using Sce.Pss.Core.Input; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; using Sce.Pss.HighLevel.UI; namespace SpriteList { public class AppMain { static private bool quit = false; public static void Main(string[] args) { Director.Initialize(); Director.Instance.RunWithScene(new Sce.Pss.HighLevel.GameEngine2D.Scene(),true); UISystem.Initialize(Director.Instance.GL.Context); Sce.Pss.HighLevel.UI.Panel dialog = new Panel(); dialog.Width = Director.Instance.GL.Context.GetViewport().Width; dialog.Height = Director.Instance.GL.Context.GetViewport().Height; Button buttonUI1 = new Button(); buttonUI1.Name = "buttonGoSlow"; buttonUI1.Text = "Slow version"; buttonUI1.Width = 300; buttonUI1.Height = 50; buttonUI1.Alpha = 0.8f; buttonUI1.TouchEventReceived += (sender, e) => { Director.Instance.ReplaceScene(new SlowVersion()); }; Button buttonUI2 = new Button(); buttonUI2.Name = "buttonGoFase"; buttonUI2.Text = "Fast version"; buttonUI2.Width = 300; buttonUI2.Height = 50; buttonUI2.SetPosition(0,55); buttonUI2.Alpha = 0.8f; buttonUI2.TouchEventReceived += (sender, e) => { Director.Instance.ReplaceScene(new FastVersion()); }; Button buttonUI3 = new Button(); buttonUI3.Name = "buttonExit"; buttonUI3.Text = "Exit App"; buttonUI3.Width = 300; buttonUI3.Height = 50; buttonUI3.SetPosition(0,110); buttonUI3.Alpha = 0.8f; buttonUI3.TouchEventReceived += (sender, e) => { quit = true; }; dialog.AddChildLast(buttonUI1); dialog.AddChildLast(buttonUI2); dialog.AddChildLast(buttonUI3); Sce.Pss.HighLevel.UI.Scene scene = new Sce.Pss.HighLevel.UI.Scene(); scene.RootWidget.AddChildLast(dialog); UISystem.SetScene(scene); while(!quit) { Director.Instance.Update(); Director.Instance.GL.Context.Clear(); Director.Instance.Render(); UISystem.Update(Touch.GetData(0)); UISystem.Render(); Director.Instance.GL.Context.SwapBuffers(); Director.Instance.PostSwap(); } } } }

 

We set up our Director object and tell it to run with an empty scene.  We then set up our UISystem in a similar manner.  Then we create a Panel object named dialog by hand.  This process is basically the same as what UI Composer generates for us, just all created in code.  We then add 3 buttons to our panel.  For each button we add a TouchEventReceived handler in the form of a lamda function.

 

If they click the Slow Version button, we create a new SlowVersion scene object and set it as the active scene in the Director.  If we click the Fast Version button, we create a FastVersion scene object and make it the active scene.  We will cover creating these two classes in just a moment.  Finally for the Exit App button, we toggle the quit bool to true, causing the event loop to exit.

 

Now that we have created our 3 buttons, we add them to our dialog Panel.  We then create an empty Scene ( UI scene, not GameEngine2D! ), add our dialog to it and set it as the active scene.  This will render our buttons visible.

 

Now we handle our event loop.  Everything here is pretty much identical to in previous tutorials, the only key difference is that we call our UISystem.Update() and Render() within the game loop.  It is important that UISystem.Render is called after Director.Instance.Render, or the Director will render over top of it!  This loop will now continue processing until the quit bool is set by pressing the Exit App button.

 

Now lets take a look at SlowVersion.cs… the bad way of doing things!

 

 

using System; using Sce.Pss.Core.Graphics; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace SpriteList { public class SlowVersion : Scene { private TextureInfo _textureInfo; private Texture2D _texture2D; private System.Collections.Generic.List<SpriteUV> _sprites; public SlowVersion () { this.Camera.SetViewFromViewport(); _texture2D = new Texture2D("/Application/Jet.png",false); _textureInfo = new TextureInfo(_texture2D); var w = Director.Instance.GL.Context.GetViewport().Width; var h = Director.Instance.GL.Context.GetViewport().Height; System.Random rand = new System.Random(); _sprites = new System.Collections.Generic.List<SpriteUV>(); for(int i = 0; i < 1000; i++) { SpriteUV sprite = new SpriteUV(_textureInfo); sprite.Position = new Sce.Pss.Core.Vector2(rand.Next(0,w) - w/2 ,rand.Next(0,h) -h/2); sprite.Quad.S = new Sce.Pss.Core.Vector2(_texture2D.Width,_texture2D.Height); sprite.Rotate(rand.Next (0,360)); sprite.Schedule( (dt) => { sprite.Position = new Sce.Pss.Core.Vector2(rand.Next(0,w)-w/2 + _texture2D.Width/2,rand.Next(0,h)-h/2); }); _sprites.Add(sprite); } foreach(var sprite in _sprites) { this.AddChild(sprite); } FPS fps = new FPS(); fps.Position = new Sce.Pss.Core.Vector2(0,0); this.AddChild(fps); } public override void Draw () { base.Draw (); } } }

 

Most everything here has also been covered in prior tutorials, but perhaps not in this form.  SlowVersion is inherited from Scene.  For member variables it contains a single Texture2D and it’s corresponding TextureInfo object.  It also contains a List of SpriteUV’s to be rendered every frame.  All of the sprites point to the same Texture, our Jet.png graphic:

 

Jet

 

Bonus points if you can identify the type of jet! Winking smile

 

In our constructor we go about the usual things, first we setup our camera, load our texture from disk and create our TextureInfo from the loaded texture.  Next we allocate our List and then create 1000 instances of our jet sprite, randomly rotated and positioned on screen.  We also register a lamda Schedule method that will be called every frame. During each update we simply randomly relocate the airplane sprite on screen.  Finally, we add the newly created sprite to our list.  Next we loop through all of our sprites and add them to our scene.

 

In the next bit we create an FPS object, a simple helper object for displaying the current frame rate on screen.  We will cover the code in a second.

 

 

Now let’s take a look at FastVersion.cs, which you will soon notice is basically just a copy and paste job!

 

using System; using Sce.Pss.Core.Graphics; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace SpriteList { public class FastVersion : Scene { private TextureInfo _textureInfo; private Texture2D _texture2D; private System.Collections.Generic.List<SpriteUV> _sprites; private Sce.Pss.HighLevel.GameEngine2D.SpriteList _spriteList; public FastVersion () { this.Camera.SetViewFromViewport(); _texture2D = new Texture2D("/Application/Jet.png",false); _textureInfo = new TextureInfo(_texture2D); var w = Director.Instance.GL.Context.GetViewport().Width; var h = Director.Instance.GL.Context.GetViewport().Height; System.Random rand = new System.Random(); _sprites = new System.Collections.Generic.List<SpriteUV>(); for(int i = 0; i < 1000; i++) { SpriteUV sprite = new SpriteUV(_textureInfo); sprite.Position = new Sce.Pss.Core.Vector2(rand.Next(0,w) - w/2 ,rand.Next(0,h) -h/2); sprite.Quad.S = new Sce.Pss.Core.Vector2(_texture2D.Width,_texture2D.Height); sprite.Rotate(rand.Next (0,360)); sprite.Schedule( (dt) => { sprite.Position = new Sce.Pss.Core.Vector2(rand.Next(0,w)-w/2,rand.Next(0,h)-h/2); }); _sprites.Add(sprite); } _spriteList = new Sce.Pss.HighLevel.GameEngine2D.SpriteList(_textureInfo); foreach(var sprite in _sprites) { _spriteList.AddChild(sprite); } FPS fps = new FPS(); fps.Position = new Sce.Pss.Core.Vector2(0,0); this.AddChild (_spriteList); this.AddChild(fps); } public override void Draw () { base.Draw (); } } }

 

You may notice two things… first, neither SlowVersion nor FastVersion do *ANY* cleanup and leak like sieves!  In non-demonstration code, be sure to clean up after yourself!

Second, they are virtually identical, but if you run them, FastVersion runs easily 4-5x faster… why is this?

 

That is the power of SpriteList, which performs a function very similar to SpriteBatch if you are familiar with XNA programming.  We only made two changes.

 

1:

private Sce.Pss.HighLevel.GameEngine2D.SpriteList _spriteList;

We declared out spriteList member.

 

2:

_spriteList = new Sce.Pss.HighLevel.GameEngine2D.SpriteList(_textureInfo); foreach(var sprite in _sprites) { _spriteList.AddChild(sprite); }

 

Then, instead of adding the sprites to the scene, we add them to our spriteList object.  That’s it!

 

There are limitations, all sprites must have a common TextureInfo, BlendMode and Color property to be added to a spriteList, so basically create one spriteList per TextureInfo if you have multiple sprites on screen and you will see a large performance boost.

 

 

Finally lets take a quick look at the FPS.cs widget.  This is just a crude hack to display FPS on screen by creating a small texture.

 

using System; using Sce.Pss.Core; using Sce.Pss.Core.Graphics; using Sce.Pss.Core.Imaging; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace SpriteList { public class FPS : SpriteUV { TextureInfo _ti; public FPS () { Texture2D texture = new Texture2D(150,1000,false, PixelFormat.Rgba); _ti = new TextureInfo(texture); this.TextureInfo = _ti; this.Quad.S = new Sce.Pss.Core.Vector2(150,100); Scheduler.Instance.ScheduleUpdateForTarget(this,1,false); } public override void Update (float dt) { _ti.Dispose(); Image img = new Image(ImageMode.Rgba, new ImageSize(150,100), new ImageColor(255,255,255,0)); img.DrawText("FPS:" + (1/dt).ToString(), new ImageColor(255,255,255,255), new Font(FontAlias.System,32,FontStyle.Bold), new ImagePosition(0,0)); Texture2D texture = new Texture2D(150,100,false, PixelFormat.Rgba); texture.SetPixels(0,img.ToBuffer(),PixelFormat.Rgba); img.Dispose(); _ti = new TextureInfo(texture); this.TextureInfo = _ti; base.Update (dt); } } }

 

All we are doing here is creating our own SpriteUV derived object, FPS, but instead of loading the texture from file, we generate an image dynamically, just like we did way back in Hello World.  We then schedule ourselves to update every frame.  In the update we create an image and print the the current frames per second ( the fraction of a second our current time delta takes) and update our texture to the newly created image.

 

 

Here is our code in action.  Unfortunately the nature of the output resulted in a horrifically large animated gif, so I had to put this one up on YouTube:

 

Running our SpriteList demo

 

You can download all of the project code here.

Programming , , ,

4. June 2012

 

 

This tutorial covers the process of getting Cocos2D-html setup and running.  If youCocos2dHTML5 already have completed this step, skip ahead.  It is also going to cover configuring a webserver, an optional ( but relatively easy ) step.  All right, lets get started.

 

First thing you need to do is get the cocos2d-html5 sources, which you can download here.

 

image

Locate the zip icon, as demonstrated in the picture to your left, click it.

 

Save the resulting download anywhere, just remember where you save it, we will need it in a few moments.

 

Now we are going to install a webserver.  This part is strictly optional, but if you want to take full advantage of the cocos demos, you are going to want a webserver installed.  It will also allow you to access your creation from different machines.  Besides, it’s a fairly simple process.  These instructions are for Windows only.

 

Now head over to WAMP and download their server.  The WAMP server is an all in one install of Apache, MySql, PHP and a few other applications, in many ways it is complete overkill, but like I said, it’s easy.  Pick whichever version is most appropriate, it doesn’t really matter which, so long as you choose the right one for your processor ( 32 or 64 bit ).

 

image

 

Now run the installer.  I choose the default value for every entry and let it install to C:\wamp.  If you saved to a different directory, adjust your path’s accordingly later on in this tutorial.  It is going to ask you to pick your default browser and default out to explorer.exe ( the shell, not to be confused with Internet Explorer the browser ).  You can explicitly choose a browser if you want but there is little reason to do so.  When prompted for mail settings, simply take the default.  WAMP server will now be installed and running in your system tray.

 

 

 

image

 

Like such.  Left clicking the tray icon will bring up the menu shown above.  Click localhost to see your webserver in action.

 

If everything went according to plan, you should see:

 

image

 

Now we want to extract the cocos2d-html archive we downloaded earlier.  Open up the zip and navigate the folders until you find a directory that looks like such:

 

image

 

CTRL+A select the entire contents of this folder in the zip and paste it  to C:\wamp\www\.

 

Now in your browser, you should be able to navigate to localhost/index.html and you will see:

 

image

 

If so, congrats, you have successfully set up Cocos2d for HTML5.  If not, make sure you have the proper contents in c:\wamp\www, and that it’s not buried in a subdirectory.  Now optionally you can open access to it up a bit so other computers on your network can see the website.  I am by no means an apache expert ( was born and raised on IIS ), so do not take this as authoritative in any way!

 

You want to edit httpd.conf, which you can do from the tray icon, like such:

image

 

 

Find and locate the following entries to match what I’ve done here ( with your IP address obviously ):

 

Set your IP and port to listen on, entry was originally Listen 80:

# # Listen: Allows you to bind Apache to specific IP addresses and/or # ports, instead of the default. See also the <VirtualHost> # directive. # # Change this to Listen on specific IP addresses as shown below to # prevent Apache from glomming onto all bound IP addresses. # Listen 192.168.2.103:80

 

Set the server name to your servers name, or if you haven’t got DNS configured ( such as yourserver.yourname.com ) use your IP address like I have:

 

# # ServerName gives the name and port that the server uses to identify itself. # This can often be determined automatically, but we recommend you specify # it explicitly to prevent problems during startup. # # If your host doesn't have a registered DNS name, enter its IP address here. # ServerName 192.168.2.103

 

Finally, grant access to your www folder:

 

<Directory "c:/wamp/www/"> # # Possible values for the Options directive are "None", "All", # or any combination of: # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews # # Note that "MultiViews" must be named *explicitly* --- "Options All" # doesn't give it to you. # # The Options directive is both complicated and important. Please see # http://httpd.apache.org/docs/2.2/mod/core.html#options # for more information. # Options Indexes FollowSymLinks # # AllowOverride controls what directives may be placed in .htaccess files. # It can be "All", "None", or any combination of the keywords: # Options FileInfo AuthConfig Limit # AllowOverride all # # Controls who can get stuff from this server. # # onlineoffline tag - don't remove Order Allow,Deny Allow from all </Directory>

 

You should now have full access to your webserver folder from other machines on your network ( and externally if you don’t have a firewall or have port forwarding configured ).  You can of course tweak the security settings until the cows come home.

 

Now, save your changes and restart your apache server, this can again be done from the tray icon, simply left click and choose Restart All Services.  If your icon doesn’t turn green, you have made an error in the httpd.conf file.

image

 

Again, the entire server portion is optional, you can just work from the local file system, but this allows you to make use of the full demo and test suite.

 

Speaking of which, if you now open your browser to http://yourserverIP/tests/index.html you should now have access to all the various cocos2d tests, like such:

 

image

 

Now you are set up and ready to go.  In the next tutorial, we will actually get to some coding.

 

Read tutorial 2

Programming , ,

30. May 2012

 

There was an interesting ( to me anyways… ) topic on Reddit today about making games that are accessible to the blind or visual impaired.  After thinking about this for a little bit, I though that there might be a remarkably easy way to add text to speech to a console based game.  Turns out I was correct.

 

 

The following is a C# app that makes heavy use of the .NET System.Speech libraries, however it does appear to be available in Mono, so it should work across platforms.

 

In the end the process is remarkably simple, all I am doing is redirecting Console.Out ( StdOut ) to my custom class, which converts the text to speech.

 

Let’s take a look, this code is across two classes.  One is our exceedingly simple game:

 

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AudibleTextAdventure { class Program { private static StdoutToSpeech speechOut; static void Main(string[] args) { speechOut = new StdoutToSpeech(); Console.SetOut(speechOut); Console.WriteLine("You are standing in an open field west of a white house, with a boarded front door."); Console.WriteLine("There is a small mailbox here."); bool done = false; while (!done) { Console.Clear(); Console.WriteLine("What would you like to do?"); string currentCommandString = Console.ReadLine().ToLower(); string [] tokens = currentCommandString.Split(new char[] { ' ' }); string currentCommand = tokens.First(); switch (currentCommand) { case "volume": { if (tokens.Length > 1) { if(tokens[1].ToLower() == "up") speechOut.VolumeUp(); if(tokens[1].ToLower() == "down") speechOut.VolumeDown(); } break; } case "quit": done = true; Console.WriteLine("Thank you for playing, Goodbye"); break; case "help": Console.WriteLine("Sorry, you are beyond help"); break; case "changevoice": speechOut.NextVoice(); break; default: Console.WriteLine("I don't know the work \"" + currentCommand + "\""); break; } } } } }

 

 

Most of that code is the skeleton of our “game”.  The majority is just getting and displaying strings as well as parsing and handling the commands our game accepts. The only lines of any real consequence here are:

speechOut = new StdoutToSpeech(); Console.SetOut(speechOut);

 

Here we declare our StdoutToSpeech object we are about to define in a second, and replace the standard output stream with it.  Now lets look at StdoutToSpeech:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Speech; using System.Media; namespace AudibleTextAdventure { class StdoutToSpeech : System.IO.TextWriter { static System.Speech.Synthesis.SpeechSynthesizer synthesizer; static SoundPlayer soundPlayer; static System.IO.TextWriter origOut; static int currentVoice = 0; static List<System.Speech.Synthesis.InstalledVoice> voices; public StdoutToSpeech() { // Grab a reference to Stdout before it's overridden origOut = Console.Out; synthesizer = new System.Speech.Synthesis.SpeechSynthesizer(); // Will bork if no voices found voices = synthesizer.GetInstalledVoices().ToList(); synthesizer.SelectVoice(voices.First().VoiceInfo.Name); // Slow it down a bit synthesizer.Rate = -1; synthesizer.Volume = 5; soundPlayer = new SoundPlayer(); } public override void WriteLine(string value) { // We still want text to show... origOut.WriteLine(value); using (System.IO.MemoryStream mem = new System.IO.MemoryStream()) { synthesizer.SetOutputToWaveStream(mem); synthesizer.Speak(value); soundPlayer.Stream = mem; soundPlayer.Stream.Position = 0; soundPlayer.PlaySync(); } } public void VolumeUp() { if (synthesizer.Volume < 10) { synthesizer.Volume++; this.WriteLine("Volume increased"); } } public void VolumeDown() { if (synthesizer.Volume > 0) { synthesizer.Volume--; this.WriteLine("Volume reduced"); } } public void NextVoice() { currentVoice++; if (currentVoice >= voices.Count) currentVoice = 0; else { synthesizer.SelectVoice(voices[currentVoice].VoiceInfo.Name); this.WriteLine("Voice changed"); } } public override Encoding Encoding { get { throw new Exception("If you are trying to use this as text, you are in for a world of hurt!"); } } } }

 

In our constructor we create our System.Speech.Sythensis.SpeechSynthesizer, set a voice, default speed and volume for it.  We also instantiate our SoundPlayer which is going to actually play the sound file our synthesizer um… synthesizes.

 

The key to this class is that it inherits from System.IO.TextWriter, this is the only type that we can set standard out to.  Our class must implement the Encoding:Get method, which if anyone actually called would cause all kinds of problems, so we throw an Exception if it is actually called. In the constructor we take a snapshot of the original standard out, so we will still be able to print to text.

 

Otherwise most of the work happens in our WriteLine overload.  First we print out to the original standard out, otherwise our command prompt would become audio only!  Next we create our memory stream that our synthesizer is going to record to.  We then render whatever speech was printed via a Console.WriteLine() call, set it as the active stream and play it using our soundPlayer.  We using PlaySync() to make sure it finished playing before continuing execution.  You could use Play(), but it would only render the most recent sentence if a couple lines where written at once.   Also note, we only overrode WriteLine(string), so if you do Console.Write, Console.WriteLine(char[]) or any other version, nothing will happen.  If you want to support more versions, you need to implement.

 

Otherwise the rest of the code provides a simple interface for increasing and decreasing volume, as well as changing between voices.  Keep in mind though, if you are using Vista or Windows 7, you probably only have on voice installed.  You can however add additional voices ( including different languages ), I download one from here for example ( copy the dlls to your debug folder if you get an error ).

 

 

Now when you run the application, you can use the following commands:

quit – exits the “game”

volume up – increases the voices volume

volume down – decreases the voices volume

help – displays a help string

changevoice – changes to the next installed voice, if you have any.

 

 

Obviously in a real game you would do substantially more.  In a real game you would also add a great deal more error checking too, as this is just a proof of concept I slapped together!  Obviously use at your own risk, as I offer no warranty.  Then again, I never offer a warranty…

 

 

And here it is in action:

Text to speech adventure in action

Programming ,

24. May 2012

 

This tutorial is going to cover everything you need to know to get a PlayN project up and running using NetBeans.  In the end you will know everything you need in order to be able to create and run the Java, Web and Android projects using PlayN 1.3.  The iOS project should work as well, but I am not on a Mac.

 

I have come to a conclusion recently.  I hate Eclipse, every time I work in it, I am fighting with the IDE more than anything else.  More specifically, I hate the M2E maven plugin more than words can describe.  I have attempted to create a series of beginner tutorials for PlayN and each and every time, I end up jumping through a half hour or more of hoops in order to get Eclipse to build my project.  Not much point in making tutorials, if my readers can’t even get PlayN up and running, right?  I don’t want to spend all of my time troubleshooting PlayN installs!  It seems like 90% of the support issues you see with PlayN revolve around Eclipse and M2E problems.

 

So that is what this guide addresses.  It is about getting a specific version of PlayN ( 1.3 ) running with a specific version of NetBeans ( 7.1.2 ), to the point you can run all of the different kinds of targets it supports.  So if you want to get started with PlayN development, start here!  It’s going to look long and daunting, but it really isn’t all that bad, I just go into a lot of detail.  If you’ve already got Netbeans up and running, you can skip 80% of it!

 

 

What you need to get started

 

 

Before we go too far ahead, you are going to need to have downloaded and installed a Java JDK.  Note, the JRE and JDK are different things, and you very much need a JDK installed.  I recommend version 6 over 7 ( that link was to 6 ) at this point, but either *should* work. Additionally, if you are intending to do Android development, you will have to download and configure the Android SDK.  Details on configuring the Android SDK are available here, just ignore all the Eclipse bits.

 

You are now going to need a version of Netbeans.  Here you have a couple of choices:

 

image

 

You want either JavaSE or Jave EE.  If you don’t care about HTML development, go with Java SE, otherwise go with EE.  In this tutorial we are going to go with the Java SE version and add the bits as we need them.  Download and install whichever version you choose now.  Before you install, make sure you have a Java JDK installed already, you will be asked for the JDK path as part of the install process.  Otherwise it’s just a next , I agree, next, I agree, click fest.  Unless of course you are the type that reads EULAs… in which case, see you in a couple hours!

 

 

Configuring NetBeans’ required plugins

 

 

Now that you’ve installed Netbeans, fire it up.  We need to configure a couple plugins right away.  Select Tools->Plugins

image

 

 

In the resulting dialog, select the Available Plugins tab, scroll down and check “Java Web Applications” then click Install.  You can skip this part if you installed Netbeans EE.

 

image

 

Dialog will appear, click Next

 

image

 

Agree to the license agreements and click Install

 

image

 

 

This process will take a few minutes, then request that it restart NetBeans, let it.

 

Now we need to set up the NBAndroid plugin, which is the Netbeans Android connector.  Once again select Tools->Plugins.  This time select Settings in the resulting dialog:

 

image

 

Then click add, and fill in the following in the dialog.  For name enter whatever you want, I went with NBAndroid.  For URL enter http://kenai.com/projects/nbandroid/downloads/download/updatecenter/updates.xml, now click OK.

 

image

 

At this point Android should show up as an option under Available plugins.  For me, as is the way with software, I encountered a bug and it didn’t.  At this point I exited and re-launch NetBeans, go to Tools->Plugins and now the options are there under Available Plugins.  If they don’t show up for you, restart Netbeans.  Now select Android and Android Test Runner for Net Beans 7.x, like so:

 

image

 

Click install, then as before, agree to the EULA and continue. You may run into this scary message, just click continue:

 

image

 

 

All right, Android is now configured, let’s create our project!

 

 

Creating your PlayN project

 

 

Select the File->New Project menu

 

image

 

In the resulting dialog, select the Maven category, then under Projects select “Project from Archetype”. Click Next.

 

image

 

 

In the next screen, on the right hand side click the Add… button

 

image

 

 

Fill the next dialog in with the following details; Group Id = com.googlecode.playn Artifact Id= playn-archetype Version 1.3, then click OK.

 

image

 

Now make sure your newly added Archetype is selected and click Next.

 

image

 

 

Now fill in the following details.  Name your project in ProjectName, I am going with playndemo and assume you did too for the rest of this tutorial.  Artifact Id will be set automatically.  Group id is your url, in my case com.gamefromscratch.  Be sure to specify 1.3, it will start as 1.0-SNAPSHOT, you don’t want this!  Finally, and this part is extremely important, fill in the value for JavaGameClassName!  I went with playndemo, but it is extremely important to click somewhere on the screen after setting it, another text box for example, or the setting will not be saved!!!  If you get an error later on that JavaGameClassName was not set, you ran into the bug of this value not saving. Now click Finish.

 

image

 

 

If all went well, it should have generated your project like this:

 

image

 

 

Building your project ( resolve Maven dependencies )

 

 

This next part is incredibly important, we need Maven to bring in all the various dependencies.  Right click the Meta Project and select Clean and Build.

 

image

 

Maven is now going to download and configure all of the various dependencies, and there are a lot of them.  Like tea?  Perhaps now would be a good time to brew a cup.  Just keep an eye on the bottom right of your screen:

 

image

 

 

Ideally what you want to see is this:

 

image

 

Fortunately, you only need to do that part once.  If you are sitting there thinking… wow, this really took a long time, I wonder if something went wrong?!?!?!  Nope, nothing went wrong, it really does take that long!  Welcome to maven.

 

Running the Desktop Java app

 

Normally all you would have to do is right click the Java project and choose run.  However as of right now there is a dependency on the LWJGL native dlls that you need to resolve manually. If you are reading this at some point in the future, you may no longer need to perform this step!

 

Try running the Java App.  Right click playndemo Java and select Run

 

image

 

In the resulting dialog, select your main class if required and click Select Main Class.

 

image

 

You will receive:

Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.2:exec (default-cli) on project playndemo-java: Command execution failed. Process exited with an error: 1(Exit value: 1) -> [Help 1]

 

If you scroll up the error, you will see:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no lwjgl in java.library.path
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1738)
    at java.lang.Runtime.loadLibrary0(Runtime.java:823)
    at java.lang.System.loadLibrary(System.java:1028)
    at org.lwjgl.Sys$1.run(Sys.java:73)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.lwjgl.Sys.doLoadLibrary(Sys.java:66)
    at org.lwjgl.Sys.loadLibrary(Sys.java:95)
    at org.lwjgl.Sys.<clinit>(Sys.java:112)
    at org.lwjgl.opengl.Display.<clinit>(Display.java:135)
    at playn.java.JavaGLContext.viewWasResized(JavaGLContext.java:125)
    at playn.core.gl.GLContext.setSize(GLContext.java:48)

 

This is because of the missing libraries, lets add those now.  ( Or if you didn’t, this problem is fixed and skip ahead! )

 

Head on over to LWJGL’s download site and download the binaries zip.  Now extract the contents of this file somewhere, it doesn’t really matter where.  I extracted to c:\temp, preserving folder names.  The end results looked like this:

 

image

 

It’s really only the native folder we care about.  Stick it anywhere, just remember the path.

 

Now back in NetBeans, right click playndemo Java and select Properties.

 

image

 

 

We now have to add a VM param to the Run options.  On the categories side, select Run, then on the right in VM options, add  -Djava.library.path=c:\path\to\lwjgwl\windows, like this:

 

image

 

Click OK, then right click the Java project and select Run.

 

image

 

Voila, your running Java PlayN application.  Now on the the HTML project.

 

Running the PlayN HTML application

 

Right click playndemo HTML and choose Run:

 

image

 

If Run isn’t an option, something went wrong when you configured the Java Webserver plugin, or you aren’t running EE edition.  Go back and repeat that step until run appears.

 

On first run ( assuming you didn’t install a webserver as part of your Netbeans install ), you will encounter this dialog with an empty server list:

 

image

 

 

We now need to install and configure a web server, don’t worry, its an easy process.  In the menu, select Tools->Servers

 

image

 

In the resulting dialog, click Add Server…

 

image

 

Click GlassFish Server 3+, then Next

 

image

 

Save it wherever you want, I went with the defaults, click Finish ( unless you want to change default settings ):

 

image

 

If all went well, you should see the following screen, simply click Close.

 

image

 

Now right click playndemo HTML and choose run. Now in the deployment dialog, you have a server you can select, do so and click OK:

 

image

 

And voila, your app running in a browser:

 

image

 

If you have problems or need to maintain or restart your GlassFish install, you can do so via the Window->Services menu option:

image

 

 

Running your PlayN Android Application in Netbeans

 

This part has a bit of a gotcha attached.  PlayN switched to use OpenGL 2.0 and the Android emulator simply doesn’t support Open GL 2.0 yet.  You can try to run it on the emulator, but it will crash every time.  If you have a physical device, you can get Netbeans to target it.

 

First right click playndemo and select Properties:

image

 

On the left hand side selection Actions.  In the Actions list, select Run Project.  Under Execute Goals: enter android:deploy android:run, and under Set Properties: enter skipTests=True and android.device=usb.  Finally click OK.  This is behind the scenes configuring a pair of maven commands.

 

image

 

Now with your Android phone plugged in, with USB debugging enabled, right click playndemo and choose Run.  Oddly enough, sometimes it deploys and runs the application, sometimes it simply deploys it and fails to run.  Worst case scenario, locate the app icon on your device and launch it manually.

 

Voila, PlayN running on my Galaxy Note  ( the black is because of the 1280x720 resolution ):

 

export_12

 

 

As you can see, you can successfully target all three platforms from Netbeans.  Trust me, that process looked a lot more complicated than it actually was.  Plus, you only have to do 95% of it once.

Programming , ,

22. May 2012

 

In this tutorial we are going to look at playing audio with the PlayStation Suite SDK.  This chapter is pretty simple over all, so it will be rather short. In the end you will have a UI driven application that can play sound effects and songs.

 

To get started, we are going to need a couple audio files, one or more songs for background music and one or more songs for sound effects.  File formats are extremely limited in the SDK, all music files must be in MP3 format, while all sound effects must be in WAV format.  If you need to convert your file, I recommend you check out audacity.

 

 

For my song files, I grabbed two freely available Mozart tracks from this site and saved them as Song1.mp3 and Song2.mp3.  For sound effects I went to freesound.org ( free login required ) and downloaded this and this which I saved as Sound1.wav and Sound2.wav respectively.  Of course, you can pick any mp3 or wav files you’d like, but these are the ones and the names I am going to use and the names I’ve chosen.

 

Now fire up UI Composer, if you haven’t already, you might want to read this tutorial, as I am not going to cover UI Composer in detail here.  Create the following UI, I called mine AudioPlayer.  Save the results and import into your project.

 

image

 

 

Let’s take a look at our GUI code in AudioPlayer.cs first, as it is the simplest.

 

using System; using System.Collections.Generic; using Sce.Pss.Core; using Sce.Pss.Core.Imaging; using Sce.Pss.Core.Environment; using Sce.Pss.HighLevel.UI; namespace Audio { public partial class AudioPlayer : Scene { public AudioPlayer() { InitializeWidget(); buttonNext.ButtonAction += delegate(object sender,TouchEventArgs e) { if(AppMain.currentSong ==0) AppMain.currentSong = 1; else AppMain.currentSong = 0; AppMain.songChanged = true; }; buttonPrev.ButtonAction += delegate(object sender,TouchEventArgs e) { if(AppMain.currentSong ==0) AppMain.currentSong = 1; else AppMain.currentSong = 0; AppMain.songChanged = true; }; buttonPlaySound1.ButtonAction += delegate(object sender, TouchEventArgs e) { AppMain.PlaySound("Sound1.wav"); LogText ("Playing Sound1.wav"); }; buttonPlaySound2.ButtonAction += delegate(object sender,TouchEventArgs e) { AppMain.PlaySound("Sound2.wav"); LogText ("Playing Sound2.wav"); }; } public void LogText(string outText) { this.labelOut.Text = outText; } } }

 

Everything here we have seen in prior tutorials.  Basically we wire up our four buttons we created in UI Composer.  nextButton and prevButton actually cheat a bit as the fact there are only two songs makes them identical in function.  Obviously if you had more songs you would require more logic.  Basically all either method do is update currentSong and songChanged values in AppMain.  We will see these variables in more detail in a second.  buttonPlaySound1 and buttonPlaySound2 simply call a PlaySound method in AppMain.  Finally LogText simply exposes the labelOut field so the AppMain class can change it’s value.

 

 

Now lets take a look at AppMain.cs:

 

using System; using System.Collections.Generic; using Sce.Pss.Core; using Sce.Pss.Core.Environment; using Sce.Pss.Core.Graphics; using Sce.Pss.Core.Input; using Sce.Pss.Core.Audio; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; using Sce.Pss.HighLevel.UI; namespace Audio { public class AppMain { private static BgmPlayer songPlayer; private static Bgm[] songs; private static SoundPlayer soundPlayer; public static bool songChanged { get; set; } public static int currentSong { get;set; } public static void Main (string[] args) { GraphicsContext graphics = new GraphicsContext(); UISystem.Initialize(graphics); AudioPlayer audioPlayer = new AudioPlayer(); UISystem.SetScene(audioPlayer); songs = new Bgm[2]; songs[0] = new Bgm("/Application/Song1.mp3"); songs[1] = new Bgm("/Application/Song2.mp3"); songPlayer = songs[0].CreatePlayer(); songPlayer.Play(); audioPlayer.LogText("Playing song1.mp3"); while(true) { SystemEvents.CheckEvents(); List<TouchData> touchData = Touch.GetData(0); UISystem.Update (touchData); graphics.SetViewport(0, 0, graphics.Screen.Width, graphics.Screen.Height); graphics.SetClearColor(new Vector4(0,0,0,1)); graphics.SetClearDepth(1.0f); graphics.Clear(); UISystem.Render (); graphics.SwapBuffers(); if(songPlayer.Status == BgmStatus.Stopped) { if(currentSong == 0) currentSong = 1; else currentSong = 0; audioPlayer.LogText ("Song ended, swapped to song:" + currentSong.ToString()); songPlayer.Dispose(); songPlayer = songs[currentSong].CreatePlayer(); songPlayer.Play (); } if(songChanged) { songPlayer.Dispose(); songPlayer = songs[currentSong].CreatePlayer(); songPlayer.Play (); songChanged = false; audioPlayer.LogText("Song changed due to user request."); } } } public static void PlaySound(string soundName) { //if(soundPlayer != null) // soundPlayer.Dispose(); soundPlayer = new Sound("/Application/" + soundName).CreatePlayer(); soundPlayer.Play(); } } }

 

The UI code is identical to that we covered in the earlier UI Composer tutorial, so we will ignore those bits.  First we will declare our three audio related variables, a BgmPlayer, our Bgm songs array and our SoundPlayer.  BGM stands for Background Music ( I assume ), and these two variables represent our songs and the player required to play them.  Please note that both BgmPlayer and SoundPlayer have a dispose method and should be disposed of, something I do not do in this example ( given the primitive nature of the event loop ).  In your code, be sure to dispose of them when you are done.  The bool songChanged and currentSong are publically exposed so our UI has access to them, you will see them in action shortly.

 

In Main() we set up our graphics, initialize the UI system, create our UI and set it as active.  We then declare our song array to contain two songs, both of which are loaded from file using the constructor.  These (mp3 and wav) files need to be added to your project just like any other resource, right click your project-> Add Files… then right click the file, select Build Action and set it to content.  The actual BgmPlayer is created by calling Bgm.CreatePlayer(), which is what we do next, then we tell the player to start playing the song.

 

Next we start our never ending event loop just like before.  Unfortunately PS SDK has no callback facility to notify when a song has finished playing ( hopefully this is changed during beta! ) so we poll the songPlayer until the BgmStatus is Stopped.  If it is stopped, we simply set currentSong to our other index into the songs array.  As songPlayer has some unmanaged resources, we need to Dispose of it before creating a new player for the next song.  Next we tell the newly created player to play the next song.

 

Next we check if the songChanged flag was changed ( from AudioPlayer on button handlers ), if a song change has occurred, we perform the exact same logic as if a song had ended.  However instead of flipping to the next sound, the button press logic has already handled this step for us, so currentSong will already reference the next song to play.

 

Finally we expose a PlaySound method that was called earlier when we pressed a PlaySound1 or PlaySound2 button.  You are limited to one Bgm at a time, but this is not the case with SoundPlayer objects.  This code will allow multiple sound effects to play at once.  Remove the commented section to limit playback to a single sound effect at a time.  I am not sure what is proper form or if failing to dispose SoundPlayer before calling CreatePlayer again causes a leak, be wary!

 

 

And there you have a simple SoundPlayer application.  Normally at this point I post an image of our application in action, but that obviously doesn’t work for a tutorial like this, so I captured instead this video:

 

PlayStation Suite Audio Tutorial application in action

 

 

You can download the full project here.  This archive also includes all the songs and sounds used in this example.

Programming ,

Month List

Popular Comments