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


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


11. May 2012

 

In the last tutorial Working with Sprite Sheets I said that I would cover updating your scene in an upcoming tutorial. This is that tutorial.

 

In this tutorial we are going to cover three different ways that you can update your scene.  As you saw earlier it is possible to update things directly inside your game loop, either directly or using a timer.  However GameEngine2D has a number of superior scheduling options built in, and we will look at 3 different options.  The first is using a Node’s Schedule method, the second is to derive from Node and override Update() and using the Scheduler singleton.  The last is to declare a ActionBase derived object, to create a re-usable updater.  We are going to create 3 bouncing smiling faces, each controlled by a different system.

 

 

We are going to start with the following basic skeleton:

 

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.Imaging; // for Font using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace Updating { public class AppMain { public static void Main (string[] args) { Director.Initialize(); Scene scene = new Scene(); scene.Camera.SetViewFromViewport(); Director.Instance.RunWithScene(scene); } } }

 

That is about as stripped down a functional program as you can make.  Be sure to add a reference to GameEngine2D.  The Imaging using statement is needed for the Font object that we will use later.  Nothing here should be new to you if you have gone through the earlier tutorials.

 

 

Alright, lets get started using the Schedule method.  First a quick explanation of how Update() works.  Remember in earlier tutorials when you called Director.Instance.Update()?  Well this triggers off a chain reaction of events.  This results in the current scene ( the one specified in the RunWithScene call ) having it’s Update call, which in turn results in all the (registered)Nodes in the Scene having their Update() methods called.  This process happens once per frame. Schedule() marks the node to be called during update, and provides a method to be called when the update occurs.  In this case we are going to declare that method using a Lambda.  Let’s take a look:

 

First off, we declare our sprite:

Texture2D texture = new Texture2D("/Application/smile.png",false); TextureInfo ti = new TextureInfo(texture); SpriteUV sprite = new SpriteUV(ti); sprite.Quad.S = new Vector2(128,128); sprite.Position = scene.Camera.CalcBounds().Center; sprite.Position = new Vector2(sprite.Position.X - 256,sprite.Position.Y); sprite.CenterSprite();

 

Everything here you’ve seen before.  The position is all relative to the middle of the screen.  We will be rendering 3 sprites, this one will be positioned off to the left. Now for the new stuff:

 

bool goingUp = true; sprite.Schedule( (dt) => { if(goingUp) { sprite.Position = new Vector2(sprite.Position.X, sprite.Position.Y + 3); if(sprite.Position.Y >= 390) goingUp = false; } else { sprite.Position = new Vector2(sprite.Position.X, sprite.Position.Y - 3); if(sprite.Position.Y <= 64) goingUp = true; } });

 

Schedule takes a (lambda) function that is called every time Update() is called.  dt is the elapsed time since Update() was last called, although we aren’t using it this time.  Our actual method is pretty straight forward, we have a bool that determines which direction we are going.  We add 3 to our Y direction until we get near the the top of the screen, then flip direction.  Otherwise we are going down, by decrementing by 3 until we get to the bottom.  ( 64 is half of our sprite height ).  The Schedule() method is probably the simplest way to update a Node ( SpriteUV is derived from Node ) object.

 

Next we look at handling updates by deriving from Node ( actually, SpriteUV again ) and overriding the Update() method.  Create a new class, I am calling mine BouncingSmile.cs but you can call it whatever you want.  Enter the following code:

 

 

using System; using Sce.Pss.Core; using Sce.Pss.Core.Graphics; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace Updating { public class BouncingSmile : SpriteUV { private bool _goingUp; public BouncingSmile () { Texture2D texture = new Texture2D("/Application/smile.png",true); this.TextureInfo = new TextureInfo(texture); this.Quad.S = new Vector2(128,128); _goingUp = true; } public override void Update (float dt) { if(_goingUp) { this.Position = new Vector2(this.Position.X, this.Position.Y + 3); if(this.Position.Y >= 390) _goingUp = false; } else { this.Position = new Vector2(this.Position.X, this.Position.Y - 3); if(this.Position.Y <= 64) _goingUp = true; } } } }

 

As you can see, the Update() method is basically identical to our lambda method earlier.  Otherwise we are basically just handling the creation of our sprite, just like we did back in the Hello World sample.

 

Now that we have a class with an Update() method defined, lets go back to AppMain.cs and put it to use.

 

BouncingSmile bs = new BouncingSmile(); bs.Position = scene.Camera.CalcBounds().Center; bs.CenterSprite(); Scheduler.Instance.ScheduleUpdateForTarget(bs,1,false);

 

We declare our BouncingSmile object object ( whose constructor took care of creating it’s Sprite ), then position it in the middle of the screen.  Finally we register our object so that it’s Update method is going to be called.  We do this using the Scheduler singleton.  ScheduleUpdateForTarget results in the target ( bs ) having it’s Update() called every frame.  In addition to the target to update, you pass in the priority ( how early in the Scheduler’s update phase it will be called ) and whether or not to start paused.  The end result of this is virtually identical to the earlier Schedule() method.  Our next method is much different however.

 

We are now going to create another class, this one called BounceAction.cs.  Create it, then add the following code:

 

using System; using Sce.Pss.Core; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace Updating { public class BounceAction : ActionBase { private bool goingUp; public BounceAction () { goingUp = true; } public override void Update (float dt) { base.Update (dt); if(goingUp) { this.Target.Position = new Vector2(this.Target.Position.X, this.Target.Position.Y + 3); if(this.Target.Position.Y >= 390) goingUp = false; } else { this.Target.Position = new Vector2(this.Target.Position.X, this.Target.Position.Y - 3); if(this.Target.Position.Y <= 64) goingUp = true; } } } }

 

We are creating a new class derived from ActionBase.  Actions are exactly what they sound like, runnable events.  ActionBase is the very base class of action types available.  Unlike our earlier examples, BounceAction isn’t actually tied to any single object, it can be applied to any Node derived object, making it very easy to create reusable/retargetable actions.  Once again, the code looks virtually identical to our prior examples. 

 

Now head back to AppMain.cs and add the following code:

 

Texture2D texture2 = new Texture2D("/Application/smile.png",false); TextureInfo ti2 = new TextureInfo(texture); SpriteUV sprite2 = new SpriteUV(ti); sprite2.Quad.S = new Vector2(128,128); sprite2.Position = scene.Camera.CalcBounds().Center; sprite2.Position = new Vector2(sprite2.Position.X + 256,sprite2.Position.Y); sprite2.CenterSprite(); BounceAction ba = new BounceAction(); ActionManager.Instance.AddAction(ba,sprite2); ba.Run();

 

The majority of that code is just us creating another sprite object.  The key part are the last three lines.  What we are doing here is creating a BounceAction, then registering it with the ActionManager singleton, applying it to our new sprite.  Finally we trigger our action to actually run.  Just like the Scheduler singleton, ActionManager is called once per frame.  The power of using ActionManager is that the action isn’t tied to the thing being acted upon.  There are some powerful things you can do with ActionManager, like queuing up actions up, or blending between actions, etc…

 

 

So there were three different different ways to handle updates with your PS Suite GameEngine2D node objects.  In a moment I will post the completed source for AppMain.cs.  There is however one new concept in the following code, Labels.  In the comments for my earlier Hello World example, I said there were better ways to actually display Hello World on screen, and that way is using the Label class.

 

In order to make a Label, you first need a Font, which in turn needs a FontMap texture. 

 

Font font = new Font(FontAlias.System,16,FontStyle.Bold); FontMap fontMap = new FontMap(font,512);

 

Then it is just a matter of creating your Label:

Label label1 = new Label("Derived from Node",fontMap); label1.Position = new Vector2(0,0);

 

Then add it to the scene to be displayed:

scene.AddChild(label1);

 

And that… is the easiest way to display text on screen.

 

Alright, now lets put everything together.

 

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.Imaging; // for font using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace Updating { public class AppMain { public static void Main (string[] args) { Director.Initialize(); Font font = new Font(FontAlias.System,16,FontStyle.Bold); FontMap fontMap = new FontMap(font,512); Scene scene = new Scene(); scene.Camera.SetViewFromViewport(); //Center sprite BouncingSmile bs = new BouncingSmile(); bs.Position = scene.Camera.CalcBounds().Center; bs.CenterSprite(); Label label1 = new Label("Derived from Node",fontMap); label1.Position = new Vector2(bs.Position.X - 80, bs.Position.Y + 200); Scheduler.Instance.ScheduleUpdateForTarget(bs,1,false); //Left sprite Texture2D texture = new Texture2D("/Application/smile.png",false); TextureInfo ti = new TextureInfo(texture); SpriteUV sprite = new SpriteUV(ti); sprite.Quad.S = new Vector2(128,128); sprite.Position = scene.Camera.CalcBounds().Center; sprite.Position = new Vector2(sprite.Position.X - 256,sprite.Position.Y); sprite.CenterSprite(); bool goingUp = true; sprite.Schedule( (dt) => { if(goingUp) { sprite.Position = new Vector2(sprite.Position.X, sprite.Position.Y + 3); if(sprite.Position.Y >= 390) goingUp = false; } else { sprite.Position = new Vector2(sprite.Position.X, sprite.Position.Y - 3); if(sprite.Position.Y <= 64) goingUp = true; } }); Label label2 = new Label("Using lambda",fontMap); label2.Position = new Vector2(sprite.Position.X - 60, sprite.Position.Y + 200); //Right sprite Texture2D texture2 = new Texture2D("/Application/smile.png",false); TextureInfo ti2 = new TextureInfo(texture); SpriteUV sprite2 = new SpriteUV(ti); sprite2.Quad.S = new Vector2(128,128); sprite2.Position = scene.Camera.CalcBounds().Center; sprite2.Position = new Vector2(sprite2.Position.X + 256,sprite2.Position.Y); sprite2.CenterSprite(); BounceAction ba = new BounceAction(); ActionManager.Instance.AddAction(ba,sprite2); ba.Run(); Label label3 = new Label("ActionBase",fontMap); label3.Position = new Vector2(sprite2.Position.X - 50, sprite2.Position.Y + 200); scene.AddChild(sprite); scene.AddChild(bs); scene.AddChild (sprite2); scene.AddChild(label1); scene.AddChild(label2); scene.AddChild(label3); Director.Instance.RunWithScene(scene); } } }

 

And here is our finished application:

smilebounce

 

As always, you can download the entire project here. Oh, and this time I actually remember to test it on my Vita! Smile

Programming


7. May 2012

 

As the title suggests, this thread is going to be about using sprite sheets with PS Studio.  For those of you unfamiliar with the term, a spritesheet is a single image with multiple sprites.  You generally group your sprites together on a single sheet as it is much more efficient for loading and generally performs better than loading one texture per sprite.

 

EDIT(5/11/2012): If you are working with an actual Vita device and using the beta SDK, there is a bug on the Vita XML that prevents this from working. Read this post for a simple workaround. Hopefully in time Sony fixes this and you no longer need to apply the bug fix. Note, the problem only occurs on an actual device.

 

Instead of showing you how to generate a spritesheet, I am going to recycle a previous post I made on creating a spritesheet using Daz3D.  Fortunately Daz Studio is still available for free if you want to follow along.  Of course you can create your spritesheet however you want, or can simply download a freely available spritesheet such as those available at opengameart.org.  Or of course you can just use my sheet which will be available later.

 

In this example, I am going to use a different program than the GIMP for assembling the spritesheet.  The end result of my Daz tutorial is a directory full of 128x96 images like these:

 

Walk_left00Walk_left01Walk_left02

 

You can download a zipped copy of the sprites I rendered right here.

 

I generated 19 frames of walking animation in each direction.  This time I am going to use the free version of the tool TexturePacker to generate my sheet.  Download and fire up TexturePacker and you will be greeted with this interface:

 

image

 

The first thing you want to do is add your sprites to the sprite palette. You can either drag and drop the folder ( using Windows Explorer ) containing your sprites to the sprite panel on the right or hit the Add Sprites button and select the sprites individually.  Add all of the sprites you just created in Daz3D ( or the folder you downloaded and unzipped ) using either method.  Dropping a folder will automatically add those sprites in a folder by that name keeping things a bit more organized.  Here is the results of me dropping my walkCycle folder on the sprites panel:

 

image

 

As you can see, it added all of the sprites under a folder named WalkCycle and automatically layed out our sprite sheet as efficiently as possible.  As this point though, we don’t really care all that much about efficiency, so we are going to make a few small changes.  In the Texture Settings panel we want to fill in a couple options.  First drop down DataFormat and change it to “Generic XML”.  Next, under Data File, pick a directory and filename to save your sprite sheet, I choose c:\temp\walk.xml.  This will automatically set the texture file to c:\temp\walk.png.  Otherwise, these are the settings I used:

 

image

 

 

You of course can use whatever settings you want, but if you want exactly the same results as me, use the above.  Now that you are ready, hit the publish button.

image

 

The end result is a PNG file and an XML document. 

 

The resulting image file:

 

walk

 

You can download the generated image file here.

 

It also generated an XML file with all of the sprite details.  The contents of that XML file look like:

 

<?xml version="1.0" encoding="UTF-8"?> <!-- Created with TexturePacker http://texturepacker.com--> <!-- $TexturePacker:SmartUpdate:a6e51795dbe6b13cd639951a1f67241c$ --> <!--Format: n => name of the sprite x => sprite x pos in texture y => sprite y pos in texture w => sprite width (may be trimmed) h => sprite height (may be trimmed) oX => sprite's x-corner offset (only available if trimmed) oY => sprite's y-corner offset (only available if trimmed) oW => sprite's original width (only available if trimmed) oH => sprite's original height (only available if trimmed) r => 'y' only set if sprite is rotated --> <TextureAtlas imagePath="walk.png" width="512" height="960"> <sprite n="Walk_left00" x="0" y="0" w="128" h="96"/> <sprite n="Walk_left01" x="128" y="0" w="128" h="96"/> <sprite n="Walk_left02" x="256" y="0" w="128" h="96"/> <sprite n="Walk_left03" x="384" y="0" w="128" h="96"/> <sprite n="Walk_left04" x="0" y="96" w="128" h="96"/> <sprite n="Walk_left05" x="128" y="96" w="128" h="96"/> <sprite n="Walk_left06" x="256" y="96" w="128" h="96"/> <sprite n="Walk_left07" x="384" y="96" w="128" h="96"/> <sprite n="Walk_left08" x="0" y="192" w="128" h="96"/> <sprite n="Walk_left09" x="128" y="192" w="128" h="96"/> <sprite n="Walk_left10" x="256" y="192" w="128" h="96"/> <sprite n="Walk_left11" x="384" y="192" w="128" h="96"/> <sprite n="Walk_left12" x="0" y="288" w="128" h="96"/> <sprite n="Walk_left13" x="128" y="288" w="128" h="96"/> <sprite n="Walk_left14" x="256" y="288" w="128" h="96"/> <sprite n="Walk_left15" x="384" y="288" w="128" h="96"/> <sprite n="Walk_left16" x="0" y="384" w="128" h="96"/> <sprite n="Walk_left17" x="128" y="384" w="128" h="96"/> <sprite n="Walk_left18" x="256" y="384" w="128" h="96"/> <sprite n="Walk_right00" x="384" y="384" w="128" h="96"/> <sprite n="Walk_right01" x="0" y="480" w="128" h="96"/> <sprite n="Walk_right02" x="128" y="480" w="128" h="96"/> <sprite n="Walk_right03" x="256" y="480" w="128" h="96"/> <sprite n="Walk_right04" x="384" y="480" w="128" h="96"/> <sprite n="Walk_right05" x="0" y="576" w="128" h="96"/> <sprite n="Walk_right06" x="128" y="576" w="128" h="96"/> <sprite n="Walk_right07" x="256" y="576" w="128" h="96"/> <sprite n="Walk_right08" x="384" y="576" w="128" h="96"/> <sprite n="Walk_right09" x="0" y="672" w="128" h="96"/> <sprite n="Walk_right10" x="128" y="672" w="128" h="96"/> <sprite n="Walk_right11" x="256" y="672" w="128" h="96"/> <sprite n="Walk_right12" x="384" y="672" w="128" h="96"/> <sprite n="Walk_right13" x="0" y="768" w="128" h="96"/> <sprite n="Walk_right14" x="128" y="768" w="128" h="96"/> <sprite n="Walk_right15" x="256" y="768" w="128" h="96"/> <sprite n="Walk_right16" x="384" y="768" w="128" h="96"/> <sprite n="Walk_right17" x="0" y="864" w="128" h="96"/> <sprite n="Walk_right18" x="128" y="864" w="128" h="96"/> </TextureAtlas>

 

You can download the xml file here. This XML file contains the details about how our original sprites are arranged within the sprite sheet and will prove useful in a moment.

 

Alright, now that we have our spritesheet and our sprite map XML file, lets fire up PSSuite and get down to some coding.  This tutorial assumes you have already gone through my earlier tutorials, at the very least the two Hello World tutorials.

 

Now create a new solution in PlayStation Studio, I called mine SpriteSheet.  We need to add a couple of references right away, add a reference to GameEngine2D, System.Xml and System.Xml.Linq. Now add your spritesheet png and xml files to the project, right click them and set their build action to content.

 

 

First we are going to create our gameloop in AppMain.cs.  I want to point something out right away… this is not the way you handle game events!  So the way things are done in this example are *NOT* the way we will do things in the future.  I went this route because you are already familiar with most of the code here.  In a (very near) future tutorial, I will show the “proper” way to handle updating game objects.

 

With that warning in place, 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.HighLevel.GameEngine2D; namespace SpriteSheet { public class AppMain { private static Walker walker; public static void Main (string[] args) { Director.Initialize(); Director.Instance.GL.Context.SetClearColor(255,255,255,0); walker = new Walker("walk.png","walk.xml"); var scene = new Scene(); scene.Camera.SetViewFromViewport(); var sprite = walker.Get("Walk_left00"); sprite.Position = scene.Camera.CalcBounds().Center; sprite.CenterSprite(); sprite.Scale = new Vector2(2,2); scene.AddChild(sprite); Director.Instance.RunWithScene(scene,true); System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); int spriteOffset = 0; timer.Start(); bool walkLeft = true; while(true) { if(timer.ElapsedMilliseconds > 100f) { string spriteName; if(walkLeft) spriteName= "Walk_left" + spriteOffset.ToString("00"); else spriteName= "Walk_right" + spriteOffset.ToString("00"); sprite.TileIndex2D = walker.Get (spriteName).TileIndex2D; if(spriteOffset >= 18) { spriteOffset = 0; walkLeft = !walkLeft; } else spriteOffset++; timer.Reset(); timer.Start(); } Sce.Pss.HighLevel.GameEngine2D.Director.Instance.Update (); Sce.Pss.HighLevel.GameEngine2D.Director.Instance.Render(); Sce.Pss.HighLevel.GameEngine2D.Director.Instance.GL.Context.SwapBuffers(); Sce.Pss.HighLevel.GameEngine2D.Director.Instance.PostSwap(); } } } }

Click here to download AppMain.cs

 

Most of the concepts in this code we’ve seen in a previous tutorial, so I will only highlight the new details.  First thing is we declare a Walker object, this is a class we are going to create shortly that is going to handle our sprite sheet.  As you can see, the Walker constructor takes the file name of the sprite texture and xml file you created earlier as parameters. 

 

Most of the remainder of this initial code is a matter of setting up our scene and sprite objects just like we did in Hello World.  A key difference is we are getting the sprite from our walker object instead of creating it from scratch or loading it from file.  The line:

sprite.Scale = new Vector2(2,2);

Is simply to double the size of our sprite in the viewport to see it better, you can easily remove this if you wish.

 

We create a Stopwatch ( a .NET object ) and start it counting up, then enter our infinite game loop.  Lets take a look at the new logic in our game loop

 

if(timer.ElapsedMilliseconds > 100f) { string spriteName; if(walkLeft) spriteName= "Walk_left" + spriteOffset.ToString("00"); else spriteName= "Walk_right" + spriteOffset.ToString("00"); sprite.TileIndex2D = walker.Get (spriteName).TileIndex2D; if(spriteOffset >= 18) { spriteOffset = 0; walkLeft = !walkLeft; } else spriteOffset++; timer.Reset(); timer.Start(); }

 

This is basically the “guts” of our game loop.  What we are doing here is waiting for the timer to reach 1/10th of a second.  Every tenth of a second we want to move on to the next frame of animation.  However we have only 19 ( walk_left00 to walk_left18 ) frames of animation in each direction and once we hit the end of our walk cycle, we want to walk in the other direction.  So what we do here is loop through each frame of animation until we hit walk_left18 or walk_right18, at which point we flip directions by inverting the value of the walkLeft bool.  This results in changing the spriteName text prefix, otherwise we simply increment to the next frame.  Then we start our timer over again to begin the process again a tenth of a second later.  One more time, this is not the proper way to handle updates!

 

The key line in all of that was:

sprite.TileIndex2D = walker.Get (spriteName).TileIndex2D;

 

This line actually tells our sprite to refer to a different sprite within our spritesheet, causing the animation.  That will make more sense in a second when we look at the code for Walker.cs.  Speaking of which, lets create it now!

 

To create a new cs file we either click the New icon ( image) or hit CTRL + N.  In the resulting dialog select General on the left, Empty Class on the right and name it Walker.cs, like such:

 

image

 

Now open up Walker.cs and enter the following code:

 

using System; using System.Linq; using System.Xml; using System.Xml.Linq; using System.Collections.Generic; using Sce.Pss.Core.Graphics; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace SpriteSheet { public class Walker { private TextureInfo _textureInfo; private Texture2D _texture; private System.Collections.Generic.Dictionary<string,Sce.Pss.HighLevel.GameEngine2D.Base.Vector2i> _sprites; public Walker (string imageFilename, string imageDetailsFilename) { XDocument doc = XDocument.Load ("application/" + imageDetailsFilename); var lines = from sprite in doc.Root.Elements("sprite") select new { Name = sprite.Attribute("n").Value, X1 = (int)sprite.Attribute ("x"), Y1 = (int)sprite.Attribute ("y"), Height = (int)sprite.Attribute ("h"), Width = (int)sprite.Attribute("w") }; _sprites = new Dictionary<string,Sce.Pss.HighLevel.GameEngine2D.Base.Vector2i>(); foreach(var curLine in lines) { _sprites.Add(curLine.Name,new Vector2i((curLine.X1/curLine.Width),9-(curLine.Y1/curLine.Height))); } _texture = new Texture2D("/Application/" + imageFilename,false); _textureInfo = new TextureInfo(_texture,new Vector2i(4,10)); } ~Walker() { _texture.Dispose(); _textureInfo.Dispose (); } public Sce.Pss.HighLevel.GameEngine2D.SpriteTile Get(int x, int y) { var spriteTile = new SpriteTile(_textureInfo); spriteTile.TileIndex2D = new Vector2i(x,y); spriteTile.Quad.S = new Sce.Pss.Core.Vector2(128,96); return spriteTile; } public Sce.Pss.HighLevel.GameEngine2D.SpriteTile Get(string name) { return Get (_sprites[name].X,_sprites[name].Y); } } }

Click here to download Walker.cs

 

Our Walker class is in charge of loading and handling our spritesheet.  TextureInfo and Texture2D you have already been exposed to and in this case, nothing is different here.  Next up we declare a Dictionary of <string,Vector2i> named _sprites.  The Vector2i type is declared in GameEngine2D.Base and is simple a pair of ints. _sprites is used to store the x,y location of each individual sprite in our spritesheet, accessed by the sprites filename.

 

Let’s take a look first at the constructor.  This code is pretty straight forward if you have ever worked with Linq or XML.  We are simply opening our XML file, the filename of which we passed in from AppMain.  Note that we added “application/” to our paths, all your files on Vita are located under this subdirectory.  Next we read the XML file and extract each <sprite> entry into a new anonymous type composed of Name, X1, Y1, Height and Width.  Name represents the file name of the source image, X1 and Y1 represent the pixel coordinates within the generated spritesheet, while Height and Width are the dimensions of the sprite within the sprite sheet.  Given that all of our sprites are the same size, these values are of little importance.

 

Now that we’ve parsed out our XML file, we populate our _sprites dictionary using these values ( minus height and width which we don’t need past this point ).  However, we don’t actually want the x and y pixel coordinates of our image, but instead the offset within the spritesheet texture.  We can determine this value by dividing the X and Y values by the sprite Width and Height respectively.  Keep in mind, this works because all our sprites are the same size ( 128x96 ) and would require different logic if you had sprites of differing sizes in your sprite sheet.  You may notice I subtract 9 from the height value… this is because GameEngine2D SpriteTile’s locations start from the bottom left instead of the top left!  I prefer top right and this is how I generated the sheet, so invert the values.  If you design your sprite sheets to start at the bottom left ( so the first frame is at the bottom left corner ), you wont need to perform this calculation.  Now you may be wondering… why 9?  Well that’s the number of sprites we have in each row on the sprite sheet (10, counting from 0 equals 9).

 

Finally we load our texture using the filename we passed in to the constructor.  Again we prepend “Application/” to our filename.  The last thing of note here is the second value in our TextureInfo constructor.  This Vector2i informs the TextureInfo the dimensions of our sprite sheet, telling it at there are 4 columns of 10 rows of sprites.  It doesn’t matter that the last row isn’t full of sprites ( there are only 2 in the spritesheets bottom row ), if you try to access them you will simply get an empty space or whatever the background colour of your spritesheet is.

 

Our destructor is nothing special, just cleans up like a good little citizen should.  Remember it’s your responsibility to dispose of any objects you own and are no longer using.  C# is garbage collected, but its still easy to run out of memory in a hurry if you don’t keep things tidy.

 

Finally we have a pair of Get() methods.  The one takes a sprite name, looks it up in our _sprite dictionary, retrieves the X and Y offset of the sprite within the texture, then passes those values into the other Get() method.  This Get() method then creates a new SpriteTile assigning it our already created _textureInfo.  Next it sets the tiles index within the texture using the passed in coordinates to represent our currently selected sprite, sets the tiles dimensions to 128x96 (pixels) and returns our sprite.  Please notice there is absolutely no error checking or handling here in order to keep things short.  You really should have a wee bit more error checking in your own code! Winking smile

 

The end result of all this activity:

 

walkcycle

 

 

Click here to download the entire project source. In addition to the full project source, the zip also includes all the image files used in this tutorial.

Programming


30. April 2012

 

BlenderOnVita

We are now going to look at creating a fully textured model in Blender and exporting to a PlayStation Studio SDK project, a simple model viewer.  We create a very simple model, UV map, texture then export it.  At this point, we process it using the PS Studio ModelConverter tool, import it into PS Studio then finally run our code in the simulator.

 

The example model is as I said, brutally simple ( it’s simply a dice, er… die, also known as a textured cube ). However the process for exporting more complex models is exactly the same.  The process isn’t really all that difficult, especially if you know your way around Blender, but if you don’t know Blender all that well don’t worry, I’ve actually captured the entire process in video form.  This video covers exactly the same thing that the rest of this tutorial does, so if you have problems following one, refer to the other and vice versa.

 

 

Modeling, texturing and exporting from Blender to Sony PlayStation Suite SDK in under 5 minutes

 

 

The above video is actually encoded at 1080p and is probably almost illegible at anything less than 720p.  You can watch it on YouTube or Vimeo in full definition.  Again, the video demonstrates exactly what I am going to show below, except the source code.  So if you are the type that prefers to learn by watching, or the type that learns by reading, you get the best of both worlds here!  Alright, let’s get started.

 

 

If you already know how to model, texture and export in COLLADA format using Blender, I suggest you skip ahead to part 2, as the remainder of this section will be quite boring for you… it goes into detail, a lot of detail. Smile

 

 

If you haven’t already, fire up Blender.  We are going to work with the default cube, if you don’t have a default cube select the Add menu –> Mesh –>Cube.  The first thing we want to do is create a new image to texture our model.

 

In the properties panel to your right ( assuming you are using the default layout, which I will for the remainder of this tutorial ), locate the Textures tab and click it.  Like such:

 

image

 

 

Now change “Type” to Image:

image

 

Scroll down slightly to the Image section ( expand it if required ) and click New:

image

 

In the resulting dialog, we specify the texture details.  I am going to name it the ultra imaginative name “Texture” but fell free to call it whatever you want.  1024x1024 is massive overkill for what we are doing, but hey… I like overkill.  When done, click OK.

 

image

 

Now we want to apply some simple texture mapping to our 3D cube.  In the 3D view, make sure you are in “Edit Mode”, either by hitting Tab until selected, or via this menu:

image

 

Now that you are in edit mode, select all the faces of your mesh by hitting “a”.  Now in the Mesh menu, you want to select Mesh->UV Unwrap…->Follow Active Quads.

image

 

Simply click OK at the resulting dialog menu.  Now we want to switch to UV editing mode.  Locate the icon to the bottom left of your view, click it and select UV/Image Editor.

image

 

We now want to make our texture the active image.  Locate the “Browse image to be linked” button, click it and select texture from the list.

 

image

 

Now press A to select all of our faces, then using hit S to scale ( then mouse move, left click when done), followed by G to translate, until our UV coordinates are all over the black image, like so:

image

 

Now we want to quickly paint our dice faces on the texture within the UV coordinates.  From the menubar select Image->Image Painting.

image

 

Now left click to mouse paint the texture like the face of a die.  You can use the left hand panel ( press “N” if not visible ) to change the paint settings.  When done you should have something like this:

image

 

Now, back in the Image menu, unclick Image Painting.  Now we want to save our completed image.  In the same menu as before, select “Save as Image” or hit F3.  In the resulting dialog, locate the directory you want to save the texture to ( make it the same place you are going to save the model ), name it, then click Save as Image:

image

 

Now with that complete, we assign our image to our texture.  Back in the Texture panel ( to the right ), locate the Image section, click the Browse Image to be Linked button and select your texture.

image

 

Now scroll down lower in the Texture panel, locate Mapping, then select the Coordinates: dropdown and select UV.

image

 

Right below that, select the Map: dropdown and select UVMap.

image

 

 

 

All right, now we have a fully texture mapped model ready for exporting.  From the File Menu ( top right ), select File->Export->COLLADA(.dae).

image

 

 

In the resulting dialog, navigate to the same location where you saved your texture, name it ( I used box.dae ), optionally select “Export only selected” then click Export COLLADA.

 

image

 

 

And we are now done with Blender.

 

 

This section has already gotten quite long so I am going to break this post into two parts.

 

 

Continue on to Part 2.

Programming Art


GFS On YouTube

See More Tutorials on DevGa.me!

Month List