Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

10. September 2012

 

The C++ mobile SDK Marmalade just announced release 6.1.

 

I suppose I can’t really refer to it as a mobile SDK any more, as one of the biggest features of 6.1 is the ability to target Windows and Mac OS desktops.

 

Other new features in this release are:

 

  • Target the desktop: With Marmalade 6.1 you can now deliver your apps to current Windows (7, Vista, XP) and Mac OS X platforms, as well as to Android MIPS devices and the latest LG Smart TVs.
  • Full flexibility: With Web Marmalade’s Plugins you can now mix HTML5 with native platform code, providing full flexibility and creativity.
  • Improved: Desktop graphics support, making it easier to develop and test Marmalade titles on a wide variety of desktop hardware.
  • Updated: You can now use floating-point within the graphics pipeline, allowing more intuitive manipulation of 3D assets.

 

 

Marmalade has been used to release a number of big name projects across many devices.  There is a free trial available, with pricing ranging from 149$ a year ( with a Splashscreen ), to 500$ a year ( no splash screen ) or a negotiated commercial license with full support ( including service level agreement ) and private beta access. 

 

One of the major features of the Marmalade SDK is the ability to target iOS without the need for a Mac.  I know there are a ton of small developers out there that want to make games for the iPhone and iPad, but don’t want to drop 1000$+ on an Apple computer.  If this description fits you, Marmalade might be the perfect option!

News , , ,

10. September 2012

 

The venerable Torque3D game engine is going completely open source, with the source code released on GitHub.image Torque was originally used to create the popular Tribes series of game, then was made available for a relatively low fee.  Considering this was ages before Unity or UDK existed, that was pretty revolutionary.

 

If you recently purchased ( this month ) Torque, GarageGames will be offering a complete refund.

 

In their words:

Eleven years ago, The GarageGames founders did an incredibly innovative thing when they sold a full source game engine for $100. We are excited to continue in their footsteps by announcing that we will be releasing Torque 3D as the best open source game technology in the world. Once again, GarageGames will be changing game development.


Why are we doing this?
Nine months ago, we realigned the goals of GarageGames; making Torque 3D available via a permissive open source license is a strategic move towards fulfilling the company vision. Our first goal was to use iTorque to build a new product we call 3 Step Studio. We envisioned a game development tool that requires no programming at all and began to build it. This product is available today for free, but it’s very, very, early in development and we expect to iterate many times before it is a commercially viable product. Our second goal was to build a service division. I’m happy to say that we’ve already booked our first million dollars in service work and we expect the growth trend to continue as we make Torque 3D more accessible. You can visit our services site at
services.garagegames.com. We are very well prepared and staffed to provide support, training, and custom development.

 

Very cool, more choice is always nice, and so is free!

 

 

Not sure the exact date of the code release, it will however be under the MIT permissive license, personally one of my favorites.  ( No GPL like downsides ).  I will be sure to check this out in closer detail once the code is released.

 

Good work Garage Games.

News

9. September 2012

 

In this part of the tutorial we are going to setup our PhsyicsScene, the heart of our physics engine.  A physics engine, in really laymans terms, take a bunch of physics objects, composed of shapes and with defined physical properties like mass, force, elasticity, etc…  and simulates movement in a virtualized world.  You then take the outputs from the physics engine and update your game accordingly.

 

Let’s jump right in to the code:

 

PhysicsScene.cs

 

    using System;
    using Sce.PlayStation.Core;
    using Sce.PlayStation.HighLevel.GameEngine2D;
    using Sce.PlayStation.HighLevel.GameEngine2D.Base;
    using Sce.PlayStation.HighLevel.Physics2D;
    
    namespace Pong
    {
        
        public class PongPhysics : PhysicsScene
        {
            // PixelsToMeters
            public const float PtoM = 50.0f;
            private const float BALLRADIUS = 35.0f/2f; 
            private const float PADDLEWIDTH = 125.0f;
            private const float PADDLEHEIGHT = 38.0f;
            private float _screenWidth;
            private float _screenHeight;
            
            
            public enum BODIES { Ball = 0, Player, Ai, LeftBumper, RightBumper };
    
            
            public PongPhysics ()
            {
                _screenWidth = Director.Instance.GL.Context.GetViewport().Width;
                _screenHeight = Director.Instance.GL.Context.GetViewport().Height;
                
                // turn gravity off
                this.InitScene();
                this.Gravity = new Vector2(0.0f,0.0f);
                
                // Set the screen boundaries + 2m or 100pixel
                this.SceneMin = new Vector2(-100f,-100f) / PtoM;
                this.SceneMax = new Vector2(_screenWidth + 100.0f,_screenHeight + 100.0f) / PtoM;
                
                // And turn the bouncy bouncy on
                this.RestitutionCoeff = 1.0f;
                
                this.NumBody = 5; // Ball, 2 paddles, 2 bumpers
                this.NumShape = 3; // One of each of the above
                
                //create the ball physics object
                this.SceneShapes[0] = new PhysicsShape(PongPhysics.BALLRADIUS/PtoM);
                this.SceneBodies[0] = new PhysicsBody(SceneShapes[0],0.1f);
                this.SceneBodies[0].ShapeIndex = 0;
                this.sceneBodies[0].ColFriction = 0.01f;
                this.SceneBodies[0].Position = new Vector2(_screenWidth/2,_screenHeight/2) / PtoM;
                
                
                //Paddle shape
                Vector2 box = new Vector2(PADDLEWIDTH/2f/PtoM,-PADDLEHEIGHT/2f/PtoM);
                this.SceneShapes[1] = new PhysicsShape(box);
                
                //Player paddle
                this.SceneBodies[1] = new PhysicsBody(SceneShapes[1],1.0f);
                this.SceneBodies[1].Position = new Vector2(_screenWidth/2f,0f+PADDLEHEIGHT/2+ 10f) / PtoM;
                this.SceneBodies[1].Rotation = 0;
                this.SceneBodies[1].ShapeIndex = 1;
                
                //Ai paddle
                this.SceneBodies[2] = new PhysicsBody(SceneShapes[1],1.0f);
                float aiX = ((_screenWidth/2f)/PtoM);
                float aiY = (_screenHeight - PADDLEHEIGHT/2 - 10f)/ PtoM;
                this.SceneBodies[2].Position = new Vector2(aiX,aiY);
                this.SceneBodies[2].Rotation = 0;
                this.SceneBodies[2].ShapeIndex = 1;
                
                //Now a shape for left and right bumpers to keep ball on screen
                this.SceneShapes[2] = new PhysicsShape((new Vector2(1.0f,_screenHeight)) / PtoM);
                
                //Left bumper
                this.SceneBodies[3] = new PhysicsBody(SceneShapes[2],PhysicsUtility.FltMax);
                this.SceneBodies[3].Position = new Vector2(0,_screenHeight/2f) / PtoM;
                this.sceneBodies[3].ShapeIndex = 2;
                this.sceneBodies[3].Rotation = 0;
                this.SceneBodies[3].SetBodyStatic();
                
                //Right bumper
                this.SceneBodies[4] = new PhysicsBody(SceneShapes[2],PhysicsUtility.FltMax);
                this.SceneBodies[4].Position = new Vector2(_screenWidth,_screenHeight/2f) / PtoM;
                this.sceneBodies[4].ShapeIndex = 2;
                this.sceneBodies[4].Rotation = 0;
                this.SceneBodies[4].SetBodyStatic();
            }
        }
    }
    

Once again, let’s take it from the top.

 

// PixelsToMeters
public const float PtoM = 50.0f;
private const float BALLRADIUS = 35.0f/2f; 
private const float PADDLEWIDTH = 125.0f;
private const float PADDLEHEIGHT = 38.0f;
private float _screenWidth;
private float _screenHeight;

Most of these are convenience variables for commonly used dimensions.  The most important concept here is PtoM, which is short hand for Pixels To Meters.  By default, the Physics2D engine measures things in meters and kilograms.  So a velocity of (5,0) is 5 meters per second, and a mass of 1 is 1kg.  This value is used to map from pixels to physics units and back, and is going to be used A LOT.  The value 50.0f was chosen pretty much at random by me.

 

public enum BODIES { Ball = 0, Player, Ai, LeftBumper, RightBumper };

These are just shorthand values for accessing our various physics objects by index value.  Obviously if you changed the order of the PhysicsBodies, you need to update this enum.  This is just to make code that access the physics scene externally a bit more readable and is completely optional.

 

The remaining code is entirely in the constructor, let’s take it in chunks:

_screenWidth = Director.Instance.GL.Context.GetViewport().Width;
_screenHeight = Director.Instance.GL.Context.GetViewport().Height;

// turn gravity off
this.InitScene();
this.Gravity = new Vector2(0.0f,0.0f);

We use the screen width and height a lot, so we cache them in a much shorter variable.  Next we init our PhysicsScene, which simply sets a number of values to defaults and should be called before using the Scene ( calling it later will overwrite all of your settings ).  We then set gravity to (0,0) so there will effectively be none.  By default it’s (0,-9.8), which is 9.8 m/s down the Y axis.

 

// Set the screen boundaries + 2m or 100pixel
this.SceneMin = new Vector2(-100f,-100f) / PtoM;
this.SceneMax = new Vector2(_screenWidth + 100.0f,_screenHeight + 100.0f) / PtoM;
                
// And turn the bouncy bouncy on
this.RestitutionCoeff = 1.0f;
                
this.NumBody = 5; // Ball, 2 paddles, 2 bumpers
this.NumShape = 3; // One of each of the above

By default, the PhysicsScene is from –1000,-1000 to 1000,1000.  Notice how we divided the value by PtoM.  This turns it from pixel coordinates to physics coordinates.  Basically we are setting the scene equal to the size of the screen + 100pixels in all directions.  Once a physics object passes out of this area, it stops updating.

 

Next we set RestituionCoeff, (somewhat short) for Coefficient of Restitution.  Let’s think of it as “bounciness”.  A value of 1.0f means something should rebound at the same force it hit, while a value of 0.0 will mean no bounce at all.  A value over 1.0f will cause a whole lot of bounce.  For some exceptionally odd reason, it is set at the scene level in the Phsyics2D library, instead of at the Body level.  Next we touch on another odd decision in the library, the PhysicsScene contains three pre-allocated arrays for Shapes, Bodies and Joins ( 100 items, 250 items and 150 items in size respectively ).  NumBody and NumShape indicate how many items of each you are going to use.  Make perfectly sure that you get the sizes exactly right or you will have problems.  As you can see, we are going to have 5 SceneBodies and 3 SceneShapes in our scene.

 

//create the ball physics object
this.SceneShapes[0] = new PhysicsShape(PongPhysics.BALLRADIUS/PtoM);
this.SceneBodies[0] = new PhysicsBody(SceneShapes[0],0.1f);
this.SceneBodies[0].ShapeIndex = 0;
this.sceneBodies[0].ColFriction = 0.01f;
this.SceneBodies[0].Position = new Vector2(_screenWidth/2,_screenHeight/2) / PtoM;

First we create a new PhysicsShape at position 0 of the SceneShapes array.  This is going to be for our ball, and is created by specifying the radius of the sphere ( again, adjusted from pixels to physics coordinates ).  We then create a PhysicsBody using the PhysicsShape.  The 0.1f represents the mass, in this case 1/10th kg.  ShapeIndex is the index within the SceneShapes array that this SceneBody uses ( and yes, is overwhelmingly redundant, another design decision I don’t understand).  As we will see shortly, multiple Bodies can use the same Shape.  ColFriction is Collision Friction which you can read more about here.  Finally we position the ball within the physics world to match it’s location in the GameEngine2D world ( we will see shortly ), again, converted using PtoM.

 

//Paddle shape
Vector2 box = new Vector2(PADDLEWIDTH/2f/PtoM,-PADDLEHEIGHT/2f/PtoM);
this.SceneShapes[1] = new PhysicsShape(box);

//Player paddle
this.SceneBodies[1] = new PhysicsBody(SceneShapes[1],1.0f);
this.SceneBodies[1].Position = new Vector2(_screenWidth/2f,0f+PADDLEHEIGHT/2+ 10f) / PtoM;
this.SceneBodies[1].Rotation = 0;
this.SceneBodies[1].ShapeIndex = 1;

//Ai paddle
this.SceneBodies[2] = new PhysicsBody(SceneShapes[1],1.0f);
float aiX = ((_screenWidth/2f)/PtoM);
float aiY = (_screenHeight - PADDLEHEIGHT/2 - 10f)/ PtoM;
this.SceneBodies[2].Position = new Vector2(aiX,aiY);
this.SceneBodies[2].Rotation = 0;
this.SceneBodies[2].ShapeIndex = 1;

 

This code sets up the player and ai paddle physics bodies, this time sharing a single PhysicsShape.  Instead of passing a radius to this one, we pass a pair of vectors representing width an height, relative to the center point ( which coincidentally is explained nowhere and learned from trial and error ).  The only other difference is for the AI paddle, I saved the X and Y values to temporary variables, aiX and aiY.  Why?  Because there is an annoying bug in Expression Evaluator, that simply could not handle _screenWidth/2f.  It would treat both numbers an ints, and was showing ( in watch windows and Expression Evaluator ) the wrong values.  In fact, Expression Evaluation thinks 3.0f/2.0f = 1!   This particular (display) bug cost me much of my sanity!

 

//Now a shape for left and right bumpers to keep ball on screen
this.SceneShapes[2] = new PhysicsShape((new Vector2(1.0f,_screenHeight)) / PtoM);

//Left bumper
this.SceneBodies[3] = new PhysicsBody(SceneShapes[2],PhysicsUtility.FltMax);
this.SceneBodies[3].Position = new Vector2(0,_screenHeight/2f) / PtoM;
this.sceneBodies[3].ShapeIndex = 2;
this.sceneBodies[3].Rotation = 0;
this.SceneBodies[3].SetBodyStatic();

//Right bumper
this.SceneBodies[4] = new PhysicsBody(SceneShapes[2],PhysicsUtility.FltMax);
this.SceneBodies[4].Position = new Vector2(_screenWidth,_screenHeight/2f) / PtoM;
this.sceneBodies[4].ShapeIndex = 2;
this.sceneBodies[4].Rotation = 0;
this.SceneBodies[4].SetBodyStatic();

This code simply creates a pair of “bumpers” for the left and right side of the screen.  Something for the ball to ricochet off of.  The only thing of note is SetBodyStatic(), which tells the physics engine the object isn’t going to move, saving some valuable calculation time.  There are 3 modes in Physics2D, Dynamic ( what are ball and paddles are ), which will be fully calculated by the engine, Kinematic, which respond to direct application of motion, but not environmental effects ( like gravity ) as well as Static, which are completely stationary.  We are sticking with Dynamic because we are going to control paddle movement through the application of Force, something Kinematic doesn’t support.

 

We now have a physics scene, in the next part we will create the actual GameEngine2D scene, GameScene.

 

Programming , , ,

7. September 2012

 

We are now going to create the MenuScene we saw at the end of part one.  This is a very simple two item menu, that will either start the game or return to the title screen.

 

MenuScene.cs

 

using System;
using Sce.PlayStation.Core;
using Sce.PlayStation.Core.Graphics;
using Sce.PlayStation.Core.Input;

using Sce.PlayStation.HighLevel.GameEngine2D;
using Sce.PlayStation.HighLevel.GameEngine2D.Base;

using Sce.PlayStation.HighLevel.UI;

namespace Pong
{
    public class MenuScene : Sce.PlayStation.HighLevel.GameEngine2D.Scene
    {
        private Sce.PlayStation.HighLevel.UI.Scene _uiScene;
        
        public MenuScene ()
        {
            this.Camera.SetViewFromViewport();
            Sce.PlayStation.HighLevel.UI.Panel dialog = new Panel();
            dialog.Width = Director.Instance.GL.Context.GetViewport().Width;
            dialog.Height = Director.Instance.GL.Context.GetViewport().Height;
            
            ImageBox ib = new ImageBox();
            ib.Width = dialog.Width;
            ib.Image = new ImageAsset("/Application/images/title.png",false);
            ib.Height = dialog.Height;
            ib.SetPosition(0.0f,0.0f);
            
            Button buttonUI1 = new Button();
            buttonUI1.Name = "buttonPlay";
            buttonUI1.Text = "Play Game";
            buttonUI1.Width = 300;
            buttonUI1.Height = 50;
            buttonUI1.Alpha = 0.8f;
            buttonUI1.SetPosition(dialog.Width/2 - 150,200.0f);
            buttonUI1.TouchEventReceived += (sender, e) => {
                Director.Instance.ReplaceScene(new GameScene());
            };
            
            Button buttonUI2 = new Button();
            buttonUI2.Name = "buttonMenu";
            buttonUI2.Text = "Main Menu";
            buttonUI2.Width = 300;
            buttonUI2.Height = 50;
            buttonUI2.Alpha = 0.8f;
            buttonUI2.SetPosition(dialog.Width/2 - 150,250.0f);
            buttonUI2.TouchEventReceived += (sender, e) => {
            Director.Instance.ReplaceScene(new TitleScene());
            };        
                
            dialog.AddChildLast(ib);
            dialog.AddChildLast(buttonUI1);
            dialog.AddChildLast(buttonUI2);
            _uiScene = new Sce.PlayStation.HighLevel.UI.Scene();
            _uiScene.RootWidget.AddChildLast(dialog);
            UISystem.SetScene(_uiScene);
            Scheduler.Instance.ScheduleUpdateForTarget(this,0,false);
        }
        public override void Update (float dt)
        {
            base.Update (dt);
            UISystem.Update(Touch.GetData(0));
            
        }
        
        public override void Draw ()
        {
            base.Draw();
            UISystem.Render ();
        }
        
        ~MenuScene()
        {
            
        }
    }
}

 

The vast majority of code is in the constructor, so let’s take a look ( in pieces ):

 

this.Camera.SetViewFromViewport();
Sce.PlayStation.HighLevel.UI.Panel dialog = new Panel();
dialog.Width = Director.Instance.GL.Context.GetViewport().Width;
dialog.Height = Director.Instance.GL.Context.GetViewport().Height;

 

Once again, MenuScene is inherited from Scene, and we start off by configuring the Camera member to set itself to the same dimensions as the screen.  Next we create a Panel object, which is part of the Sce.PlayStation.HighLevel.UI library.  A panel is a container of other controls.  We want ours to be the full width and height of the display.

 

ImageBox ib = new ImageBox();
ib.Width = dialog.Width;
ib.Image = new ImageAsset("/Application/images/title.png",false);
ib.Height = dialog.Height;
ib.SetPosition(0.0f,0.0f);

 

Next we create an ImageBox, and image box is simply a control that displays an image.  We are creating this to give the illusion that our menu is overlain over our title screen ( where in reality, it is a completely different scene ).  We again want the ImageBox to take up the full dimensions of our panel, so we set it’s Width and Height to the same as the panel.  Next we load the image as an ImageAsset.  ImageAsset is similar to Texture2D, but is specific to the UI library.  This is an annoying trend of the whole PlayStation Mobile library, each library has a tendency to reinvent the wheel… take a look some time at how many degrees to radian functions the SDK has!  Finally we position our imagebox at (0,0), which is the bottom left corner of the screen.   The end result, show the same image that we showed during the title screen.

 

Button buttonUI1 = new Button();
buttonUI1.Name = "buttonPlay";
buttonUI1.Text = "Play Game";
buttonUI1.Width = 300;
buttonUI1.Height = 50;
buttonUI1.Alpha = 0.8f;
buttonUI1.SetPosition(dialog.Width/2 - 150,200.0f);
buttonUI1.TouchEventReceived += (sender, e) => {
    Director.Instance.ReplaceScene(new GameScene());
};

Button buttonUI2 = new Button();
buttonUI2.Name = "buttonMenu";
buttonUI2.Text = "Main Menu";
buttonUI2.Width = 300;
buttonUI2.Height = 50;
buttonUI2.Alpha = 0.8f;
buttonUI2.SetPosition(dialog.Width/2 - 150,250.0f);
buttonUI2.TouchEventReceived += (sender, e) => {
Director.Instance.ReplaceScene(new TitleScene());
};        

 

Next we create a pair of buttons, one to play the game, the other to go back to main menu ( ok… this is brutally superfluous, but what kind of menu has just one menu option!!! Keep in mind, some of this is for demonstration only! ).  We set each one to be 300 pixels wide, 50 pixels in height and 20% transparent.  We then center horizontally and on top of each other vertically.  If either button is clicked we change the scene, either going back to the TitleScene, or going to the GameScene, which is the main scene controlling our game.

 

dialog.AddChildLast(ib);
dialog.AddChildLast(buttonUI1);
dialog.AddChildLast(buttonUI2);
_uiScene = new Sce.PlayStation.HighLevel.UI.Scene();
_uiScene.RootWidget.AddChildLast(dialog);
UISystem.SetScene(_uiScene);
Scheduler.Instance.ScheduleUpdateForTarget(this,0,false);

 

We then add the image box and both buttons to our dialog using AddChildLast(), create a UIScene ( which has *NOTHING* to do with GameEngine2D scenes, by the way ) then set our scene’s RootWidget to our newly created panel.  The UIScene itself can only contain a single control, the RootWidget, which in turn is a container of other widgets.  We then set our newly created scene as the active scene of the UISystem singleton with the call SetScene().  As you can see, the format is very similar to GameEngine2D and the Director/Scene relationship.  We then schedule this scene ( as in GameEngine scene… not the UIScene… confused yet?  Yeah, the naming convention is kind of brutal at times ) to receive updates with a call to ScheduleUpdateForTarget, which will result in the Update() method being called each frame.

 

public override void Update (float dt)
{
    base.Update (dt);
    UISystem.Update(Touch.GetData(0));
    
}

 

Each update, we simply update the UISystem with the current touch data.  It is your applications responsibility to feed input to the UI, which is exactly what we are doing here.

 

public override void Draw ()
{
    base.Draw();
    UISystem.Render ();
}

It is also our game’s responsibility to call the UISystem Render() method each frame we want it displayed.  It is very important that we call the scene base class Draw() method before we call the UISystem.Render(), otherwise the GameEngine rendering process will draw overtop the UI.  Instead of making the main menu separate from the title screen, it would have been easy enough to make the UISystem Update and Render calls only when you want to display the menu, giving you the same end result.

 

In the next part, we will implement yet another different thing named Scene… physics.

 

Programming , , ,

7. September 2012

 

In this series we are going to create a complete Pong clone using the PlayStation Mobile SDK.  The main purpose is to show how all the various pieces ( GameEngine2D, Physics2D, Scenes, Audio, etc… ), fit together to create a full game, something the current documentation doesn’t really

The end results of this tutorial series ( minus sound… it has sound too )
cover.  This tutorial series is going to assume you have already gone through the other PlayStation Mobile tutorials on this site, so I may not be going in to detail on areas that have already been covered.

 

 

I am going to present in the following format.  I will specify the filename, then give the complete source code, followed by a nearly line by line explanation of what the source is doing.

 

 

To complete this tutorial, you are going to need to add references to the libraries Sce.PlayStation.HighLevel.GameEngine2D, Sce.PlayStation.HighLevel.Physics2D and Sce.PlayStation.HighLevel.UI. There is a bug in the PlayStation Mobile Studio that causes auto completion of newly added libraries to not work unless a) you reload the project b) you add ( then remove ) another library c) you exit and restart Studio.

 

Let’s jump right in.

 

AppMain.cs

 

using System;
using Sce.PlayStation.HighLevel.UI; 
using Sce.PlayStation.HighLevel.GameEngine2D;

namespace Pong
{
    public class AppMain
    {
        public static void Main (string[] args)
        {
            Director.Initialize();
            UISystem.Initialize(Director.Instance.GL.Context);
            Director.Instance.RunWithScene(new TitleScene());                
        }
    }
}

 

Every C# application has a Main, and as you can see, ours does remarkably little.  Thanks to the Scene based organization of GameEngine2D, it is fairly easy to divide out your application into logical chunks.  Here we initialize the Director and UISystem singletons, create and run a TitleScene scene, to predictably enough, display a title screen.  Let’s look at that now.

 

TitleScene.cs

 

using System;
using Sce.PlayStation.Core;
using Sce.PlayStation.Core.Graphics;
using Sce.PlayStation.Core.Audio;
using Sce.PlayStation.HighLevel.GameEngine2D;
using Sce.PlayStation.HighLevel.GameEngine2D.Base;
using Sce.PlayStation.Core.Input;
 
namespace Pong
{
    public class TitleScene : Scene
    {
        private TextureInfo _ti;
        private Texture2D _texture;
        
        private Bgm _titleSong;
        private BgmPlayer _songPlayer;
        
        public TitleScene ()
        {
            this.Camera.SetViewFromViewport();
            _texture = new Texture2D("Application/images/title.png",false);
            _ti = new TextureInfo(_texture);
            SpriteUV titleScreen = new SpriteUV(_ti);
            titleScreen.Scale = _ti.TextureSizef;
            titleScreen.Pivot = new Vector2(0.5f,0.5f);
            titleScreen.Position = new Vector2(Director.Instance.GL.Context.GetViewport().Width/2,
                                              Director.Instance.GL.Context.GetViewport().Height/2);
            this.AddChild(titleScreen);
            
            Vector4 origColor = titleScreen.Color;
            titleScreen.Color = new Vector4(0,0,0,0);
            var tintAction = new TintTo(origColor,10.0f);
            ActionManager.Instance.AddAction(tintAction,titleScreen);
            tintAction.Run();
            
            _titleSong = new Bgm("/Application/audio/titlesong.mp3");
            
            if(_songPlayer != null)
            _songPlayer.Dispose();
            _songPlayer = _titleSong.CreatePlayer();
            
            Scheduler.Instance.ScheduleUpdateForTarget(this,0,false);

            // Clear any queued clicks so we dont immediately exit if coming in from the menu
            Touch.GetData(0).Clear();
        }
        
        public override void OnEnter ()
        {
            _songPlayer.Loop = true;
            _songPlayer.Play();
        }
        public override void OnExit ()
        {
            base.OnExit ();
            _songPlayer.Stop();
            _songPlayer.Dispose();
            _songPlayer = null;
        }
        
        public override void Update (float dt)
        {
            base.Update (dt);
            var touches = Touch.GetData(0).ToArray();
            if((touches.Length >0 && touches[0].Status == TouchStatus.Down) || Input2.GamePad0.Cross.Press)
            {
                Director.Instance.ReplaceScene(new MenuScene());
            }
        }
    
        ~TitleScene()
        {
            _texture.Dispose();
            _ti.Dispose ();
        }
    }
}
 

 

This code creates a title screen, loops the background music and waits for the user to touch the screen, at which point it displays the main menu scene.  Let’s take a closer look.

 

private TextureInfo _ti;
private Texture2D _texture;
        
private Bgm _titleSong;
private BgmPlayer _songPlayer;

 

First we declare our member variables.  A TextureInfo holds a texture, and important UV information about it, especially if that texture contains multiple sprites.  Texture2D is the texture itself and holds the image pixel data.  Bgm is a class for holding a music file ( Bgm == background music ), while a BgmPlayer is predictably enough, for playing a Bgm file.  It is important to note, all 4 of these classes implement IDisposable, so you need to release them when you are done, or you will leak memory.

 

public TitleScene ()
    {
        this.Camera.SetViewFromViewport();
        _texture = new Texture2D("Application/images/title.png",false);
        _ti = new TextureInfo(_texture);
        SpriteUV titleScreen = new SpriteUV(_ti);
        titleScreen.Scale = _ti.TextureSizef;
        titleScreen.Pivot = new Vector2(0.5f,0.5f);
        titleScreen.Position = new Vector2(Director.Instance.GL.Context.GetViewport().Width/2,
                                       Director.Instance.GL.Context.GetViewport().Height/2);
        this.AddChild(titleScreen);

 

This code is the first half of our constructor, called when the class is created.  First thing we do is calibrate the scene camera ( TitleScene inherited from Scene which has a camera property ) to set it’s dimensions to match the full viewport.  Next we load the texture title.png from the images folder.  By default, all folders and files added to a project will be under the /Application folder when you run.  Note, these files are all read only!  If you need write access, you need to put them in special folders, described elsewhere in the tutorials.  The false in Texture2D’s constructor is to indicate we do not want to generate a mipmap.  ( A mip map is a special multi resolution version of a texture, used for variable levels of detail, and is not applicable to this application ).

 

One we have loaded our texture, we pass it to the constructor TextureInfo.  Again, TextureInfo simply holds a texture and UV information to go with that texture.  We then use the TextureInfo in our constructor to SpriteUV, which is the actual graphic we are going to display on screen.  All of the graphics in our game are going to be SpriteUVs, and our title screen is no exception.  Now that we have created our title screen graphic, we set it’s size equal to the pixel size of the source image, set it’s pivot to it’s middle point, and it’s position to the center of the screen. 

 

Two important concepts here are the Pivot point and Positioning. The pivot is the location your sprite is going to be transformed relative to, by setting it to the middle of the sprite, this makes rotating a heck of a lot easier, but you now have to remember, when your objet is at say (0,0), only a quarter of it will be visible on screen.  So when checking boundaries, you will have to add half the width and/or height to the equation.  Pivot is represented as a pair of x,y coordinates going from 0 to 1.  (0,0) is the bottom left corner of the sprite, while (1,1) would be the top right.  Obviously then, (0.5,0.5) is the middle.

 

The positioning in GameEngine2D starts at the bottom left corner of the screen.  This is taken from Cocos2D, which GameEngine2D is modeled on, which in turn takes it from OpenGL.  If you are used to the origin being at the top left, this can take a bit of getting used to. Director.Instance.GL.Context allows you some access to the underlying OpenGL context, and will be used quite often throughout the application.  The Director singleton does take care of most of the gritty rendering details, so we wont have to get too low level.  Finally AddChild() adds the child to the scene for displaying and updating.

 

 

    Vector4 origColor = titleScreen.Color;
    titleScreen.Color = new Vector4(0,0,0,0);
    var tintAction = new TintTo(origColor,10.0f);
    ActionManager.Instance.AddAction(tintAction,titleScreen);
    tintAction.Run();
        
    _titleSong = new Bgm("/Application/audio/titlesong.mp3");

    if(_songPlayer != null)
        _songPlayer.Dispose();
    _songPlayer = _titleSong.CreatePlayer();
            
    Scheduler.Instance.ScheduleUpdateForTarget(this,0,false);
            
    // Clear any queued clicks so we dont immediately exit if coming in from the menu
    Touch.GetData(0).Clear();
    }

 

This is the second half of our constructor and most of it revolves around an easy special effect.  First we are caching a copy of the titleScreen’s current color value.  We then set the color to black and completely transparent ( color is made up of four numbers, the Red, Green, Blue as well as Alpha ( transparency ) values, ranging from 0 (none) to 255(full)).  Next we create an Action of type TintTo and over a period of 10 seconds, revert our color back to normal.  Finally we register our action using the ActionManager singleton’s AddAction() method, passing in the action to run, as well as the target node to perform the action on.  Think of actions as reusable “threads” of activity to perform various tasks on Nodes.  If you look at the class hierarchy of GameEngine2D, you will discover most of the class are inherited from Node and can thus have actions performed on them.  ( Scene and SpriteUV are both derived from Node ).  Now that our action is created and registered, we simply run it.  The end result of all of this is the title screen graphic will fade from black to full color over a period of 10 seconds.

 

Next up we load our title song from disk in the folder audioAgain, all folders by default are added under the Application folder and are read only.  Bgm files only support the mp3 format.  The only method of importance for a Bgm file is CreatePlayer() which creates a BgmPlayer instance.  A warning up front, the lifecycle of BgmPlayer is extremely odd, there is something going on behind the scenes with these classes that makes dealing with them extremely irritating, which is why you see a Dispose() call on a class that shouldn’t possibly exist.  Next we register our Scene class with the Scheduler singleton.  This singleton is responsible for calling Update each frame.  The end result is that our Update() method will get called each frame ( ideally 60 times a second ).  The second parameter ( 0 ) is used to set the priority that Scheduler should give your classes Update() calls, but it is currently ignored.  The final parameter to ScheduleUpdateForTarget is simply telling Scheduler if we want to start with our updating paused, which we don’t.  The last line is a bit of a hack, but a required one.  Later on, we will return to the TitleScreen from a different direction, and it is possible that if the user double tapped the screen that a tap is queued up to be processed, which would result in the title screen immediately going away.  This line simply clears out all outstanding touch events to prevent this possibility.

 

public override void OnEnter ()
    {
        _songPlayer.Loop = true;
        _songPlayer.Play(); 
    }
    public override void OnExit ()
    {
        base.OnExit ();
        _songPlayer.Stop();
        _songPlayer.Dispose();
        _songPlayer = null;
    }

OnEnter() is called when your Scene is actually displayed.  It is called after the constructor and it is possible for your Scene to potentially be shown and hidden multiple times.  OnExit() is called when your scene is replaced.  We our scene is first displayed, we start our background music playing and we want it to loop.  On exit, we stop and dispose of the sound and _songPlayer.  Again, managing the memory on these classes is tricky, there is something odd behind the scenes.

 

public override void Update (float dt)
{
    base.Update (dt);
    var touches = Touch.GetData(0).ToArray();
    if((touches.Length >0 && touches[0].Status == TouchStatus.Down) || Input2.GamePad0.Cross.Press)
    {
        Director.Instance.ReplaceScene(new MenuScene());
    }
}

 

This is the method that the Scheduler will call every frame.  It is called before rendering occurs.  Dt represents the elapsed time, in seconds, since Update was last called.  All we are doing here is checking to see if any touches have occurred, or if the user pressed the Cross ( X ) gamepad button, in which case we replace the scene with our MenuScene, which we will create shortly.  Basically, update sits around waiting for the user to tap the screen or hit X, in which case it loads and displays the menu.

 

 

~TitleScene()
    {
        _texture.Dispose();
        _ti.Dispose ();
    }

 

Finally out destructor that, which simply disposes of the title screen Texture2D and TextureInfo objects.  We dispose of the BGMPlayer when the scene changes.  The Bgm file is apparently leaked, but really isn’t.  As I said, something odd is going on.  Play around with the lifecycle of BgmPlayer and Bgm and you will see what I mean!

 

 

 

The resources

 

Our game requires a number of media files, the following is a description of each.  You can use mine or create your own, I release all files ( I own ) to the public domain free of constraints.  You need to create a pair of folders in your project ( within PlayStation Mobile Studio ), audio and images.  For each file, be sure to add it the the appropriate sub folder, then right click it in the Solution panel, and set it’s build action to content!  This step is very important, or you may receive errors when you try to run your application.

 

In the end, your project should look like this:

 

image

 

Now the files.  In the final part of this tutorial, I will make the entire project available as a zip file, but you can grab individual resources below.

ball.png

ball

paddle.png

Paddle

 

title.png

title

 

winner.png

winner

 

loser.png

loser

 

 

In the next part we will create the menu scene, making use of the UI library.

 

Programming , ,

Month List

Popular Comments