Difference between revisions of "Game Programming Class8"
(8 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
+ | ==Midterm Game Presentations== | ||
+ | |||
+ | |||
+ | ==Portability== | ||
+ | |||
+ | Separation of Concerns | ||
+ | *https://msdn.microsoft.com/en-us/library/ee658124.aspx | ||
+ | *http://en.wikipedia.org/wiki/Separation_of_concerns | ||
+ | |||
+ | Single Responsibility Principle | ||
+ | *http://en.wikipedia.org/wiki/Single_responsibility_principle | ||
+ | *http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod | ||
+ | |||
+ | ==PacMan Class Simplified and Generalized== | ||
+ | |||
+ | <syntaxhighlight lang="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); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==MovementController in Monogame== | ||
+ | |||
+ | In MonogameDemos Project is IntroPacManMovementController | ||
+ | |||
+ | file is PacManController.cs | ||
+ | |||
+ | <syntaxhighlight lang="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 | ||
+ | #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 | ||
+ | #endif | ||
+ | Direction= PacManKeyDir + PacManDPadDir + pacStickDir; | ||
+ | if (Direction.Length() > 0) | ||
+ | { | ||
+ | Direction = Vector2.Normalize(Direction); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | GameConsolePacMan.cs //hadles logging | ||
+ | <syntaxhighlight lang="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); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | MonoGamePacMan.cs | ||
+ | <syntaxhighlight lang="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); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==MovementController in Unity== | ||
+ | Unity Project is GameProgrammingUnity | ||
+ | |||
+ | Package is PacManWithFood.unitypackage | ||
+ | |||
+ | script is PlayerController.cs | ||
+ | <syntaxhighlight lang="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(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | UnityPacMan.cs //Hadles logging | ||
+ | <syntaxhighlight lang="csharp"> | ||
+ | public class UnityPacMan : PacMan { | ||
+ | |||
+ | private GameObject _gameObject; | ||
+ | |||
+ | public UnityPacMan(GameObject g) : base() | ||
+ | { | ||
+ | _gameObject = g; | ||
+ | } | ||
+ | |||
+ | public override void Log(string s) | ||
+ | { | ||
+ | Debug.Log(s); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | Player.cs | ||
+ | <syntaxhighlight lang="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"); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==Chase and Evade== | ||
+ | |||
+ | Example of simple states and simple vector geometry. | ||
+ | |||
+ | [http://creators.xna.com/en-US/sample/chaseevade Better example not yet implemented] | ||
+ | |||
+ | PacMan Chase and Evade example in repo '''IntroChaseEvade''' demostratea | ||
+ | *Collision | ||
+ | *State for feeble AI | ||
+ | *Sprite Markers | ||
+ | |||
+ | ==Physics== | ||
+ | http://box2d.org/ | ||
+ | |||
+ | c# Box2D https://code.google.com/p/box2dx/ | ||
+ | |||
+ | https://github.com/d-snp/Farseer-Physics | ||
+ | |||
+ | http://farseerphysics.codeplex.com/ | ||
+ | |||
+ | http://www.mataliphysics.com/ | ||
+ | |||
+ | ==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 | ||
+ | |||
+ | ==Monogame samples== | ||
+ | |||
+ | https://github.com/Mono-Game/MonoGame.Samples | ||
+ | |||
+ | ==Strategy Pattern== | ||
+ | |||
+ | Manager classes to implement strategy pattern | ||
+ | |||
+ | http://www.dofactory.com/Patterns/PatternStrategy.aspx | ||
+ | |||
+ | |||
+ | |||
+ | Discuss Final Projects requirements | ||
+ | |||
+ | Version 0 POC | ||
+ | *Temp art | ||
+ | *Test main machanic | ||
+ | |||
+ | Version 1 game ready for internal testing | ||
+ | *Temp art | ||
+ | *First Pass and Game mechanics may not be fully balanced or perfect but is playable | ||
+ | *All Major Game features have been implemented | ||
+ | **Implementations may not be complete but they should be functional | ||
+ | |||
+ | Version 2 game is ready for public testing | ||
+ | *No Temp art | ||
+ | *All Game features are implemented and testable | ||
+ | *Audio | ||
+ | *Animations | ||
+ | |||
+ | After the final games have been tested internally we will revise and then test them externally. | ||
+ | |||
==Sound Manager== | ==Sound Manager== | ||
Line 25: | Line 545: | ||
*Has website for distribution | *Has website for distribution | ||
− | == | + | ==Home Work== |
− | + | Final Game main mechanic POC |
Latest revision as of 19:41, 29 March 2016
Contents
- 1 Midterm Game Presentations
- 2 Portability
- 3 PacMan Class Simplified and Generalized
- 4 MovementController in Monogame
- 5 MovementController in Unity
- 6 Chase and Evade
- 7 Physics
- 8 Platformer Example
- 9 Monogame samples
- 10 Strategy Pattern
- 11 Sound Manager
- 12 Demo Icon
- 13 Demo Packaging
- 14 Requirements for 2D Game
- 15 Home Work
Midterm Game Presentations
Portability
Separation of Concerns
- https://msdn.microsoft.com/en-us/library/ee658124.aspx
- http://en.wikipedia.org/wiki/Separation_of_concerns
Single Responsibility Principle
- http://en.wikipedia.org/wiki/Single_responsibility_principle
- http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
PacMan Class Simplified and Generalized
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);
}
}
MovementController in Monogame
In MonogameDemos Project is IntroPacManMovementController
file is PacManController.cs
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
#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
#endif
Direction= PacManKeyDir + PacManDPadDir + pacStickDir;
if (Direction.Length() > 0)
{
Direction = Vector2.Normalize(Direction);
}
}
}
GameConsolePacMan.cs //hadles logging
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);
}
}
}
MonoGamePacMan.cs
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);
}
}
MovementController in Unity
Unity Project is GameProgrammingUnity
Package is PacManWithFood.unitypackage
script is PlayerController.cs
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();
}
}
UnityPacMan.cs //Hadles logging
public class UnityPacMan : PacMan {
private GameObject _gameObject;
public UnityPacMan(GameObject g) : base()
{
_gameObject = g;
}
public override void Log(string s)
{
Debug.Log(s);
}
}
Player.cs
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");
}
}
Chase and Evade
Example of simple states and simple vector geometry.
Better example not yet implemented
PacMan Chase and Evade example in repo IntroChaseEvade demostratea
- Collision
- State for feeble AI
- Sprite Markers
Physics
c# Box2D https://code.google.com/p/box2dx/
https://github.com/d-snp/Farseer-Physics
http://farseerphysics.codeplex.com/
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
Monogame samples
https://github.com/Mono-Game/MonoGame.Samples
Strategy Pattern
Manager classes to implement strategy pattern
http://www.dofactory.com/Patterns/PatternStrategy.aspx
Discuss Final Projects requirements
Version 0 POC
- Temp art
- Test main machanic
Version 1 game ready for internal testing
- Temp art
- First Pass and Game mechanics may not be fully balanced or perfect but is playable
- All Major Game features have been implemented
- Implementations may not be complete but they should be functional
Version 2 game is ready for public testing
- No Temp art
- All Game features are implemented and testable
- Audio
- Animations
After the final games have been tested internally we will revise and then test them externally.
Sound Manager
SoundManager.cs Sound management class. There are several problems with the sound
How to expose methods to the other classes. Should it be a sevice? Event Model?
- Sound Variables
- Global Variables
- RPC Variables
- Homework
- Midterm Project Due Next Week
Demo Icon
Demo Packaging
Requirements for 2D Game
- Loads and Displays 2D graphics
- Uses Text for Output to player and GameConsole for debugging
- Uses GamePad or Keyboard for input. Uses a game service to handle input from these devices
- Has stateful sprites that animate or change states
- Has some kind of Animation
- Uses Sound
- Has website for distribution
Home Work
Final Game main mechanic POC