Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
20. June 2012

 

This tutorial is going to cover creating transition effects between your GameEngine2D scenes.  It is actually going to be rather short and mostly code, as the subject is actually quite simple and consistent between different effects.

 

All right, lets just right in.  First of all, when need our main application.  This is going to perhaps be the simplest one we have created to date, and all of the code should be familiar to you by now.  Create a new file named AppMain.cs as follows:

 

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; namespace SceneTransitions { public class AppMain { public static void Main (string[] args) { Director.Initialize(); Scene1 scene1 = new Scene1(); Director.Instance.RunWithScene(scene1); } } }

 

All we are doing is Initializing the Director object, creating our custom scene of type Scene1 and telling it to run.  This literally is about the shortest PlayStation Mobile application you can create.

 

Now lets take a look at Scene1.cs

using System; using Sce.Pss.Core.Graphics; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace SceneTransitions { public class Scene1 : Scene { Texture2D texture; TextureInfo ti; public Scene1 () { this.Camera.SetViewFromViewport(); texture = new Texture2D("/Application/pic1.png",false); ti = new TextureInfo(texture); SpriteUV sprite = new SpriteUV(ti); sprite.Quad.S = ti.TextureSizef; sprite.Position = new Sce.Pss.Core.Vector2(0,0); this.AddChild(sprite); this.ScheduleUpdate(1); this.RegisterDisposeOnExitRecursive(); } ~Scene1 () { ti.Dispose(); texture.Dispose(); } public override void Update (float dt) { if((Sce.Pss.Core.Input.GamePad.GetData(0).ButtonsDown & Sce.Pss.Core.Input.GamePadButtons.Cross) == Sce.Pss.Core.Input.GamePadButtons.Cross) { if(Director.Instance.CurrentScene.IsRunning) Director.Instance.ReplaceScene(new Scene2()); } } } }

 

We are declaring a new class Scene1, which is derived from GameEngine2D.Scene. In this scene, in the constructor we are setting the camera to be sized to the viewport, creating a texture from our image pic1.png ( oh yeah, you need to add 3 images to your project, I picked three PS Vita wallpapers I downloaded off the net, you can use whatever files you want, but I assume you name them pic1.png pic2.png and pic3.png ) then create a sprite out of it.  This is all stuff we have done in prior tutorials.  We now add the sprite to our scene, and schedule this scene to receive updates.  Finally we call this.RegisterDisposeOnExitRecursive().  This function call is key, as it tells the Director to dispose of this scene when it is replaced.  If you do not call this, we will quickly run out of memory.

 

Next up is our destructor.  TextureInfo and Texture are both classes that you are expected to manage destruction of yourself ( they implement IDisposable ).  So in the destructor, which is called when our scene is replaced, we need to dispose of both of these items.  ( Actually I believe TextureInfo will take care of Texture2D, but don’t quote me on that ).

 

Next up is our Update() method.  As we have seen in the past, this method is called once every frame and is where your scene’s logic should be located.  In this case, we are waiting on the user hitting the Cross button ( or S key if using the simulator ), in which case we tell the Director to Replace the scene with a new created Scene2, which we will now create.

 

Create a file named Scene2.cs with the following code ( which is going to look extremely familiar… )

 

using System; using Sce.Pss.Core.Graphics; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace SceneTransitions { public class Scene2 : Scene { Texture2D texture; TextureInfo ti; public Scene2 () { this.Camera.SetViewFromViewport(); texture = new Texture2D("/Application/pic2.png",false); ti = new TextureInfo(texture); SpriteUV sprite = new SpriteUV(ti); sprite.Quad.S = ti.TextureSizef; sprite.Position = new Sce.Pss.Core.Vector2(0,0); this.AddChild(sprite); this.ScheduleUpdate(1); this.RegisterDisposeOnExitRecursive(); } ~Scene2() { ti.Dispose(); texture.Dispose(); } public override void Update (float dt) { if((Sce.Pss.Core.Input.GamePad.GetData(0).ButtonsDown & Sce.Pss.Core.Input.GamePadButtons.Cross) == Sce.Pss.Core.Input.GamePadButtons.Cross) { if(Director.Instance.CurrentScene.IsRunning) { Director.Instance.ReplaceScene( new TransitionCrossFade( new Scene3() ) { Duration = 4.0f, Tween = (x) => Sce.Pss.HighLevel.GameEngine2D.Base.Math.PowEaseOut( x, 3.0f )} ); } } } } }

 

So, basically it’s a cut and paste job of Scene1, just with a different texture file, and a different looking replace scene call.  I will look closer at that in a second.  Notice too that here we are creating a Scene3 in our Update() call, lets go ahead and create it next.

 

Create Scene3.cs

 

using System; using Sce.Pss.Core.Graphics; using Sce.Pss.HighLevel.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; namespace SceneTransitions { public class Scene3 : Scene { Texture2D texture; TextureInfo ti; public Scene3 () { this.Camera.SetViewFromViewport(); texture = new Texture2D("/Application/pic3.png",false); ti = new TextureInfo(texture); SpriteUV sprite = new SpriteUV(ti); sprite.Quad.S = ti.TextureSizef; sprite.Position = new Sce.Pss.Core.Vector2(0,0); this.AddChild(sprite); this.ScheduleUpdate(1); this.RegisterDisposeOnExitRecursive(); } ~Scene3() { ti.Dispose(); texture.Dispose(); } public override void Update (float dt) { if((Sce.Pss.Core.Input.GamePad.GetData(0).ButtonsDown & Sce.Pss.Core.Input.GamePadButtons.Cross) == Sce.Pss.Core.Input.GamePadButtons.Cross) { if(Director.Instance.CurrentScene.IsRunning) { Director.Instance.ReplaceScene( new TransitionSolidFade( new Scene1() ) { Duration = 1.0f, Tween = (x) => Sce.Pss.HighLevel.GameEngine2D.Base.Math.PowEaseOut( x, 3.0f )} ); } } } } }

Again, another cut and paste job, with just the texture file name changed, and a different ReplaceScene() call, this time creating a Scene1, basically creating an infinite application that shows Scene1, then button press, shows Scene2, button press, shows Scene3, button press, shows Scene1, repeat over an over.

 

Now lets look at the key differences between how the transitions work:

 

Scene1:

Director.Instance.ReplaceScene(new Scene2());

Scene2:

Director.Instance.ReplaceScene( new TransitionCrossFade( new Scene3() ) { Duration = 4.0f, Tween = (x) => Sce.Pss.HighLevel.GameEngine2D.Base.Math.PowEaseOut( x, 3.0f )} );

Scene3:

Director.Instance.ReplaceScene( new TransitionSolidFade( new Scene1() ) { Duration = 1.0f, Tween = (x) => Sce.Pss.HighLevel.GameEngine2D.Base.Math.PowEaseOut( x, 3.0f )} );

 

The transition from the first screen, does nothing, it simply transition to the next scene with no visual effect.  Your first image will simply be replaced with your second image.

 

Scene2 however, transitions by creating a new temporary scene, a TransitionCrossFade, which is essential a short lived scene that goes between Scene2 and Scene3 and causes a cross fade effect.  As you can see, we provide a Tween method which controls the rate at which the fade will occur.  A tween are not simply the people responsible for the bizarre success of the Twilight franchise, its actually short had for inbetween, which is essentially just a function that is going to be called over and over during a transition until it is completed.  Tweens are quite common in the animation world. 

 

Scene3 works almost identically to Scene2, but instead of a cross fade, it’s a solid fade.  You can see the difference in the video of this application in action.  There is one very important thing to be aware of here… these temporary scenes, TransitionCrossFade and TransitionSolidFade, if you try to replace them before they have completed running, your application will crash!  Truth is, in real life you won’t be switching scenes this quickly, so it should be a non-issue, but it is something you should be aware of.  If you want to witness this first hand, load this code up in the simulator and just repeatedly hit S, you will crash soon enough.

 

Now, let’s take a look at our code in action:

 

Transition between scenes on the PlayStation Vita Simulator

 

As always, you can download the full sources here.

Programming


13. June 2012

 

In this tutorial we are going to look at the various Gesture detectors built into the UI layer of the PlayStation Mobile SDK.  They all work fairly consistently, so this tutorial is going to be pretty light in explanation. 

 

There are 6 gesture detectors built in to the PS SDK; FlickGestureDetector, DragGestureDetector, TapGestureDetector, LongPressGestureDetector, PinchGestureDetector and DoubleTapGestureDetector.  DoubleTapGestureDetector barely works on the simulator and doesn’t work at all on the actual Vita, so we aren’t going to use it. 

 

Let’s just right in to coding, in this tutorial we are going to create an ImageBox UI widget that you can use with all these various GestureDetectors to flick, scale and drag all over the screen.  Here is the code:

 

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.UI; namespace UIEffects { public class AppMain { private static GraphicsContext _graphics; private static ImageBox _imageBox; private static Label _label; private static FlickGestureDetector _flickGestureDetector; private static DragGestureDetector _dragGestureDetector; private static TapGestureDetector _tapGestureDector; private static LongPressGestureDetector _longPressGestureDetector; private static PinchGestureDetector _pinchGestureDetector; static private bool quit = false; private static float _origWidth; private static float _origHeight; private static float _origX; private static float _origY; // Note, no housecleaning, all event handlers leak! public static void Main(string[] args) { _graphics = new GraphicsContext(); UISystem.Initialize(_graphics); Sce.Pss.HighLevel.UI.Panel dialog = new Panel(); dialog.Width = _graphics.GetViewport().Width; dialog.Height = _graphics.GetViewport().Height; _label = new Label(); _label.Width = dialog.Width; _label.Height = 35; _label.Font = new Sce.Pss.Core.Imaging.Font(Sce.Pss.Core.Imaging.FontAlias.System,32,Sce.Pss.Core.Imaging.FontStyle.Regular); _label.SetPosition(0,0); _label.HorizontalAlignment = HorizontalAlignment.Center; _label.TextColor = new UIColor(0,0,0,255); _imageBox = new Sce.Pss.HighLevel.UI.ImageBox(); _imageBox.Image = new ImageAsset("/Application/image.png"); _imageBox.PivotType = PivotType.MiddleCenter; _imageBox.Width = _imageBox.Image.Width;///2; _imageBox.Height = _imageBox.Image.Height;///2; _imageBox.SetPosition(dialog.Width/2,//-_imageBox.Width/2, dialog.Height/2);///2-_imageBox.Height/2); _origWidth = _imageBox.Width; _origHeight = _imageBox.Height; _origX = _imageBox.X; _origY = _imageBox.Y; _flickGestureDetector = new FlickGestureDetector(); _flickGestureDetector.Direction = FlickDirection.Horizontal; _flickGestureDetector.FlickDetected += delegate(object sender, FlickEventArgs e) { if(e.Speed.X < 0) // Left new MoveEffect(_imageBox,800f,0f,_imageBox.Y,MoveEffectInterpolator.Elastic).Start (); else new MoveEffect(_imageBox,800f,dialog.Width,_imageBox.Y,MoveEffectInterpolator.Elastic).Start (); _label.Text = "Flicked"; }; _dragGestureDetector = new DragGestureDetector(); _dragGestureDetector.DragDetected += delegate(object sender, DragEventArgs e) { var newPos = new Vector2(e.Source.X,e.Source.Y) + e.Distance; e.Source.SetPosition(newPos.X,newPos.Y); _label.Text = "Dragged"; }; _tapGestureDector = new TapGestureDetector(); _tapGestureDector.TapDetected += delegate(object sender, TapEventArgs e) { e.Source.SetPosition(dialog.Width/2,dialog.Height/2); _label.Text = "Tapped"; }; _longPressGestureDetector = new LongPressGestureDetector(); _longPressGestureDetector.MinPressDuration = 3000f; _longPressGestureDetector.LongPressDetected += delegate(object sender, LongPressEventArgs e) { MessageDialog.CreateAndShow(MessageDialogStyle.Ok,"Press results", "Hey, let go of me! " + (e.ElapsedTime/1000).ToString() + " seconds elapsed!"); _label.Text = "Long pressed"; }; _pinchGestureDetector = new PinchGestureDetector(); _pinchGestureDetector.PinchDetected += delegate(object sender, PinchEventArgs e) { Vector2 newSize = new Vector2(e.Source.Width * e.Scale,e.Source.Height * e.Scale); e.Source.SetSize(newSize.X,newSize.Y); _label.Text = "Pinched"; }; _imageBox.AddGestureDetector(_flickGestureDetector); _imageBox.AddGestureDetector(_dragGestureDetector); _imageBox.AddGestureDetector(_tapGestureDector); _imageBox.AddGestureDetector(_longPressGestureDetector); _imageBox.AddGestureDetector(_pinchGestureDetector); dialog.AddChildLast(_imageBox); dialog.AddChildLast(_label); Sce.Pss.HighLevel.UI.Scene scene = new Sce.Pss.HighLevel.UI.Scene(); scene.RootWidget.AddChildLast(dialog); UISystem.SetScene(scene,new SlideTransition(2000.0f,FourWayDirection.Left,MoveTarget.NextScene,SlideTransitionInterpolator.Linear)); while(!quit) { _graphics.SetClearColor(255,255,255,0); _graphics.Clear(); if((GamePad.GetData(0).Buttons & GamePadButtons.Cross) == GamePadButtons.Cross) { //Reset _imageBox.SetSize(_origWidth,_origHeight); _imageBox.SetPosition(_origX,_origY); } if((GamePad.GetData(0).Buttons & GamePadButtons.Triangle) == GamePadButtons.Triangle) { quit = true; } UISystem.Update(Touch.GetData(0)); UISystem.Render(); _graphics.SwapBuffers(); } } } }

 

 

First we create our Graphics context and initialize our UISystem, then create a Panel named dialog that is going to be our active window.  We then create a label for output purposes, as well as an ImageBox that is going to contain our Jet sprite ( oh yeah… add an image to your project Smile I am reusing the Jet sprite from the last tutorial but you can use anything ).  The Image property of our ImageBox contains an ImageAsset, which we construct by simply passing it the filename of the graphic we want to use.  Next we store the original location of the _imageBox, so we can revert back to defaults later if desired.

 

The first gesture detector we are going to look at is the FlickGestureDetector.  This controls rapidly sliding your finger in one direction on the screen… think Angry birds.  The heart of the flick handling is the FlickDetected delegate, repeated here:

 

_flickGestureDetector.FlickDetected += delegate(object sender, FlickEventArgs e) { if(e.Speed.X < 0) // Left new MoveEffect(_imageBox,800f,0f,_imageBox.Y,MoveEffectInterpolator.Elastic).Start (); else new MoveEffect(_imageBox,800f,dialog.Width,_imageBox.Y,MoveEffectInterpolator.Elastic).Start (); _label.Text = "Flicked"; };

 

All we are doing here is detecting if the speed is a positive or negative value.  If it is a negative value, it means we are flicking right to left, otherwise it is left to right.  Obviously if you were interested in velocity, you would take the actual value into account.  We indicated with the line:

    _flickGestureDetector.Direction = FlickDirection.Horizontal;

that we are only interested in flicking along the horizontal axis.  Therefore any vertical flicks are going to be ignored. 

 

The direction simply determines what the Y coordinate is going to be in our MoveEffect call, either 0 ( far left ) or dialog.Width ( far right ).  MoveEffect causes the passed widget ( _imageBox ), to be moved over time (800ms in this case) to the passed coordinates.  The MoveEffectInterpolator determines how the move is going to happen, we choose Elastic which will cause it to have a bit of a spring effect.  For no effect at all, choose the Linear interpolator.  Essentially, if we flick left, we send the jet to the left side of the screen, while if we flick right, we send the jet to the right side of the screen.

 

Next up is the DragGestureDetector.  Dragging is when you tap, hold, then move the widget.  If we detect a drag, we simply want to translate our _imageBox location to the position the user dragged to.  We accomplish this with this code:

 

_dragGestureDetector = new DragGestureDetector(); _dragGestureDetector.DragDetected += delegate(object sender, DragEventArgs e) { var newPos = new Vector2(e.Source.X,e.Source.Y) + e.Distance; e.Source.SetPosition(newPos.X,newPos.Y); _label.Text = "Dragged"; };

 

As you can see, the various Gesture detectors use remarkably similar handlers.  In this case all we do is get the current position of the dragged widget ( the value in e.Source will be the widget being acted upon ) and add the distance it’s been dragged.

 

Now we add a TapGestureDetector.  In the event of a tap, we simply want to relocated the sprite back to the center of the screen.  Here is the code:

 

_tapGestureDector = new TapGestureDetector(); _tapGestureDector.TapDetected += delegate(object sender, TapEventArgs e) { e.Source.SetPosition(dialog.Width/2,dialog.Height/2); _label.Text = "Tapped"; };

 

Nothing really new there.  Again, e.Source represents the Widget object that is being tapped.  When a tap occurs we simply set it’s location to the center of the screen and log the tapped activity.

 

Next up is the LongPressGestureDetector.  A long press is a tap, followed by a hold for a given threshold of time, in our case 3000 milliseconds.  Once the long press occurs and hits the threshold, the delegate is fired.

 

_longPressGestureDetector = new LongPressGestureDetector(); _longPressGestureDetector.MinPressDuration = 3000f; _longPressGestureDetector.LongPressDetected += delegate(object sender, LongPressEventArgs e) { MessageDialog.CreateAndShow(MessageDialogStyle.Ok,"Press results", "Hey, let go of me! " + (e.ElapsedTime/1000).ToString() + " seconds elapsed!"); _label.Text = "Long pressed"; };

 

In the delegate, all we do is pop up a MessageDialog window.  There are two ways of working with MessageDialog, you can create one directly and then show it, or like we’ve done here, you can use the static CreateAndShow method.

 

Finally we have the PinchGesture Detector.  A pinch is a two finger concurrent tap, followed by either sliding your fingers closer together, or further apart.  Ultimately the value of most importance is the e.Scale, which determines if they pinched larger ( out ) or smaller ( in ) and how much.  By multiplying the e.Scale value against the widget’s current size, it will either scale larger or smaller.

 

_pinchGestureDetector.PinchDetected += delegate(object sender, PinchEventArgs e) { Vector2 newSize = new Vector2(e.Source.Width * e.Scale,e.Source.Height * e.Scale); e.Source.SetSize(newSize.X,newSize.Y); _label.Text = "Pinched"; };

 

One really confusing thing I ran into is the PinchGestureDetector’s PinchEndDetector is never fired!  Be aware of this; although truth is, you don’t really need it in the end.  As I mentioned at the beginning, there is also a DoubleTapGestureDetector, but it doesn’t work.

 

Finally, we wire all of these GestureDetectors to our ImageBox widget using this code:

_imageBox.AddGestureDetector(_flickGestureDetector); _imageBox.AddGestureDetector(_dragGestureDetector); _imageBox.AddGestureDetector(_tapGestureDector); _imageBox.AddGestureDetector(_longPressGestureDetector); _imageBox.AddGestureDetector(_pinchGestureDetector);

 

You can have multiple widgets using the same gestures, and as you can see in this example, a single widget can have multiple detectors attached.  The remaining code is all stuff we have covered in prior tutorials.

 

Here is the result in action.  Since the Simulator doesn’t support multitouch, I took a video of my Vita in action.

 

Pinching, zooming, tapping, dragging and pressing on the PlayStation Vita with the PSSDK

 

 

Once again, you can download the full project here.

Programming


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


AppGameKit Studio

See More Tutorials on DevGa.me!

Month List