Game Programming Class6

esse quam videri
Revision as of 19:43, 11 March 2015 by Jeff (talk | contribs) (Physics)
Jump to: navigation, search


Portability

Separation of Concerns

Single Responsibility Principle

PacMan Class Simplified and Generalized

<csharp> public enum PacManState { Spawning, Still, Chomping, SuperPacMan }

   public class PacMan 
   {
       protected PacManState _state;
       public PacManState State
       {
           get { return _state; }
           set
           {
               if (_state != value)
               {
                   this.Log(string.Format("{0} was: {1} now {2}", this.ToString(), _state, value));
                   
                   _state = value;
               }
           }
       }
       public PacMan()
       {
           
           //Set default state will call notify so make sure this.Ghosts is intitialized first
           this.State = PacManState.Still;
       }
       //Extra method for logging state change
       public virtual void Log(string s)
       {
           //nothing
           Console.WriteLine(s);
       }
   }

</csharp>

MovementController in Monogame

In MonogameDemos Project is IntroPacManMovementController

file is PacManController.cs

<csharp> sealed class PacManController

   {
       InputHandler input; //game service to handle input
       public Vector2 Direction {get; private set;}
       public float Rotate { get; private set;}
       //Checks to see if there is anymovement
       public bool hasInputForMoverment
       {
           get
           {
               
               if (Direction.Length() == 0) return false;
               return true;
           }
       }
       public PacManController(Game game)
       {
           //get input handler from game services
           input = (InputHandler)game.Services.GetService<IInputHandler>();
           if (input == null)
           {
               throw new Exception("PacMan controller depends on InputHandler service please add Input Handler as a service first");
           }
       }
       public void Update()
       {
           //Input for update from analog stick
           GamePadState gamePad1State = input.GamePads[0]; //HACK hard coded player index
           #region LeftStick
           Vector2 pacStickDir = Vector2.Zero;
           if (gamePad1State.ThumbSticks.Left.Length() > 0.0f)
           {
               pacStickDir = gamePad1State.ThumbSticks.Left;
               pacStickDir.Y *= -1;      //Invert Y Axis
               float RotationAngle = (float)Math.Atan2(
                   gamePad1State.ThumbSticks.Left.X,
                   gamePad1State.ThumbSticks.Left.Y);
               Rotate = (float)MathHelper.ToDegrees(RotationAngle - (float)(Math.PI / 2));
           }
           #endregion
           //Update for input from DPad
           #region DPad
           Vector2 PacManDPadDir = Vector2.Zero;
           if (gamePad1State.DPad.Left == ButtonState.Pressed)
           {
               //Orginal Position is Right so flip X
               PacManDPadDir += new Vector2(-1, 0);
           }
           if (gamePad1State.DPad.Right == ButtonState.Pressed)
           {
               //Original Position is Right
               PacManDPadDir += new Vector2(1, 0);
           }
           if (gamePad1State.DPad.Up == ButtonState.Pressed)
           {
               //Up
               PacManDPadDir += new Vector2(0, -1);
           }
           if (gamePad1State.DPad.Down == ButtonState.Pressed)
           {
               //Down
               PacManDPadDir += new Vector2(0, 1);
           }
           if (PacManDPadDir.Length() > 0)
           {
               //Angle in radians from vector
               float RotationAngleKey = (float)Math.Atan2(
                       PacManDPadDir.X,
                       PacManDPadDir.Y * -1);
               //Find angle in degrees
               Rotate = (float)MathHelper.ToDegrees(
                   RotationAngleKey - (float)(Math.PI / 2)); //rotated right already
               //Normalize NewDir to keep agled movement at same speed as horilontal/Vert
               PacManDPadDir = Vector2.Normalize(PacManDPadDir);
           }
           #endregion
           //Update for input from Keyboard
  1. if !XBOX360
           #region KeyBoard
           KeyboardState keyboardState = Keyboard.GetState();
           Vector2 PacManKeyDir = new Vector2(0, 0);
           if (keyboardState.IsKeyDown(Keys.Left))
           {
               //Flip Horizontal
               PacManKeyDir += new Vector2(-1, 0);
           }
           if (keyboardState.IsKeyDown(Keys.Right))
           {
               //No new sprite Effects
               PacManKeyDir += new Vector2(1, 0);
           }
           if (keyboardState.IsKeyDown(Keys.Up))
           {
               PacManKeyDir += new Vector2(0, -1);
           }
           if (keyboardState.IsKeyDown(Keys.Down))
           {
               PacManKeyDir += new Vector2(0, 1);
           }
           if (PacManKeyDir.Length() > 0)
           {
               float RotationAngleKey = (float)Math.Atan2(
                       PacManKeyDir.X,
                       PacManKeyDir.Y * -1);
               Rotate = (float)MathHelper.ToDegrees(
                   RotationAngleKey - (float)(Math.PI / 2));
               //Normalize NewDir to keep agled movement at same speed as horilontal/Vert
               PacManKeyDir = Vector2.Normalize(PacManKeyDir);
           }
           #endregion
  1. endif
           Direction= PacManKeyDir + PacManDPadDir + pacStickDir;
           if (Direction.Length() > 0)
           {
               Direction = Vector2.Normalize(Direction);
           }
       }
   }

</csharp>

GameConsolePacMan.cs //hadles logging <csharp> class GameConsolePacMan : PacMan

   {
       GameConsole console;
       public GameConsolePacMan()
       {
           this.console = null;
       }
       public GameConsolePacMan(GameConsole console)
       {
           this.console = console;
       }
       public override void Log(string s)
       {
           if (console != null)
           {
               console.GameConsoleWrite(s);
           }
           else
           {
               base.Log(s);
           }
       }
   }

</csharp>

MonoGamePacMan.cs <csharp> class MonoGamePacMan : IntroGameLibrary.Sprite2.DrawableSprite2

   {
       internal PacManController controller { get; private set; }
       internal GameConsolePacMan PacMan
       {
           get;
           private set;
       }
       protected PacManState pacState;
       public PacManState PacState
       {
           get { return this.pacState; }
           //Change pacman state also
           set { 
               if(this.pacState != value)
                   this.pacState = this.PacMan.State = value; 
           }
       }
               
       public MonoGamePacMan(Game game)
           : base(game)
       {
           this.controller = new PacManController(game);
           PacMan = new GameConsolePacMan((GameConsole)game.Services.GetService<IGameConsole>());
       }
       protected override void LoadContent()
       {
           base.LoadContent();
           this.SpriteTexture = content.Load<Texture2D>("PacManSingle");
           this.Orgin = new Vector2(this.SpriteTexture.Width / 2, this.SpriteTexture.Height / 2);
           this.Location = new Microsoft.Xna.Framework.Vector2(100, 100);
           this.Speed = 200;
       }
       public override void Update(GameTime gameTime)
       {
           //Elapsed time since last update
           float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
           
           this.controller.Update();
           this.Location += ((this.controller.Direction * (time / 1000)) * Speed);      //Simple Move 
           this.Rotate = this.controller.Rotate;
           //Change State based on movement
           if (this.controller.hasInputForMoverment)
           {
               if(pacState != PacManState.Spawning && pacState != PacManState.SuperPacMan)
                   this.PacState = PacManState.Chomping;
           }
           else
           {
               if (pacState != PacManState.Spawning && pacState != PacManState.SuperPacMan)
                   this.PacState = PacManState.Still;
           }
           //Keep PacMan On Screen
           if (this.Location.X > Game.GraphicsDevice.Viewport.Width - (this.spriteTexture.Width / 2))
           {
               this.Location.X = Game.GraphicsDevice.Viewport.Width - (this.spriteTexture.Width / 2);
           }
           if (this.Location.X < (this.spriteTexture.Width / 2))
               this.Location.X = (this.spriteTexture.Width / 2);
           if (this.Location.Y > Game.GraphicsDevice.Viewport.Height - (this.spriteTexture.Height / 2))
               this.Location.Y = Game.GraphicsDevice.Viewport.Height - (this.spriteTexture.Height / 2);
           if (this.Location.Y < (this.spriteTexture.Height / 2))
               this.Location.Y = (this.spriteTexture.Height / 2);
           base.Update(gameTime);
       }
   }

</csharp>

MovementController in Unity

Unity Project is GameProgrammingUnity

Package is PacManWithFood.unitypackage

script is PlayerController.cs <csharp>

   public class PlayerController : MonoBehaviour {

public Vector2 direction = new Vector2();

   private Vector2 keyDirection;
   private Vector2 padDirection;

public bool hasInputForMoverment { get {

           //Debug.Log(direction.sqrMagnitude);
           if (direction.magnitude == 0) return false;

return true;} }

   public PlayerController()
   {
       keyDirection = new Vector2();
   }

// Use this for initialization void Start () {

}

// Update is called once per frame void Update () {

       keyDirection.x = keyDirection.y = 0;
       //Keyboard
       if (Input.GetKey("right"))
       {
           keyDirection.x += 1;
       }

if (Input.GetKey ("left")) {

           keyDirection.x += -1;

}

       if (Input.GetKey("up"))
       {
           keyDirection.y += 1;
       }

if (Input.GetKey ("down")) {

           keyDirection.y += -1;

}

       direction = keyDirection;
       //Gamepad
       padDirection.x = Input.GetAxis("Horizontal");
       padDirection.y = Input.GetAxis("Vertical");
       if (padDirection.magnitude > 0)
       {
           //Debug.Log(padDirection + " " + padDirection.magnitude + " " + (padDirection.magnitude > 0));
           direction += padDirection;
       }
       //normalize
       direction.Normalize();

} } </csharp>

UnityPacMan.cs //Hadles logging <csharp> public class UnityPacMan : PacMan {

   private GameObject _gameObject;
   public UnityPacMan(GameObject g) : base()
   {
       _gameObject = g;
   }
   public override void Log(string s)
   {
       Debug.Log(s);
   }

} </csharp>


Player.cs <csharp>

public class Player : MonoBehaviour {

   private PlayerController controller;
   public Vector2 Direction = new Vector2(1, 0);
   public float Speed = 10;
   public float Angle;
   public UnityPacMan PacMan { get; private set; }
   private Vector3 moveTranslation;
   
   // Use this for initialization

void Start () {

       //Get PlayerController from game object

controller = GetComponent<PlayerController>(); //Log error if controller is null will throw null refernece exception eventually if (controller == null) { Debug.LogWarning( "GetComponent of type " + typeof( PlayerController ) + " failed on " + this.name, this ); }

//or //Util.GetComponentIfNull<PlayerController> (this, ref controller);

       PacMan = new UnityPacMan(this.gameObject);

}

// Update is called once per frame void Update () {

       if (this.controller.hasInputForMoverment) 
       {

this.Direction = this.controller.direction; Angle = Mathf.Atan2 (this.Direction.y, this.Direction.x) * Mathf.Rad2Deg; this.transform.eulerAngles = new Vector3 (0, 0, Angle);

           if (this.PacMan.State != PacManState.SuperPacMan)
           {
               this.PacMan.State = PacManState.Chomping;
           }

} else { this.Direction = new Vector2(0,0);

           if (this.PacMan.State != PacManState.SuperPacMan)
           {
               this.PacMan.State = PacManState.Still;
           }

}

this.moveTranslation = new Vector3(this.Direction.x, this.Direction.y) * Time.deltaTime * this.Speed; this.transform.position += new Vector3(this.moveTranslation.x, this.moveTranslation.y);

}

   public virtual void PowerUp()
   {
       this.PacMan.State = PacManState.SuperPacMan;
       this.StartCoroutine("PowerUpTimer");
   }


   IEnumerator PowerUpTimer()
   {
       this.PacMan.Notify("SuperPacMan");
       yield return new WaitForSeconds(3);
       this.PacMan.State = PacManState.Still;
       this.PacMan.Notify("SuperPacMan End");
   }

} </csharp>


Simple Animation

what extra fields do we need for animation?

http://create.msdn.com/en-US/education/catalog/sample/sprite_sheet

Platformer Example

Show platformer starter kit

http://msdn.microsoft.com/en-us/library/dd254918(v=xnagamestudio.31).aspx

simple and elegant but not that flexible

Animation Classes

Way too many classes for me to type here

  • PacMan States
  • PacMan Dying Animation
  • PacMan Eating

IntroAnimation Project IntroAnimation.zip

Complicated but flexible

  • DrawableAnimatableSprite.cs
  • CelAnimationManager.cs

Homework

  • InputControllerMonogame

Update your movement project in MonoGame to have good separation of concern and use an input class with an input controller.

Use the IntroGameLibrary if you like to quickly make drawable sprites

  • InputControllerUnity

Make a similar project to you MonoGame InputController.

Use an Input controller class in Unity to control the sprite. Reuse as much code as possible from the Monogame InputContorller