Gameplay trailer
overview
Charlie Sheen's F'ed Up Adventure is a 2D Platformer/Action game created by myself and 5 other programmers: Camron Quadri, James Balduzzi, Charles Petry , Sean McAdam and Jerry Yip (note: six programmers working together usually results in poor artwork, therefore all of the art comes from a wide variety of sources and I take credit for none of it, credit where credit is due). The player takes control of Charlie Sheen, who has envisioned himself as a ninja of justice on his never ending journey to get over the rainbow. Along the way, Charlie battles some of his greatest foes including tigers and rehab nurses while collecting pills and tiger blood. It combines a bizarre atmosphere with tight gameplay to create a truly unique and memorable experience for the player. Prepare for winning.
The game was a programming project at SUNY Stony Brook, where the students were divided into groups to design any program we wanted to make from the ground up. We, of course, decided to make a game (very loosely) based on the Charlie Sheen weirdness that was occurring in early 2011.
I'm very proud of this project to this day. The group's communication, dedication, vision and talent were excellent and have become the standard that I base all of my group projects on.
The game was a programming project at SUNY Stony Brook, where the students were divided into groups to design any program we wanted to make from the ground up. We, of course, decided to make a game (very loosely) based on the Charlie Sheen weirdness that was occurring in early 2011.
I'm very proud of this project to this day. The group's communication, dedication, vision and talent were excellent and have become the standard that I base all of my group projects on.
DEVELOPMENT INFOGame Type: 2D Platformer/Action
Engine: XNA platformer starter kit Team Size: 6 developers Development Time: 8 weeks (150 hours) Playtime: 30-45 minutes |
ResponsibilitiesDesign a game based on the imagined adventures of Charlie Sheen
Build platformer engine from basic XNA framework Program scrolling camera Program enemy classes Overhaul basic player class to have more features (running, shooting, health, power-ups, color overlays, etc) Overhaul level loader to play larger levels with more items Minor collision updates Placement of power-ups |
screenshots
Code
namespace Platformer { ////// This is the main type for your game /// public class PlatformerGame : Microsoft.Xna.Framework.Game { // Resources for drawing. public GraphicsDeviceManager graphics; private SpriteBatch spriteBatch; // Global content. private SpriteFont hudFont; private Texture2D titleScreen; private Texture2D winOverlay; private Texture2D loseOverlay; private Texture2D diedOverlay; private Texture2D titleOverlay; private Texture2D victoryOverlay; private Texture2D comedyOverlay; // Meta-level game state. public static int LevelIndex { get { return levelIndex; } } private static int levelIndex = 0; private Level level; private bool wasContinuePressed; private bool ResetInput = false; bool canClick = true; // When the time remaining is less than the warning time, it blinks on the hud private static readonly TimeSpan WarningTime = TimeSpan.FromSeconds(30); // We store our input states so that we only poll once per frame, // then we use the same input state wherever needed private GamePadState gamePadState; private KeyboardState keyboardState; Boolean[] numKeysDown = new Boolean[10]; private KeyboardState oldKeyboardState; private MouseState mouseState; private TouchCollection touchState; private AccelerometerState accelerometerState; // The number of levels in the Levels directory of our content. We assume that // levels in our content are 0-based and that all numbers under this constant // have a level file present. This allows us to not need to check for the file // or handle exceptions, both of which can add unnecessary time to level loading. private const int numberOfLevels = 3; //Add game states and stuff here public static int Lives { get { return playerLives; } set { playerLives = value; } } private static int playerLives = 5; public static int Score { get { return score; } set { score = value; } } private static int score = 0; private static bool isTitle = true; public bool startup = false; public bool beginning = true; KeyboardState prevState; public const ConsoleColor conColorDefault = ConsoleColor.White; public const ConsoleColor conColorWarning = ConsoleColor.Green; public const ConsoleColor conColorChange = ConsoleColor.Yellow; public const ConsoleColor conColorError = ConsoleColor.Red; public PlatformerGame() { graphics = new GraphicsDeviceManager(this); graphics.PreferredBackBufferHeight = 600; graphics.PreferredBackBufferWidth = 800; System.Console.ForegroundColor = PlatformerGame.conColorDefault; #if DEBUG this.IsMouseVisible = true; #endif Content.RootDirectory = "Content"; #if WINDOWS_PHONE graphics.IsFullScreen = true; TargetElapsedTime = TimeSpan.FromTicks(333333); #endif Accelerometer.Initialize(); } ////// LoadContent will be called once per game and is the place to load /// all of your content. /// protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // Load fonts hudFont = Content.Load("Fonts/Hud"); // Load overlay textures winOverlay = Content.Load ("Overlays/you_win"); loseOverlay = Content.Load ("Overlays/you_lose"); diedOverlay = Content.Load ("Overlays/you_died"); titleOverlay = Content.Load ("Backgrounds/title"); victoryOverlay = Content.Load ("Backgrounds/Victory"); comedyOverlay = Content.Load ("Backgrounds/winnersdontusedrugs"); //Known issue that you get exceptions if you use Media PLayer while connected to your PC //See http://social.msdn.microsoft.com/Forums/en/windowsphone7series/thread/c8a243d2-d360-46b1-96bd-62b1ef268c66 //Which means its impossible to test this from VS. //So we have to catch the exception and throw it away try { MediaPlayer.IsRepeating = true; MediaPlayer.Play(Content.Load ("Sounds/Music")); } catch { } LoadNextLevel(); } /// /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// /// Provides a snapshot of timing values. protected override void Update(GameTime gameTime) { // Handle polling for our input and handling high-level input HandleInput(); if (level.IsPaused == false) { if (startup == false && beginning == false) { // update our level, passing down the GameTime along with all of our input states level.Update(gameTime, keyboardState, gamePadState, touchState, accelerometerState, Window.CurrentOrientation); base.Update(gameTime); } } } private void HandleInput() { if (ResetInput) { ResetInput = false; oldKeyboardState = keyboardState; return; } // get all of our input states oldKeyboardState = keyboardState; keyboardState = Keyboard.GetState(); mouseState = Mouse.GetState(); gamePadState = GamePad.GetState(PlayerIndex.One); touchState = TouchPanel.GetState(); accelerometerState = Accelerometer.GetState(); // Exit the game when back is pressed. if (gamePadState.Buttons.Back == ButtonState.Pressed) Exit(); if (prevState.IsKeyUp(Keys.P) && keyboardState.IsKeyDown(Keys.P)) { if (level.IsPaused == false) { level.IsPaused = true; } else if (level.IsPaused == true) level.IsPaused = false; } bool continuePressed = keyboardState.IsKeyDown(Keys.Space) || gamePadState.IsButtonDown(Buttons.A) || touchState.AnyTouch(); ///num keys if (keyboardState.IsKeyDown(Keys.Z) && beginning) { beginning = false; startup = true; } if (prevState.IsKeyUp(Keys.Space) && keyboardState.IsKeyDown(Keys.Space) && startup && levelIndex == 1) { startup = false; } for (int i = 0; i < 10; i++) { numKeysDown[i] = false; } if (keyboardState.IsKeyDown(Keys.D0)) { numKeysDown[0] = true; } else if (keyboardState.IsKeyDown(Keys.D1)) { numKeysDown[1] = true; } else if (keyboardState.IsKeyDown(Keys.D2)) { numKeysDown[2] = true; } else if (keyboardState.IsKeyDown(Keys.D3)) { numKeysDown[3] = true; } else if (keyboardState.IsKeyDown(Keys.D4)) { numKeysDown[4] = true; } else if (keyboardState.IsKeyDown(Keys.D5)) { numKeysDown[5] = true; } else if (keyboardState.IsKeyDown(Keys.D6)) { numKeysDown[6] = true; } else if (keyboardState.IsKeyDown(Keys.D7)) { numKeysDown[7] = true; } else if (keyboardState.IsKeyDown(Keys.D8)) { numKeysDown[8] = true; } else if (keyboardState.IsKeyDown(Keys.D9)) { numKeysDown[9] = true; } //end num keys // Perform the appropriate action to advance the game and // to get the player back to playing. if (!wasContinuePressed && continuePressed) { if (!level.Player.IsAlive) { if (playerLives > 0) { playerLives--; } else if (playerLives <= 0) { levelIndex = 0; LoadNextLevel(); startup = true; playerLives = 5; score = 0; } System.Console.WriteLine("Lives:{0}", playerLives); level.StartNewLife(); } if (level.ReachedExit) LoadNextLevel(); //else //ReloadCurrentLevel(); } wasContinuePressed = continuePressed; //Reload level (for fast level editing) if ((keyboardState.IsKeyDown(Keys.F5) && (oldKeyboardState.IsKeyUp(Keys.F5)))) { ReloadCurrentLevel(); PlatformerGame.writeChange("Reloaded level from F5 press"); } //Saves the player's postion and reloads the level else if ((keyboardState.IsKeyDown(Keys.F6)&& (oldKeyboardState.IsKeyUp(Keys.F6)))) { Vector2 playerPos = level.Player.Position; ReloadCurrentLevel(); level.Player.Position = playerPos; PlatformerGame.writeChange("Reloaded level (player pos saved) from F6 press"); } //Load maps with Ctrl + levelNum else if ((keyboardState.IsKeyDown(Keys.LeftControl)) || (keyboardState.IsKeyDown(Keys.RightControl))) { int levelNum = -1; if (numKeysDown[0]) { levelNum = 0; } else if (numKeysDown[1]) { levelNum = 1; } else if (numKeysDown[2]) { levelNum = 2; } else if (numKeysDown[3]) { levelNum = 3; } else if (numKeysDown[4]) { levelNum = 4; } else if (numKeysDown[5]) { levelNum = 5; } else if (numKeysDown[6]) { levelNum = 6; } else if (numKeysDown[7]) { levelNum = 7; } else if (numKeysDown[8]) { levelNum = 8; } else if (numKeysDown[9]) { levelNum = 9; } if (levelNum != -1) { PlatformerGame.writeChange("LEVEL: Loading level " + levelNum + " from (Ctrl + " + levelNum + ") input"); ResetInput = true; LoadNextLevel(levelNum); } } //ESC - exit if ((keyboardState.IsKeyDown(Keys.Escape) && (oldKeyboardState.IsKeyUp(Keys.Escape)))) { PlatformerGame.writeChange("EXITING (Esc pressed)"); this.Exit(); } //mouse x,y in title prevState = keyboardState; int worldMouseX = mouseState.X + (int)level.cameraPositionXaxis; int worldMouseY = mouseState.Y + (int)level.cameraPositionYAxis; #if DEBUG Window.Title = String.Format("World[X: {2},Y: {3}] Tile[({4},{5})] Screen[X: {0},Y: {1}] ", mouseState.X, mouseState.Y, //screen worldMouseX, worldMouseY, //world mouseState.X / Tile.Width, mouseState.Y / Tile.Height); //tile blocks (row and colum #) #endif #region objectSpawnClick if ((mouseState.LeftButton == ButtonState.Pressed) && (canClick) ) { bool aNumKeyIsDown = false; for (int i = 0; i < 10; i++) { if (numKeysDown[i] == true) { aNumKeyIsDown = true; break; } } canClick=false; SparseObjectType objToLoad = 0; if (aNumKeyIsDown) { if (numKeysDown[1]) { objToLoad = SparseObjectType.testFlyer; } else if (numKeysDown[2]) { objToLoad = SparseObjectType.testStationary; } else if (numKeysDown[3]) { objToLoad = SparseObjectType.MonsterA; } else if (numKeysDown[4]) { objToLoad = SparseObjectType.MonsterB; } else if (numKeysDown[5]) { objToLoad = SparseObjectType.MonsterC; } else if (numKeysDown[6]) { objToLoad = SparseObjectType.MonsterD; } else if (numKeysDown[7]) { objToLoad = SparseObjectType.testFloatingDroid; } else if (numKeysDown[0]) { level.Player.Position = new Vector2(worldMouseX, worldMouseY); } if (objToLoad != 0) { PlatformerGame.writeChange("Loaded" + objToLoad.ToString() + "@ (" + worldMouseX + "," + worldMouseY + ")"); level.LoadSparseObject((int)objToLoad, worldMouseX, worldMouseY); } } } if (mouseState.LeftButton == ButtonState.Released) { canClick=true; } #endregion } private void LoadNextLevel(int levelNum=-1) { if (levelNum == -1) { // move to the next level levelIndex++; } else levelIndex = levelNum; // Unloads the content for the current level before loading the next one. if (level != null) level.Dispose(); // Load the level. string levelPath = string.Format("Content/Levels/{0}.txt", levelIndex); //Prototype #HACK //string levelPath = "Content/Levels/proto3.txt"; //string levelPath = "Content/Levels/9.txt"; PlatformerGame.writeChange("LEVEL: Loading level: '" + levelPath + "'"); using (Stream fileStream = TitleContainer.OpenStream(levelPath)) level = new Level(Services, fileStream, levelIndex, spriteBatch); } private void ReloadCurrentLevel() { --levelIndex; LoadNextLevel(); } ////// Draws the game from background to foreground. /// /// Provides a snapshot of timing values. protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); level.Draw(gameTime, spriteBatch); DrawHud(); base.Draw(gameTime); } private void DrawHud() { spriteBatch.Begin(); Rectangle titleSafeArea = GraphicsDevice.Viewport.TitleSafeArea; Vector2 hudLocation = new Vector2(titleSafeArea.X, titleSafeArea.Y); Vector2 center = new Vector2(titleSafeArea.X + titleSafeArea.Width / 2.0f, titleSafeArea.Y + titleSafeArea.Height / 2.0f); // Draw score DrawShadowedString(hudFont, "SCORE: " + score.ToString(), hudLocation + new Vector2(0.0f, 0), Color.Yellow); DrawShadowedString(hudFont, "HEALTH: " + level.Player.Health.ToString(), hudLocation + new Vector2(0.0f, 20), Color.Blue); DrawShadowedString(hudFont, "Lives: " + playerLives.ToString(), hudLocation + new Vector2(0.0f, 40), Color.Red); // Determine the status overlay message to show. Texture2D status = null; if (startup) { status = titleOverlay; } if (beginning) { status = comedyOverlay; } if (beginning) { status = comedyOverlay; } if (level.ReachedExit) { if (levelIndex == 0) { status = victoryOverlay; playerLives = 5; score = 0; startup = true; } else status = winOverlay; } if (!level.Player.IsAlive) { status = diedOverlay; } if (status != null) { // Draw status message. Vector2 statusSize = new Vector2(status.Width, status.Height); spriteBatch.Draw(status, center - statusSize / 2, Color.White); } spriteBatch.End(); } private void DrawShadowedString(SpriteFont font, string value, Vector2 position, Color color) { spriteBatch.DrawString(font, value, position + new Vector2(1.0f, 1.0f), Color.Black); spriteBatch.DrawString(font, value, position, color); } public static void write(String str) { System.Console.ForegroundColor = conColorDefault; System.Console.WriteLine(str); } public static void writeWarning(String str) { System.Console.ForegroundColor = conColorWarning; System.Console.WriteLine(str); System.Console.ForegroundColor = conColorDefault; } public static void writeChange(String str) { System.Console.ForegroundColor = conColorChange; System.Console.WriteLine(str); System.Console.ForegroundColor = conColorDefault; } public static void writeError(String str) { System.Console.ForegroundColor = conColorError; System.Console.WriteLine(str); System.Console.ForegroundColor = conColorDefault; } } }
namespace Platformer { ////// Our fearless adventurer! /// class Player : Entity { // Animations private Animation idleAnimation; private Animation runAnimation; private Animation jumpAnimation; private Animation celebrateAnimation; private Animation dieAnimation; public SpriteEffects flip = SpriteEffects.None; private AnimationPlayer sprite; // Sounds private SoundEffect killedSound; private SoundEffect jumpSound; private SoundEffect fallSound; public Listprojectiles = new List (); KeyboardState prevState; KeyboardState prev2; KeyboardState prev3; /* public Level Level { get { return level; } } Level level; */ public bool IsAlive { get { return isAlive; } } bool isAlive; // Physics state /* public Vector2 Position { get { return position; } set { position = value; } } Vector2 position; */ private float previousBottom; public Vector2 Velocity { get { return velocity; } set { velocity = value; } } Vector2 velocity; // Constants for controling horizontal movement private const float MoveAcceleration = 13000.0f; private const float MaxMoveSpeed = 1750.0f; private const float GroundDragFactor = 0.48f; private const float AirDragFactor = 0.58f; private const float LadderXDrag = .48f; private const float MaxClimbSpeed = 1500f; // Constants for controlling vertical movement private const float MaxJumpTime = 0.35f; private const float JumpLaunchVelocity = -3500.0f; private const float GravityAcceleration = 3400.0f; private const float MaxFallSpeed = 550.0f; private const float JumpControlPower = 0.14f; // Input configuration private const float MoveStickScale = 1.0f; private const float AccelerometerScale = 1.5f; private const Buttons JumpButton = Buttons.A; /// /// Gets whether or not the player's feet are on the ground. /// public bool IsOnGround { get { return isOnGround; } } bool isOnGround; public int Health { get { return health; } } public int health; public int MaxHealth { get { return maxHealth; } } private int maxHealth = 100; public float injuredTime; public bool IsInjured { get { return injuredTime > 0.0f; } } public Color color = Color.White; ////// Current user movement input. /// private float movement; private float movementY; // Jumping state private bool isJumping; private bool wasJumping; private float jumpTime; //private Rectangle localBounds; ////// Gets a rectangle which bounds this player in world space. /// public Rectangle BoundingRectangle { get { int left = (int)Math.Round(Position.X - sprite.Origin.X) + localBounds.X; int top = (int)Math.Round(Position.Y - sprite.Origin.Y) + localBounds.Y; return new Rectangle(left, top, localBounds.Width, localBounds.Height); } } private bool isOverlappingLadder = false; private bool isClimbing = false; private bool wasClimbing = false; public bool upRight = false; public bool upLeft = false; public bool downRight = false; public bool downLeft = false; public bool down = false; public bool up = false; public bool isTiger = false; private Color[] tigerFlash = { Color.Yellow, Color.Orange, Color.OrangeRed, Color.DarkOrange, Color.Red, Color.DarkRed, Color.Black }; private float maxTigerTime = 5.0f; public bool IsTiger { get { return tigerTime > 0.0f; } } public float tigerTime; private bool dash; ////// Constructors a new player. /// public Player(Level level, Vector2 position) { this.level = level; LoadContent(); Reset(position); } ////// Loads the player sprite sheet and sounds. /// public void LoadContent() { // Load animated textures. idleAnimation = new Animation(Level.Content.Load("Sprites/Player/Idle"), 0.1f, true); runAnimation = new Animation(Level.Content.Load ("Sprites/Player/Run"), 0.1f, true); jumpAnimation = new Animation(Level.Content.Load ("Sprites/Player/Jump"), 0.1f, false); celebrateAnimation = new Animation(Level.Content.Load ("Sprites/Player/Celebrate"), 0.1f, false); dieAnimation = new Animation(Level.Content.Load ("Sprites/Player/Die"), 0.1f, false); // Calculate bounds within texture size. int width = (int)(idleAnimation.FrameWidth * 0.4); int left = (idleAnimation.FrameWidth - width) / 2; int height = (int)(idleAnimation.FrameWidth * 0.75); int top = idleAnimation.FrameHeight - height; localBounds = new Rectangle(left, top, width, height); // Load sounds. killedSound = Level.Content.Load ("Sounds/PlayerKilled"); jumpSound = Level.Content.Load ("Sounds/PlayerJump"); fallSound = Level.Content.Load ("Sounds/PlayerFall"); } /// /// Resets the player to life. /// /// The position to come to life at. public void Reset(Vector2 position) { Position = position; Velocity = Vector2.Zero; isAlive = true; health = maxHealth; injuredTime = 0.0f; sprite.PlayAnimation(idleAnimation); tigerTime = 0.0f; } public override void Update(GameTime gameTime) { throw new NotImplementedException(); } ////// Handles input, performs physics, and animates the player sprite. /// ////// We pass in all of the input states so that our game is only polling the hardware /// once per frame. We also pass the game's orientation because when using the accelerometer, /// we need to reverse our motion when the orientation is in the LandscapeRight orientation. /// public void Update( GameTime gameTime, KeyboardState keyboardState, GamePadState gamePadState, TouchCollection touchState, AccelerometerState accelState, DisplayOrientation orientation) { GetInput(keyboardState, gamePadState, touchState, accelState, orientation); ApplyPhysics(gameTime); //Time for TIger powerup if (IsTiger) tigerTime = Math.Max(0.0f, tigerTime - (float)gameTime.ElapsedGameTime.TotalSeconds); //Invulnerability time when player is injured if (IsInjured) { injuredTime = Math.Max(0.0f, injuredTime - (float)gameTime.ElapsedGameTime.TotalSeconds); } if (IsAlive && IsOnGround) { if (Math.Abs(Velocity.X) - 0.02f > 0) { sprite.PlayAnimation(runAnimation); } else { sprite.PlayAnimation(idleAnimation); } } // Clear input. movement = 0.0f; movementY = 0.0f; isJumping = false; wasClimbing = isClimbing; isClimbing = false; if (!IsTiger) color = Color.White; else color = Color.Orange; } ////// Gets player horizontal movement and jump commands from input. /// private void GetInput( KeyboardState keyboardState, GamePadState gamePadState, TouchCollection touchState, AccelerometerState accelState, DisplayOrientation orientation) { // Get analog horizontal movement. movement = gamePadState.ThumbSticks.Left.X * MoveStickScale; // Ignore small movements to prevent running in place. if (Math.Abs(movement) < 0.5f) movement = 0.0f; // Move the player with accelerometer if (Math.Abs(accelState.Acceleration.Y) > 0.10f) { // set our movement speed movement = MathHelper.Clamp(-accelState.Acceleration.Y * AccelerometerScale, -1f, 1f); // if we're in the LandscapeLeft orientation, we must reverse our movement if (orientation == DisplayOrientation.LandscapeRight) movement = -movement; } //Used for 8-directional projectile firing if (prevState.IsKeyUp(Keys.Z) && keyboardState.IsKeyDown(Keys.Z)) { level.AddProjectile(Position + new Vector2(0, -idleAnimation.FrameHeight/5), projectiles); } if (keyboardState.IsKeyDown(Keys.Up) && keyboardState.IsKeyDown(Keys.Right)) { upRight = true; } if (keyboardState.IsKeyUp(Keys.Up) || keyboardState.IsKeyUp(Keys.Right)) { upRight = false; } if (keyboardState.IsKeyDown(Keys.Up) && keyboardState.IsKeyDown(Keys.Left)) { upLeft = true; } if (keyboardState.IsKeyUp(Keys.Up) || keyboardState.IsKeyUp(Keys.Left)) { upLeft = false; } if (keyboardState.IsKeyDown(Keys.Down) && keyboardState.IsKeyDown(Keys.Right)) { downRight = true; } if (keyboardState.IsKeyUp(Keys.Down) || keyboardState.IsKeyUp(Keys.Right)) { downRight = false; } if (keyboardState.IsKeyDown(Keys.Down) && keyboardState.IsKeyDown(Keys.Left)) { downLeft = true; } if (keyboardState.IsKeyUp(Keys.Down) || keyboardState.IsKeyUp(Keys.Left)) { downLeft = false; } if (keyboardState.IsKeyDown(Keys.Down)) { down = true; } if (keyboardState.IsKeyUp(Keys.Down)) { down = false; } if (keyboardState.IsKeyDown(Keys.Up)) { up = true; } if (keyboardState.IsKeyUp(Keys.Up)) { up = false; } if (keyboardState.IsKeyDown(Keys.LeftShift) && keyboardState.IsKeyDown(Keys.Right)) { dash = true; } if (keyboardState.IsKeyDown(Keys.LeftShift) && keyboardState.IsKeyDown(Keys.Left)) { dash = true; } if (keyboardState.IsKeyUp(Keys.LeftShift)) { dash = false; } // If any digital horizontal movement input is found, override the analog movement. if (gamePadState.IsButtonDown(Buttons.DPadLeft) || keyboardState.IsKeyDown(Keys.Left) || keyboardState.IsKeyDown(Keys.A)) { movement = -1.0f; } else if (gamePadState.IsButtonDown(Buttons.DPadRight) || keyboardState.IsKeyDown(Keys.Right) || keyboardState.IsKeyDown(Keys.D)) { movement = 1.0f; } //Ladder if (gamePadState.IsButtonDown(Buttons.DPadUp) || keyboardState.IsKeyDown(Keys.Up)) { movementY = -1.0f; } else if (gamePadState.IsButtonDown(Buttons.DPadDown) || keyboardState.IsKeyDown(Keys.Down)) { movementY = 1.0f; } // Check if the player wants to jump. isJumping = gamePadState.IsButtonDown(JumpButton) || keyboardState.IsKeyDown(Keys.Space) || //keyboardState.IsKeyDown(Keys.Up) || keyboardState.IsKeyDown(Keys.W) || touchState.AnyTouch(); //check for ladder climb if (isOverlappingLadder) { if (keyboardState.IsKeyDown(Keys.Up) || keyboardState.IsKeyDown(Keys.Down)) { isClimbing = true; wasClimbing = true; //System.Console.WriteLine("started climbing"); } //Check if climbing on ladder without holding up or down (standing still) else if (wasClimbing) { isClimbing = true; wasClimbing = true; } } if (isClimbing) { /* if (keyboardState.IsKeyDown(Keys.Up)) { } else if ( keyboardState.IsKeyDown(Keys.Down)) { } * */ //movementY; } prev3 = prev2; prev2 = prevState; prevState = keyboardState; } ////// Updates the player's velocity and position based on input, gravity, etc. /// public void ApplyPhysics(GameTime gameTime) { float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds; Vector2 previousPosition = Position; // Base velocity is a combination of horizontal movement control and // acceleration downward due to gravity. if(!dash) velocity.X += movement * MoveAcceleration * elapsed; else velocity.X += movement * MoveAcceleration * 2 * elapsed; //velocity.Y = MathHelper.Clamp(velocity.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed); if (!isClimbing) { velocity.Y = MathHelper.Clamp(velocity.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed); velocity.Y = DoJump(velocity.Y, gameTime); } else { velocity.Y = 0; velocity.Y += movementY * MoveAcceleration * elapsed; //System.Console.WriteLine("###Did ladder Y, isClimbing={0}",isClimbing); } // Apply pseudo-drag horizontally. if (IsOnGround) velocity.X *= GroundDragFactor; else velocity.X *= AirDragFactor; // Prevent the player from running faster than his top speed. velocity.X = MathHelper.Clamp(velocity.X, -MaxMoveSpeed, MaxMoveSpeed); // Apply velocity. Position += velocity * elapsed; Position = new Vector2((float)Math.Round(Position.X), (float)Math.Round(Position.Y)); // If the player is now colliding with the level, separate them. HandleCollisions(); // If the collision stopped us from moving, reset the velocity to zero. if (Position.X == previousPosition.X) velocity.X = 0; if (Position.Y == previousPosition.Y) velocity.Y = 0; } ////// Calculates the Y velocity accounting for jumping and /// animates accordingly. /// ////// During the accent of a jump, the Y velocity is completely /// overridden by a power curve. During the decent, gravity takes /// over. The jump velocity is controlled by the jumpTime field /// which measures time into the accent of the current jump. /// /// /// The player's current velocity along the Y axis. /// ////// A new Y velocity if beginning or continuing a jump. /// Otherwise, the existing Y velocity. /// private float DoJump(float velocityY, GameTime gameTime) { // If the player wants to jump if (isJumping) { // Begin or continue a jump if ((!wasJumping && IsOnGround) || jumpTime > 0.0f) { if (jumpTime == 0.0f) jumpSound.Play(); jumpTime += (float)gameTime.ElapsedGameTime.TotalSeconds; sprite.PlayAnimation(jumpAnimation); } // If we are in the ascent of the jump if (0.0f < jumpTime && jumpTime <= MaxJumpTime) { // Fully override the vertical velocity with a power curve that gives players more control over the top of the jump velocityY = JumpLaunchVelocity * (1.0f - (float)Math.Pow(jumpTime / MaxJumpTime, JumpControlPower)); } else { // Reached the apex of the jump jumpTime = 0.0f; } } else { // Continues not jumping or cancels a jump in progress jumpTime = 0.0f; } wasJumping = isJumping; return velocityY; } ////// Detects and resolves all collisions between the player and his neighboring /// tiles. When a collision is detected, the player is pushed away along one /// axis to prevent overlapping. There is some special logic for the Y axis to /// handle platforms which behave differently depending on direction of movement. /// private void HandleCollisions() { // Get the player's bounding rectangle and find neighboring tiles. Rectangle bounds = BoundingRectangle; int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width); int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1; int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height); int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1; // Reset flag to search for ground collision. isOnGround = false; isOverlappingLadder = false; // For each potentially colliding tile, for (int y = topTile; y <= bottomTile; ++y) { for (int x = leftTile; x <= rightTile; ++x) { // If this tile is collidable, TileCollision collision = Level.GetCollision(x, y); if (collision != TileCollision.Passable) { // Determine collision depth (with direction) and magnitude. Rectangle tileBounds = Level.GetBounds(x, y); Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds); if (depth != Vector2.Zero) { float absDepthX = Math.Abs(depth.X); float absDepthY = Math.Abs(depth.Y); if (collision == TileCollision.Ladder) { isOverlappingLadder = true; } // Resolve the collision along the shallow axis. else if (absDepthY < absDepthX || collision == TileCollision.Platform) { // If we crossed the top of a tile, we are on the ground. if (previousBottom <= tileBounds.Top) isOnGround = true; // Ignore platforms, unless we are on the ground. if (collision == TileCollision.Impassable || IsOnGround) { // Resolve the collision along the Y axis. Position = new Vector2(Position.X, Position.Y + depth.Y); // Perform further collisions with the new bounds. bounds = BoundingRectangle; } } else if (collision == TileCollision.Impassable) // Ignore platforms. { // Resolve the collision along the X axis. Position = new Vector2(Position.X + depth.X, Position.Y); // Perform further collisions with the new bounds. bounds = BoundingRectangle; } } } } } // Save the new bounds bottom. previousBottom = bounds.Bottom; } ////// Called when the player has been killed. /// /// /// The enemy who killed the player. This parameter is null if the player was /// not killed by an enemy (fell into a hole). /// public void OnKilled(Enemy killedBy) { isAlive = false; if (killedBy != null) killedSound.Play(); else fallSound.Play(); sprite.PlayAnimation(dieAnimation); } public void OnKilledByOpponent(Opponent killedBy) { isAlive = false; if (killedBy != null) killedSound.Play(); else fallSound.Play(); sprite.PlayAnimation(dieAnimation); } ////// Called when this player reaches the level's exit. /// public void OnReachedExit() { sprite.PlayAnimation(celebrateAnimation); } ////// Draws the animated player. /// public void Draw(GameTime gameTime, SpriteBatch spriteBatch) { // Flip the sprite to face the way we are moving. if (Velocity.X > 0) flip = SpriteEffects.FlipHorizontally; else if (Velocity.X < 0) flip = SpriteEffects.None; if (IsTiger) { float t = ((float)gameTime.TotalGameTime.TotalSeconds + tigerTime / maxTigerTime) * 20.0f; int colorIndex = (int)t % tigerFlash.Length; color = tigerFlash[colorIndex]; } else if (IsInjured) { color.R=255; color.B = 0; color.G = 0; } else { color = Color.White; } // Draw that sprite. sprite.Draw(gameTime, spriteBatch, Position, flip, color); } } }
namespace Platformer { enum ProjectileType { playerKnife, enemyProjTest1, enemyProjTest2 } class Projectile : Entity { private Entity owner; private bool friendlyToPlayer; private Vector2 origin; Animation projAnim; AnimationPlayer sprite; //Rectangle localBounds; private Vector2 location; //private Vector2 velocity; //int speed; //Level level; Viewport viewport; SpriteBatch batch; int damage; bool movingLeft; //Sound stuff too. Shoot sound. hit sound. //damage? public Vector2 Location { get { return location; } } public bool isFriendly { get { return friendlyToPlayer; } } public bool Active { get { return isActive; } } public bool isActive; public Rectangle BoundingRectangle { get { int left = (int)Math.Round(location.X - origin.X) + localBounds.X; int top = (int)Math.Round(location.Y - origin.Y) + localBounds.Y; return new Rectangle(left, top, localBounds.Width, localBounds.Height); } } //Constructor public Projectile(/*ProjectileType type,*/ Vector2 pos, Entity owner, Viewport port, Level level, bool friendly, int damage) { location = pos; this.owner = owner; viewport = port; this.level = level; isActive = true; friendlyToPlayer = friendly; if (owner == level.Player && level.Player.IsTiger) { this.damage = damage * 2; } else this.damage = damage; if (friendlyToPlayer == true) LoadContentFriendly(); else LoadContentHarmful((Opponent)owner); } public void LoadContentFriendly() { //8-directional projectile velocities, ex. will fire down and right if player has the down and right arrow keys pressed if (level.Player.upRight) { velocity = new Vector2(20, -20); } else if (level.Player.upLeft) { velocity = new Vector2(-20, -20); } else if (level.Player.downRight) { velocity = new Vector2(20, 20); } else if (level.Player.downLeft) { velocity = new Vector2(-20, 20); } else if (level.Player.down) { velocity = new Vector2(0, 20); } else if (level.Player.up) { velocity = new Vector2(0, -20); } else if (level.Player.flip == SpriteEffects.FlipHorizontally) velocity = new Vector2(20, 0); else velocity = new Vector2(-20, 0); projAnim = new Animation(level.Content.Load("Sprites/Machete-sprite"), 0.1f, true); sprite.PlayAnimation(projAnim); origin = new Vector2(projAnim.FrameWidth / 2.0f, projAnim.FrameHeight / 2.0f); // Calculate bounds within texture size. int width = (int)(projAnim.FrameWidth * 0.35); int left = (projAnim.FrameWidth - width) / 2; int height = (int)(projAnim.FrameWidth * 0.7); int top = projAnim.FrameHeight - height; localBounds = new Rectangle(left, top, width, height); } public void LoadContentHarmful(Opponent opponent) { SparseObjectType obType = opponent.obType; if (obType == SparseObjectType.testStationary) { PlatformerGame.writeChange("***enemyshoot 2"); if (opponent.direction == FaceDirection.Right) { velocity = new Vector2(10, 0); movingLeft = false; } else { velocity = new Vector2(-10, 0); movingLeft = true; } projAnim = new Animation(level.Content.Load ("Sprites/enemyShoot2"), 0.1f, true); sprite.PlayAnimation(projAnim); origin = new Vector2(projAnim.FrameWidth / 2.0f, projAnim.FrameHeight / 2.0f); // Calculate bounds within texture size. int width = (int)(projAnim.FrameWidth * 0.35); int left = (projAnim.FrameWidth - width) / 2; int height = (int)(projAnim.FrameWidth * 0.7); int top = projAnim.FrameHeight - height; localBounds = new Rectangle(left, top, width, height); } else if (obType == SparseObjectType.MonsterD) { //PlatformerGame.writeChange("***enemyshoot 2"); if (opponent.direction == FaceDirection.Right) { velocity = new Vector2(10, 0); movingLeft = false; } else { velocity = new Vector2(-10, 0); movingLeft = true; } projAnim = new Animation(level.Content.Load ("Sprites/enemyShoot3"), 0.1f, true); sprite.PlayAnimation(projAnim); origin = new Vector2(projAnim.FrameWidth / 2.0f, projAnim.FrameHeight / 2.0f); // Calculate bounds within texture size. int width = (int)(projAnim.FrameWidth * 0.35); int left = (projAnim.FrameWidth - width) / 2; int height = (int)(projAnim.FrameWidth * 0.7); int top = projAnim.FrameHeight - height; localBounds = new Rectangle(left, top, width, height); } else { if (opponent.direction == FaceDirection.Right) { velocity = new Vector2(10, 0); movingLeft = false; } else { velocity = new Vector2(-10, 0); movingLeft = true; } projAnim = new Animation(level.Content.Load ("Sprites/Enemy-Shoot"), 0.1f, true); sprite.PlayAnimation(projAnim); origin = new Vector2(projAnim.FrameWidth / 2.0f, projAnim.FrameHeight / 2.0f); // Calculate bounds within texture size. int width = (int)(projAnim.FrameWidth * 0.35); int left = (projAnim.FrameWidth - width) / 2; int height = (int)(projAnim.FrameWidth * 0.7); int top = projAnim.FrameHeight - height; localBounds = new Rectangle(left, top, width, height); } } public override void Update(Microsoft.Xna.Framework.GameTime gameTime) { throw new NotImplementedException(); } //Updates the projectiles public void Update() { location += velocity; //Kill projectile if it goes off screen if (location.X + projAnim.FrameWidth / 2 > level.cameraPositionXaxis + viewport.Width || location.X + projAnim.FrameWidth / 2 < level.cameraPositionXaxis) isActive = false; //If player is hit by an enemy projectile and isn't invulnerable, do a small push back on player if (this.BoundingRectangle.Intersects(level.Player.BoundingRectangle) && !level.Player.IsInjured && !friendlyToPlayer && isActive) { if (movingLeft) level.Player.Velocity += new Vector2(-1000, -5000); else level.Player.Velocity += new Vector2(1000, -5000); this.isActive = false; level.Player.injuredTime = 1.0f; level.Player.health -= damage; if (level.Player.health <= 0) { level.Player.health = 0; level.OnPlayerKilledByOpponent((Opponent)owner); } } //Check if player projectiles hit an enemy foreach (Opponent opponent in level.opponents) { if (this.BoundingRectangle.Intersects(opponent.BoundingRectangle) && opponent.IsAlive && friendlyToPlayer) { this.isActive = false; opponent.health -= damage; if (opponent.health <= 0) { PlatformerGame.Score += opponent.Points; opponent.IsAlive = false; } } } //Level collision Rectangle bounds = BoundingRectangle; int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width); int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1; int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height); int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1; Boolean didCollideWithLevel = false; TileCollision collision = Level.GetCollision(rightTile, topTile); //RIGHT if ((collision != TileCollision.Passable) && (collision != TileCollision.Ladder)) { didCollideWithLevel = true; } if (didCollideWithLevel) { velocity.X = 0; velocity.Y = 0; level.removeFromLevel(this); } //LEFT collision = Level.GetCollision(leftTile, topTile); if ((collision != TileCollision.Passable) && (collision != TileCollision.Ladder)) { didCollideWithLevel = true; } if (didCollideWithLevel) { velocity.X = 0; velocity.Y = 0; level.removeFromLevel(this); } //Level col end } public void Draw(GameTime gameTime, SpriteBatch spriteBatch) { if (!movingLeft) { sprite.Draw(gameTime, spriteBatch, location, SpriteEffects.None, Color.White); } else sprite.Draw(gameTime, spriteBatch, location, SpriteEffects.FlipHorizontally, Color.White); } } }
namespace Platformer { enum OppenentFaceDirection { Left = -1, Right = 1, Up = -2, Down = 2 } //Base abstract class for all bad guys. Will replace the "Enemy" class. //Subclass into different opponent types or behaviors. abstract class Opponent : Entity { //protected OppenentType opponentType; public SparseObjectType obType; protected AnimationPlayer sprite; public FaceDirection direction = FaceDirection.Left; //public ListopponentProjectiles = new List (); protected Boolean isAttackingPlayer = false; protected Boolean shouldStopToAttackPlayer = true; protected int Health { get { return health; } } public int health; protected float injuredTime; public bool IsInjured { get { return injuredTime > 0.0f; } } public Rectangle BoundingRectangle { get { int left = (int)Math.Round(Position.X - sprite.Origin.X) + localBounds.X; int top = (int)Math.Round(Position.Y - sprite.Origin.Y) + localBounds.Y; return new Rectangle(left, top, localBounds.Width, localBounds.Height); } } public abstract void Draw(GameTime gameTime, SpriteBatch spriteBatch); public abstract void OnKilled(Projectile killedBy); public int Points { get { return points; } set { points = value; } } public int points;