Difference between revisions of "Game Programming Class6"
(→Animation Classes) |
(→Homework) |
||
Line 458: | Line 458: | ||
− | |||
− | + | ==Advanced Sprites/Collision== | |
+ | Simple Collision | ||
− | + | Check if two rectangles intersect. By adding a rectangle that represents the area of the sprite it's easy to ask XNA if two rectangles intersect | |
− | + | 3 tutorial on App Hub | |
− | * | + | *http://xbox.create.msdn.com/en-us/education/catalog/tutorial/collision_2d_rectangle |
+ | *http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel | ||
+ | *http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel_transformed | ||
− | Make a | + | ==PerPixelCollision== |
+ | Load both textures into a color array. Make a rectangle of the intersection of the two textures. Check all the pixels in intersection of the color arrays for intersecting pixels. | ||
− | + | The definition for both methods is in the Sprite class (Sprite.cs) | |
+ | |||
+ | ==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 |
Revision as of 03:30, 6 October 2015
Contents
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
<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
- 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); } } }
</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>
Advanced Sprites/Collision
Simple Collision
Check if two rectangles intersect. By adding a rectangle that represents the area of the sprite it's easy to ask XNA if two rectangles intersect
3 tutorial on App Hub
- http://xbox.create.msdn.com/en-us/education/catalog/tutorial/collision_2d_rectangle
- http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel
- http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel_transformed
PerPixelCollision
Load both textures into a color array. Make a rectangle of the intersection of the two textures. Check all the pixels in intersection of the color arrays for intersecting pixels.
The definition for both methods is in the Sprite class (Sprite.cs)
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