In this tutorial we are going to manually manage the game loop manually. Additionally we are going to look at the various ways of handling input from the gamepad. This tutorial builds on the code we developed in the previous tutorial. As you may recall, we created a “Hello World” sprite and centered it to the camera. This time, we are going to give the user the ability to control the sprite’s position and size using the gamepad.
Let’s get straight to 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.GameEngine2D; using Sce.Pss.HighLevel.GameEngine2D.Base; using Sce.Pss.Core.Imaging; namespace HelloWorld { public class AppMain { public static void Main (string[] args) { Director.Initialize(); Scene scene = new Scene(); scene.Camera.SetViewFromViewport(); var width = Director.Instance.GL.Context.GetViewport().Width; var height = Director.Instance.GL.Context.GetViewport().Height; Image img = new Image(ImageMode.Rgba,new ImageSize(width,height),new ImageColor(255,0,0,0)); img.DrawText("Hello World", new ImageColor(255,0,0,255), new Font(FontAlias.System,170,FontStyle.Regular), new ImagePosition(0,150)); Texture2D texture = new Texture2D(width,height,false,PixelFormat.Rgba); texture.SetPixels(0,img.ToBuffer()); img.Dispose(); TextureInfo ti = new TextureInfo(); ti.Texture = texture; SpriteUV sprite = new SpriteUV(); sprite.TextureInfo = ti; sprite.Quad.S = ti.TextureSizef; sprite.CenterSprite(); sprite.Position = scene.Camera.CalcBounds().Center; scene.AddChild(sprite); Director.Instance.RunWithScene(scene,true); bool gameOver = false; while(!gameOver) { Sce.Pss.HighLevel.GameEngine2D.Director.Instance.Update(); if(Input2.GamePad.GetData(0).Left.Release) { sprite.Rotate(Sce.Pss.HighLevel.GameEngine2D.Base.Math.Deg2Rad(90)); } if(Input2.GamePad0.Right.Release) { sprite.Rotate(Sce.Pss.HighLevel.GameEngine2D.Base.Math.Deg2Rad(-90)); } if((Sce.Pss.Core.Input.GamePad.GetData(0).Buttons & GamePadButtons.Up) == GamePadButtons.Up) { sprite.Quad.S = new Vector2(sprite.Quad.S.X += 10.0f,sprite.Quad.S.Y += 10.0f); sprite.CenterSprite(); } if((Sce.Pss.Core.Input.GamePad.GetData(0).Buttons & GamePadButtons.Down) == GamePadButtons.Down) { sprite.Quad.S = new Vector2(sprite.Quad.S.X -= 10.0f,sprite.Quad.S.Y -= 10.0f); sprite.CenterSprite(); } if(Input2.GamePad0.Circle.Press == true) gameOver = true; Sce.Pss.HighLevel.GameEngine2D.Director.Instance.Render(); Sce.Pss.HighLevel.GameEngine2D.Director.Instance.GL.Context.SwapBuffers(); Sce.Pss.HighLevel.GameEngine2D.Director.Instance.PostSwap(); } Director.Terminate(); } } }
The top portion of the code is completely unchanged, our new additions start at the line:
Director.Instance.RunWithScene(scene,true);
The key addition here is the second parameter “true”. This bool is telling the Director singleton that we are going to handle the game loop ourselves, this means we need to call 4 methods manually ( described in a moment ). Next up we create a bool gameOver, which is going to control when we exit our game loop. Obviously we don’t want to exit right away, so we default it to false. Speaking of game loops, that’s what the while line does, loops over and over until gameOver is set to true.
Now in each iteration of our loop, there are four methods we have to call, Update(), Render(), GL.Context.SwapBuffer() and PostSwap(). Update() tells the director we have moved on to the next frame, Render() draws the frame, SwapBuffers displays what Render() drew on the backbuffer to the screen (makes it visible) and finally PostSwap() tells the director we’ve finished swapping buffers and it must be called after SwapBuffers(). Those four combined represent a complete game loop, all the rest of the code handles input from the game pad.
Just to make something perfectly clear here, I am using 3 different ways to check for input, *you won’t do this in your code*. I am just doing it to illustrate all of your different options in one example. You should just pick one ( probably Input2 ) and use only it. Lets look at them one at a time.
if(Input2.GamePad.GetData(0).Left.Release) { sprite.Rotate(Sce.Pss.HighLevel.GameEngine2D.Base.Math.Deg2Rad(90)); }
This method is the most likely way you will deal with Input. Input2 is a wrapper around the Input object to make things a bit simpler. GetData takes a parameter telling it which control ( controller 1, controller 2, etc ) you want to poll, returning a GamePadData object, representing the state the controller is in. We are then checking if the “Left” button has been released. In the case Left is released, we then rotate our sprite 90 degrees. Rotate takes an angle value in radians, so we use the Math.Deg2Rad() helper function to convert from degrees to radians. Of course you could have passed the radian value in instead of converting, 1.5709633 is 90 degrees in radians, it’s just a bit harder to look at.
if(Input2.GamePad0.Right.Release) { sprite.Rotate(Sce.Pss.HighLevel.GameEngine2D.Base.Math.Deg2Rad(-90)); }
This if statement is almost identical to the last one, but instead of using GetData(0), we use a handy alias (GamePad0) that represents exactly the same thing. The only other difference is, in this case we are checking to see if the “Right” button has been released, and we are rotating by a negative value ( the other way ) if it is.
if((Sce.Pss.Core.Input.GamePad.GetData(0).Buttons & GamePadButtons.Up) == GamePadButtons.Up) { sprite.Quad.S = new Vector2(sprite.Quad.S.X += 10.0f,sprite.Quad.S.Y += 10.0f); sprite.CenterSprite(); } if((Sce.Pss.Core.Input.GamePad.GetData(0).Buttons & GamePadButtons.Down) == GamePadButtons.Down) { sprite.Quad.S = new Vector2(sprite.Quad.S.X -= 10.0f,sprite.Quad.S.Y -= 10.0f); sprite.CenterSprite(); }
This time we are using Input directly, using Input instead of Input2. As you can see, the results are a bit more “raw”. In this case we have to use bit masking to determine if a given button is pressed and there is no Released option. In this case we are checking for the “Up” and “Down” buttons. In the event that the user is pressing Up or Down, we are modifying the Scale of the quad our hello texture is pasted on. Remember initially Quad.S is equal to the size of the screen in pixels. If we press Up, we scale the image up 10 pixels in size, if we press down, we shrink it by 10 pixels in size.
Finally, we check ( using the Input2 method ), if the user has pressed the Circle button, in which case we set gameOver to true, causing our loop to exit and our program to end.
One thing to notice at this point is how we scaled the sprite. Unlike rotate, we didn’t call a method, instead we modified the Quad.S(cale) property. The actual transformation matrix of a node ( which SpriteUV is derived from ) is actually determined combining the Position, Scale, Skew and Rotation+Angle+RotationNormalize properties. Therefore modifying any of these properties will translate the node accordingly.
Now run our game, if we press left or right, we rotate 90 degrees, while pressing up or down scales the images. Finally press Circle to exit.
One last thing I suppose needs covering… how exactly do you press Left, Right, or Circle on the simulator?
Left directional key
Cursor key: ←
Up directional key
Cursor key: ↑
Right directional key
Cursor key: →
Down directional key
Cursor key: ↓
Square button
Alphabet: A
Triangle button
Alphabet: W
Circle button
Alphabet: D
Cross button
Alphabet: S
SELECT button
Alphabet: Z
START button
Alphabet: X
L button
Alphabet: Q
R button
Alphabet: E
Sadly, you cannot currently emulate the analog sticks using the simulator. Obviously you can on the Vita device.
EDIT: Oops, forgot to include the project source code, which you can download here.