A complete PlayStation Vita game from scratch: Summary

 

This post is a simple summary of all the code used in the complete Vita game tutorial series, all on a single page.  Additionally, you can download the complete project here.

 

Here is the end result of all of this code in action:

Vita pong!

 

 

And here is the code:

 

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());                          }      }  }

 

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 ();          }      }  }   

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()          {                        }      }  }

GameScene.cs

using System;  using Sce.PlayStation.Core;  using Sce.PlayStation.HighLevel.GameEngine2D;  using Sce.PlayStation.HighLevel.GameEngine2D.Base;  using Sce.PlayStation.HighLevel.Physics2D;  using Sce.PlayStation.Core.Audio;    namespace Pong  {      public class GameScene : Scene      {      private Paddle _player,_ai;      public static Ball ball;      private PongPhysics _physics;      private Scoreboard _scoreboard;      private SoundPlayer _pongBlipSoundPlayer;      private Sound _pongSound;                    // Change the following value to true if you want bounding boxes to be rendered          private static Boolean DEBUG_BOUNDINGBOXS = false;                    public GameScene ()          {              this.Camera.SetViewFromViewport();              _physics = new PongPhysics();                              ball = new Ball(_physics.SceneBodies[(int)PongPhysics.BODIES.Ball]);              _player = new Paddle(Paddle.PaddleType.PLAYER,                                    _physics.SceneBodies[(int)PongPhysics.BODIES.Player]);              _ai = new Paddle(Paddle.PaddleType.AI,                                _physics.SceneBodies[(int)PongPhysics.BODIES.Ai]);              _scoreboard = new Scoreboard();                            this.AddChild(_scoreboard);              this.AddChild(ball);              this.AddChild(_player);              this.AddChild(_ai);                                          // This is debug routine that will draw the physics bounding box around the players paddle              if(DEBUG_BOUNDINGBOXS)              {                  this.AdHocDraw += () => {                      var bottomLeftPlayer = _physics.SceneBodies[(int)PongPhysics.BODIES.Player].AabbMin;                      var topRightPlayer = _physics.SceneBodies[(int)PongPhysics.BODIES.Player].AabbMax;                      Director.Instance.DrawHelpers.DrawBounds2Fill(                          new Bounds2(bottomLeftPlayer*PongPhysics.PtoM,topRightPlayer*PongPhysics.PtoM));                        var bottomLeftAi = _physics.SceneBodies[(int)PongPhysics.BODIES.Ai].AabbMin;                      var topRightAi = _physics.SceneBodies[(int)PongPhysics.BODIES.Ai].AabbMax;                      Director.Instance.DrawHelpers.DrawBounds2Fill(                          new Bounds2(bottomLeftAi*PongPhysics.PtoM,topRightAi*PongPhysics.PtoM));                        var bottomLeftBall = _physics.SceneBodies[(int)PongPhysics.BODIES.Ball].AabbMin;                      var topRightBall = _physics.SceneBodies[(int)PongPhysics.BODIES.Ball].AabbMax;                      Director.Instance.DrawHelpers.DrawBounds2Fill(                          new Bounds2(bottomLeftBall*PongPhysics.PtoM,topRightBall*PongPhysics.PtoM));                  };              }                            //Now load the sound fx and create a player              _pongSound = new Sound("/Application/audio/pongblip.wav");              _pongBlipSoundPlayer = _pongSound.CreatePlayer();                            Scheduler.Instance.ScheduleUpdateForTarget(this,0,false);          }                    private void ResetBall()          {              //Move ball to screen center and release in a random directory              _physics.SceneBodies[(int)PongPhysics.BODIES.Ball].Position =                   new Vector2(Director.Instance.GL.Context.GetViewport().Width/2,                              Director.Instance.GL.Context.GetViewport().Height/2) / PongPhysics.PtoM;                            System.Random rand = new System.Random();              float angle = (float)rand.Next(0,360);                        if((angle%90) <=15) angle +=15.0f;                        _physics.SceneBodies[(int)PongPhysics.BODIES.Ball].Velocity =                   new Vector2(0.0f,5.0f).Rotate(PhysicsUtility.GetRadian(angle));          }                    public override void Update (float dt)          {              base.Update (dt);                            if(Input2.GamePad0.Select.Press)                  Director.Instance.ReplaceScene(new MenuScene());                            //We don't need these, but sadly, the Simulate call does.              Vector2 dummy1 = new Vector2();              Vector2 dummy2 = new Vector2();                            //Update the physics simulation              _physics.Simulate(-1,ref dummy1,ref dummy2);                            //Now check if the ball it either paddle, and if so, play the sound              if(_physics.QueryContact((uint)PongPhysics.BODIES.Ball,(uint)PongPhysics.BODIES.Player) ||                  _physics.QueryContact((uint)PongPhysics.BODIES.Ball,(uint)PongPhysics.BODIES.Ai))              {                  if(_pongBlipSoundPlayer.Status == SoundStatus.Stopped)                      _pongBlipSoundPlayer.Play();              }                            //Check if the ball went off the top or bottom of the screen and update score accordingly              Results result = Results.StillPlaying;              bool scored = false;                            if(ball.Position.Y > Director.Instance.GL.Context.GetViewport().Height + ball.Scale.Y/2)              {                  result = _scoreboard.AddScore(true);                  scored = true;              }              if(ball.Position.Y < 0 - ball.Scale.Y/2)              {                  result =_scoreboard.AddScore(false);                  scored = true;              }                            // Did someone win?  If so, show the GameOver scene              if(result == Results.AiWin)                   Director.Instance.ReplaceScene(new GameOverScene(false));              if(result == Results.PlayerWin)                   Director.Instance.ReplaceScene(new GameOverScene(true));                            //If someone did score, but game isn't over, reset the ball position to the middle of the screen              if(scored == true)              {                  ResetBall ();              }                            //Finally a sanity check to make sure the ball didn't leave the field.              var ballPB = _physics.SceneBodies[(int)PongPhysics.BODIES.Ball];                            if(ballPB.Position.X < -(ball.Scale.X/2f)/PongPhysics.PtoM ||                 ballPB.Position.X > (Director.Instance.GL.Context.GetViewport().Width)/PongPhysics.PtoM)              {                  ResetBall();              }          }                    ~GameScene(){              _pongBlipSoundPlayer.Dispose();          }      }  }

Ball.cs

using System;  using Sce.PlayStation.Core;  using Sce.PlayStation.Core.Graphics;  using Sce.PlayStation.HighLevel.GameEngine2D;  using Sce.PlayStation.HighLevel.GameEngine2D.Base;  using Sce.PlayStation.HighLevel.Physics2D;    namespace Pong  {      public class Ball : SpriteUV      {          private PhysicsBody _physicsBody;          // Change this value to make the game faster or slower          public const float BALL_VELOCITY = 5.0f;                    public Ball (PhysicsBody physicsBody)          {              _physicsBody = physicsBody;                            this.TextureInfo = new TextureInfo(new Texture2D("Application/images/ball.png",false));              this.Scale = this.TextureInfo.TextureSizef;              this.Pivot = new Sce.PlayStation.Core.Vector2(0.5f,0.5f);              this.Position = new Sce.PlayStation.Core.Vector2(                  Director.Instance.GL.Context.GetViewport().Width/2 -Scale.X/2,                  Director.Instance.GL.Context.GetViewport().Height/2 -Scale.Y/2);                                          //Right angles are exceedingly boring, so make sure we dont start on one              //So if our Random angle is between 90 +- 25 degrees or 270 +- 25 degrees              //we add 25 degree to value, ie, making 90 into 115 instead              System.Random rand = new System.Random();              float angle = (float)rand.Next(0,360);                        if((angle%90) <=25) angle +=25.0f;              this._physicsBody.Velocity = new Vector2(0.0f,BALL_VELOCITY).Rotate(PhysicsUtility.GetRadian(angle));;                            Scheduler.Instance.ScheduleUpdateForTarget(this,0,false);          }                    public override void Update (float dt)          {                this.Position = _physicsBody.Position * PongPhysics.PtoM;                              // We want to prevent situations where the balls is bouncing side to side              // so if there isnt a certain amount of movement on the Y axis, set it to + or - 0.2 randomly              // Note, this can result in the ball bouncing "back", as in it comes from the top of the screen              // But riccochets back up at the user.  Frankly, this keeps things interesting imho              var normalizedVel = _physicsBody.Velocity.Normalize();              if(System.Math.Abs (normalizedVel.Y) < 0.2f)               {                  System.Random rand = new System.Random();                  if(rand.Next (0,1) == 0)                      normalizedVel.Y+= 0.2f;                                    else                      normalizedVel.Y-= 0.2f;              }                            // Pong is a mess with physics, so just fix the ball velocity              // Otherwise the ball could get faster and faster ( or slower ) on each collision              _physicsBody.Velocity = normalizedVel * BALL_VELOCITY;            }                    ~Ball()          {              this.TextureInfo.Texture.Dispose();              this.TextureInfo.Dispose();          }      }  }

Paddle.cs

using System;  using Sce.PlayStation.Core;  using Sce.PlayStation.Core.Graphics;  using Sce.PlayStation.HighLevel.GameEngine2D;  using Sce.PlayStation.HighLevel.GameEngine2D.Base;  using Sce.PlayStation.HighLevel.Physics2D;    namespace Pong  {      public class Paddle : SpriteUV      {          public enum PaddleType { PLAYER, AI };                    private PaddleType _type;          private PhysicsBody _physicsBody;          private float _fixedY;                    public Paddle (PaddleType type, PhysicsBody physicsBody)          {              _physicsBody = physicsBody;              _type = type;                this.TextureInfo = new TextureInfo(new Texture2D("Application/images/Paddle.png",false));              this.Scale = this.TextureInfo.TextureSizef;              this.Pivot = new Sce.PlayStation.Core.Vector2(0.5f,0.5f);                            if(_type== PaddleType.AI)              {                  this.Position = new Sce.PlayStation.Core.Vector2(                      Director.Instance.GL.Context.GetViewport().Width/2 - this.Scale.X/2,                      10 + this.Scale.Y/2);                                  }              else              {                  this.Position = new Sce.PlayStation.Core.Vector2(                      Director.Instance.GL.Context.GetViewport().Width/2 - this.Scale.X/2,                      Director.Instance.GL.Context.GetViewport().Height - this.Scale.Y/2 - 10);              }                            // Cache the starting Y position, so we can reset and prevent any vertical movement from the Physics Engien              _fixedY = _physicsBody.Position.Y;                            // Start with a minor amount of movement              _physicsBody.Force = new Vector2(-10.0f,0);                            Scheduler.Instance.ScheduleUpdateForTarget(this,0,false);          }                    // This method will fix the physics bounding box to the sprites current position          // Not currently used, was used for debug, left in for interest sake only          private void ClampBoundingBox()          {              var bbBL = new Vector2(Position.X- Scale.X/2, Position.Y- Scale.Y/2) / PongPhysics.PtoM;              var bbTR = new Vector2(Position.X+ Scale.X/2, Position.Y+ Scale.Y/2) / PongPhysics.PtoM;              _physicsBody.AabbMin = bbBL;              _physicsBody.AabbMax = bbTR;                        }          public override void Update (float dt)          {              // Reset rotation to prevent "spinning" on collision              _physicsBody.Rotation = 0.0f;                                          if(_type == PaddleType.PLAYER)              {                  if(Input2.GamePad0.Left.Down)                  {                      _physicsBody.Force = new Vector2(-30.0f,0.0f);                  }                  if(Input2.GamePad0.Right.Down)                  {                      _physicsBody.Force = new Vector2(30.0f,0.0f);                  }              }              else if(_type == PaddleType.AI)              {                  if(System.Math.Abs (GameScene.ball.Position.X - this.Position.X) <= this.Scale.Y/2)                      _physicsBody.Force = new Vector2(0.0f,0.0f);                  else if(GameScene.ball.Position.X < this.Position.X)                      _physicsBody.Force = new Vector2(-20.0f,0.0f);                  else if(GameScene.ball.Position.X > this.Position.X)                      _physicsBody.Force = new Vector2(20.0f,0.0f);              }                            //Prevent vertical movement on collision.  Could also implement by making paddle Kinematic              //However, lose ability to use Force in that case and have to use AngularVelocity instead              //which results in more logic in keeping the AI less "twitchy", a common Pong problem              if(_physicsBody.Position.Y != _fixedY)                  _physicsBody.Position = new Vector2(_physicsBody.Position.X,_fixedY);                            this.Position = _physicsBody.Position * PongPhysics.PtoM;          }                    ~Paddle()          {              this.TextureInfo.Texture.Dispose ();              this.TextureInfo.Dispose();          }      }  }

Scoreboard.cs

using System;    using Sce.PlayStation.Core.Imaging;  using Sce.PlayStation.Core.Graphics;  using Sce.PlayStation.Core;  using Sce.PlayStation.HighLevel.GameEngine2D;  using Sce.PlayStation.HighLevel.GameEngine2D.Base;    namespace Pong  {      public enum Results { PlayerWin, AiWin, StillPlaying };            public class Scoreboard : SpriteUV      {          public int playerScore = 0;          public int aiScore = 0;                    public Scoreboard ()          {              this.TextureInfo = new TextureInfo();              UpdateImage();                            this.Scale = this.TextureInfo.TextureSizef;              this.Pivot = new Vector2(0.5f,0.5f);              this.Position = new Vector2(Director.Instance.GL.Context.GetViewport().Width/2,                                          Director.Instance.GL.Context.GetViewport().Height/2);                        }                    private void UpdateImage()          {              Image image = new Image(ImageMode.Rgba,new ImageSize(110,100),new ImageColor(0,0,0,0));              Font font = new Font(FontAlias.System,50,FontStyle.Regular);              image.DrawText(playerScore + " - " + aiScore,new ImageColor(255,255,255,255),font,new ImagePosition(0,0));              image.Decode();                var texture  = new Texture2D(110,100,false,PixelFormat.Rgba);              if(this.TextureInfo.Texture != null)                  this.TextureInfo.Texture.Dispose();              this.TextureInfo.Texture = texture;              texture.SetPixels(0,image.ToBuffer());              font.Dispose();              image.Dispose();          }          public void Clear()          {              playerScore = aiScore = 0;              UpdateImage();          }                    public Results AddScore(bool player)          {              if(player)                  playerScore++;              else                  aiScore++;              if(playerScore > 3) return Results.PlayerWin;              if(aiScore > 3) return Results.AiWin;                            UpdateImage();                return Results.StillPlaying;          }      }  }

GameOver.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 GameOverScene : Scene      {          private TextureInfo _ti;          private Texture2D _texture;                    public GameOverScene (bool win)          {              this.Camera.SetViewFromViewport();              if(win)                  _texture = new Texture2D("Application/images/winner.png",false);              else                  _texture = new Texture2D("Application/images/loser.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);                            Scheduler.Instance.ScheduleUpdateForTarget(this,0,false);                            Touch.GetData(0).Clear();          }                    public override void Update (float dt)          {              base.Update (dt);              int touchCount = Touch.GetData(0).ToArray().Length;              if(touchCount > 0 || Input2.GamePad0.Cross.Press)              {                  Director.Instance.ReplaceScene( new TitleScene());              }          }                    ~GameOverScene()          {              _texture.Dispose();              _ti.Dispose ();          }      }  }

Programming PSSDK PlayStation Mobile


Scroll to Top