Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon

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 , ,

5. September 2012

 

In this section we are going to do something we probably shouldn’t… work with the keyboard.  As you will soon see, when dealing with mobiles, this isn’t a particularly smooth journey! Mobile and keyboards go together like peanut butter and hand grenades.

 

As is always the way, let’s jump right in with code:

 

screenWidth = MOAIEnvironment.screenWidth
screenHeight = MOAIEnvironment.screenHeight
print("Starting up on:" .. MOAIEnvironment.osBrand);

if screenWidth == nil then screenWidth =640 end
if screenHeight == nil then screenHeight = 480 end

MOAISim.openWindow("Window",screenWidth,screenHeight)

viewport = MOAIViewport.new()
viewport:setSize(screenWidth,screenHeight)
viewport:setScale(screenWidth,screenHeight)


layer = MOAILayer2D.new()
layer:setViewport(viewport)

MOAIRenderMgr.pushRenderPass(layer)


chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'

font = MOAIFont.new()
font:loadFromTTF('comic.ttf',chars,60,72)


text = MOAITextBox.new()
text:setString('Press a key')
text:setFont(font)
text:setTextSize(60,72)
text:setYFlip(true)
text:setRect(-320,-240,320,240)
text:setAlignment(MOAITextBox.CENTER_JUSTIFY,MOAITextBox.CENTER_JUSTIFY)


if(MOAIInputMgr.device.keyboard) then
    print("Keyboard")
    MOAIInputMgr.device.keyboard:setCallback(
        function(key,down)
            if down==true then
                text:setString(string.char(tostring(key)))

            end
        end
    )
else
    print("No Keyboard")

    if(MOAIEnvironment.osBrand == "iOS")   then
        MOAIKeyboardIOS.showKeyboard()
        MOAIKeyboardIOS.setListener(MOAIKeyboardIOS.EVENT_INPUT,function(start,length,textVal)
            text:setString(textVal);
        end
        )
    else
        print("The keyboard is a lie");
        -- Android, no keyboard support :(
    end
end

layer:insertProp(text)

 

Here is the program in action:

 

image

 

Exciting eh?  Again, we are going to skip over the familiar bits and jump right in to the new stuff.

 

One thing you might notice compared to prior tutorials, the line:

 

print("Starting up on:" .. MOAIEnvironment.osBrand  .. " version:" .. MOAIEnvironment.osVersion)

 

Has changed to

 

print("Starting up on:" .. MOAIEnvironment.osBrand);

 

Why is this?  Well remember when I said it was up to the host to implement the various values in MOAIEnvironment.  Well, I finally got around to testing on iOS and apparently MOAIEnvironment.osVersion isn’t implemented, at least, it isn’t on the Simulator.  As I recommended earlier, never trust these values to exist on all platforms! In production code, be sure to check for Nil.

 

Now the new code, let’s start with:

chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'

font = MOAIFont.new()
font:loadFromTTF('comic.ttf',chars,60,72)

 

The first line is a string of characters representing the individual characters we are going to be using.  If you need additional characters ( such as punctuation ) be sure to add them to this string.  We then create a new MOAIFont object, then load the font from a ttf file, comic.ttf ( I copied from the Windows font directory, you cannot legally redistribute fonts you don’t license! ).  Regardless to where you got it from, be sure to place a ttf file in the same directory as main.lua.  I moai to create the font created at a font height of 60 pixels and at a resolution of 72dpi.

 

Next up:

text = MOAITextBox.new()
text:setString('Press a key')
text:setFont(font)
text:setTextSize(60,72)
text:setYFlip(true)
text:setRect(-320,-240,320,240)
text:setAlignment(MOAITextBox.CENTER_JUSTIFY,MOAITextBox.CENTER_JUSTIFY)

 

Now we are creating a MOAITextBox, which is a bit misleading in name, especially if you have done some prior WinForm or ASP.NET programming.  A MOAITextBox is simply a text area on screen, often referred to as a label or text area in other libraries.  We then set text’s string value to “Press a Key” using the setString() method, set the font to the font we created earlier with a call to setFont(), and set the text size and dpi to match the values we used to create the font.  Next we call setYFlip to invert the font.  For reasons I don’t completely understand, MOAI renders text upside down by default… so this inverts it to the proper position.  Next we position and size the MOAITextBox with a call to setRect, telling it to position centered and use our full 640x480 screen ( again, remembering that coordinates are relative to 0,0 being the middle of the screen ).  Finally we center the text horizontally and vertically within the text area with a call to setAlignment().

 

Now we actually deal with handling keyboard entry:

 

if(MOAIInputMgr.device.keyboard) then
    print("Keyboard")
    MOAIInputMgr.device.keyboard:setCallback(
        function(key,down)
            if down==true then
                text:setString(string.char(tostring(key)))

            end
        end
    )
else

 

Here we are testing to see if a MOAIInputMgr.device.keyboard has been defined.  If this value is assigned, it means we have a keyboard ( currently this means we are running on a PC or Mac host, but don’t expect that to stay true forever ).  If a keyboard is in fact available, we set a callback function to handle key input.  This callback takes the key code ( as a number ) and a boolean, indicating if it was pressed or released and will be called every time a key is pressed.  We check to see if the key was pressed ( as opposed to released ), and if so, we simply convert the key code to an actual character and display it in our text box.

 

However, if there isn’t a keyboard…

 

else
    print("No Keyboard")

    if(MOAIEnvironment.osBrand == "iOS")   then
        MOAIKeyboardIOS.showKeyboard()
        MOAIKeyboardIOS.setListener(MOAIKeyboardIOS.EVENT_INPUT,function(start,length,textVal)
            text:setString(textVal);
        end
        )
    else
        print("The keyboard is a lie");
        -- Android, no keyboard support :(
    end
end

 

First we check to see if we are running on an iOS device.  If we are, we display the on screen keyboard, then set an event listener using MOAIKeyboardIOS listening for EVENT_INPUT events.  We then set the typed value textVal to our text box, which will be the currently typed character.  Otherwise we assume we are running on Android in which case we are…

 

 

Screwed.  Basically.  As of exactly this moment, there is no MOAIKeyboardAndroid available, although one has been developed so it should be available soon.  Until then, you can’t really handle keyboard entry on Android, unless you extend the host yourself.  I will update this guide when Android support is officially added.  You may be thinking to yourself “what about my hardware keyboard, it surely works, right???”.  Actually no.  Alternatives do exist ( there is a GUI package with an onscreen keyboard included ) that we will cover later, but for now at least until Android keyboard support is made publically available, you are kinda screwed.

 

 

Finally, we add our MOAITextBox to the layer with a call to

layer:insertProp(text)

 

Now you are happily traveling along with full keyboard support in your application!  Well, unless of course you have an Android device, in which case you are probably sulking in a corner.

 

 

Programming , , ,

3. September 2012

 

 

Have I ever mentioned how much I hate

 

a) developing for Android

b) using Eclipse

c) developing for Android using Eclipse?

 

 

Well, I do.  So often you spend more time fighting the tools than you do fighting with code, and today was yet another example.

 

I have some Moai code that worked perfectly well, both in the Windows host and on my device.  I made some alterations to the Lua code testing it to work on iOS ( by the way, the process of getting Moai running on iOS is 10000000x times easier than getting it running on Android! ), so other than some scripting changes, I haven't changed a thing.

 

I load up Eclipse click Run and…

 

[2012-09-03 14:54:20 - DeviceMonitor] Failed to start monitoring 84ef7369

[2012-09-03 14:54:20 - DeviceMonitor] Failed to start monitoring 84ef7369

[2012-09-03 14:54:20 - DeviceMonitor] Failed to start monitoring 84ef7369

[2012-09-03 14:54:20 - DeviceMonitor] Failed to start monitoring 84ef7369

[2012-09-03 14:54:20 - DeviceMonitor] Failed to start monitoring 84ef7369

[2012-09-03 14:54:20 - DeviceMonitor] Failed to start monitoring 84ef7369

[2012-09-03 14:54:20 - DeviceMonitor] Failed to start monitoring 84ef7369

 

WTF?

 

So I kill off adb ( adb kill-server ) and restart it ( adb start-server ).  Still no luck.

 

I exit and restart Eclipse.  No luck

 

I reboot my computer and phone.  No luck

 

I switch devices and try a different Android device.  No luck

 

 

Want to know what it is?  USB3.

 

Seriously, ADB doesn’t play well with USB3, or at least Eclipse+ADB don’t play well with USB3.

 

I plug into a different port and everything is just fine.  Well, except a few more gray hairs that is. Sad smile

Totally Off Topic ,

31. August 2012

 

It’s 300+ pages of how-to, tips, tricks and what have you, about all things art.  Ryan Hawkins is an industry veteran with experience working with Blizzard, Pixologic, Vigil Games and more.  Plus, he brought along friends, Tor Frick, John Park as well as people from Blizzard and Naughtyimage Dog.  That’s some pretty heavy caliber talent sharing their art talents.  Topics include sculpting, texturing, concept art, color theory and more.

 

 

Oh yeah, for free.

 

 

It’s a 216MB e-book and, well….

image

 

So, I might recommend you try a torrent instead.

 

Of course, you can read more at this website.  I actually managed to download using the US mirror without issue.

 

Very cool work guys.

Art

30. August 2012

 

In celebration of their 40th anniversary, Atari has re-released a number of their classic games as HTML5 in their newly launched web arcade.  Each of the titles has received a facelift, and the list includes:

  • Asteroids
  • Centipede
  • Combat
  • Lunar Lander
  • Missile Command
  • Pong
  • Super Breakout
  • Yar’s Revenge

 

 

As you can see, the games have received a facelift:

 

Asteroids:

image

 

Yar’s Revenge:

image

 

 

 

The project is a team up between Atari, CreateJS and Microsoft.  The Microsoft connection is Internet Explorer 10, which allows you to view the arcade ad free.  Atari is releasing an SDK for publishing on their arcade, the download and documentation page is currently down, so details are a bit sparse right now.  Their quick start pdf is currently available and gives a glimpse into the process. Presumably the arcade would work on a revenue sharing scheme, but that is just guesswork at the moment.

 

The library used to create all the games is called CreateJS, and is a bundling of HTML5 libraries including:

EaselJS – a HTML5 Canvas library with a Flash like API

TweenJS – a chainable tweening library

SoundJS – a HTML5 audio library

PreLoadJS – an asset loading and caching library

 

Plus the newly added tool, Zoe.  Zoe is a tool that takes SWF Flash animations and generates sprite sheets.

 

 

I look forward to looking in to Atari’s new API once their documentation page is back online.  Atari has also created a GitHub repository to support the project, but it is currently a little sparse.  In their own words:

 

Welcome to the Atari Arcade SDK.

This is the initial release of the SDK, which we hope to evolve over the next few weeks, adding
* more documentation
* examples
* updates

This repository contains
* Atari Arcade SDK classes in scripts/libs
* scripts necessary to run the SDK locally, in scripts/min
* API documentation and a quick start guide in docs/
* A test harness page to bootstrap and launch games

 

 

All told, a pretty cool project.  At the very least, check out the arcade, it’s a great deal of fun.

 

General ,

Month List

Popular Comments