using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; using System.Runtime.InteropServices; public class User32 { [DllImport("user32.dll")] public static extern void SetWindowPos(uint hwnd, uint level, int x, int y, int w, int h, uint flags); } namespace trial2 { class Entity { public static Texture2D tex; public float x, y; public float w, h; public Color color = Color.White; public static Color baseColor = new Color( 1, 0, 0, 0.2f ); public static void init( GraphicsDevice gpu, int width, int height ) { Color[] blockColors = new Color[ width * height ]; for( int i = 0 ; i < width * height ; i++ ) blockColors[ i ] = Color.AliceBlue; tex = new Texture2D( gpu, width, height ); tex.SetData( blockColors ); } public float Left { get { return x; } set { x = value; } } public float Right { get { return x+w; } set { x = value - w ; } } public float Top { get { return y ; } set { y = value ; } } public float Bottom { get { return y + h; } set { y = value - h ; } } public Rectangle getRect() { return new Rectangle((int)x,(int)y,(int)w,(int)h); } /// Its only intersecting if there is overlap in BOTH x and y. public bool intersects( Entity o ) { return ( ( this.hitsBottomOf(o) || this.hitsTopOf(o) ) && ( this.hitsLeftOf(o) || this.hitsRightOf(o) ) ); } // get overlap. // -y = this ON o's TOP edge (needs to move -y: down) // +y = this ON o's BOTTOM edge (needs to move +y: up) // -x = this ON o's RIGHT (needs to move -x: to the left) // +x = this ON o's LEFT (needs to move +x: to the right) public bool hitsTopOf( Entity o ) { // +----------> +x // | ______ // | |this| // | |____|_ // | || o | // | | | // | |____| // v // +y // 1. this hits o from the top return ( this.Bottom <= o.Bottom && // this is above o and this.Bottom > o.Top ) ; // this's Bottom below o's Top } public bool hitsBottomOf( Entity o ) { // 2. this's head hit a ceiling tile o // ______ // | o | // |____|_ // ||this| // | | // |____| return ( this.Top >= o.Top && // this is below o and this.Top < o.Bottom ) ;// this's top has entered bottom of o } public bool hitsLeftOf( Entity o ) { // x // 3. // ______ // __|_o_ | // |this| | // | |_| // |____| return ( this.Left <= o.Left && // this approaches from left and this.Right > o.Left ) ; // this's right is stepping on o's left OR } public bool hitsRightOf( Entity o ) { // 4. // ______ // __|this| // | o | | // | |_| // |____| return ( this.Right >= o.Right && // this approaches from right and this.Left < o.Right ) ; // this's left is stepping into o's right } public void pushOffX( Entity o ) { // 3. // ______ // __|_o_ | // |this| | // | |_| // |____| if( this.hitsLeftOf( o ) ) this.x -= ( this.Right - o.Left ) ; // 4. // ______ // __|this| // | o | | // | |_| // |____| else if( this.hitsRightOf( o ) ) this.x += o.Right - this.Left ; } public void pushOffY( Entity o ) { // +----------> +x // | ______ // | |this| // | |____|_ // | || o | // | | | // | |____| // v // +y // 1. this hits o from the top if( this.hitsTopOf( o ) ) // 1. this hit floor o with his feet this.y -= this.Bottom - o.Top ; // -y value // 2. this's head hit a ceiling tile o // ______ // | o | // |____|_ // ||this| // | | // |____| if( this.hitsBottomOf( o ) ) this.y += o.Bottom - this.Top ; } /// Mag px overlap, x /// /// public float XOverlap( Entity o ) { // for this to "count", // you must overlap in Y if( this.hitsTopOf( o ) || this.hitsBottomOf( o ) ) { // 3. // ______ // __|_o_ | // |this| | // | |_| // |____| if( this.hitsLeftOf( o ) ) // 3. t1 is bumping into t2 to the right //return o.Left - this.Right; // -x, b/c this needs move left return this.Right - o.Left ; // abs(x) // 4. // ______ // __|this| // | o | | // | |_| // |____| else if( this.hitsRightOf( o ) ) // 4. t1 hitting t2 to the left return o.Right - this.Left; // +x else return 0 ; } else return 0; } /// Magnitude of pixel overlap in y public float YOverlap( Entity o ) { if( this.hitsLeftOf( o ) || this.hitsRightOf( o ) ) { // +----------> +x // | ______ // | |this| // | |____|_ // | || o | // | | | // | |____| // v // +y // 1. this hits o from the top if( this.hitsTopOf( o ) ) // 1. this hit floor o with his feet //return o.Top - this.Bottom ; // -y value return this.Bottom - o.Top ; // abs(y) // 2. this's head hit a ceiling tile o // ______ // | o | // |____|_ // ||this| // | | // |____| else if( this.hitsBottomOf( o ) ) // 2. this's head hit a ceiling tile o return o.Bottom - this.Top ; // player move down +y else return 0 ; } else return 0 ; } } class Player : Entity { public float sp = 5 ; public float vx,vy; public bool groundContact; public static double JUMP_JUICE = .3; public double jumpJuiceRem = 0; double expFactor = 1.0; /// how strong gravity is. make large for stronger gravity public Player( int width, int height ) { w=width; h=height; x = y = w; vx=vy=0; color = new Color( 0, 1, 0, 0.2f ); } public void doJump() { if( groundContact ) // if on ground { jumpJuiceRem = Player.JUMP_JUICE; groundContact = false; } } public void computeVel( GameTime gt ) { vx=vy=0; // Set his velocity if( jumpJuiceRem > 0 ) // jump juice left { // he's jumping // 5 - 5 = 0 (at start) // 5 - 0 = 5 (at end) double jumpDist = 22.5 * Math.Exp( -expFactor * ( JUMP_JUICE - jumpJuiceRem ) ); vy -= ( float )jumpDist; // spend his jump juice by time jumpJuiceRem -= gt.ElapsedGameTime.TotalSeconds; } vy += sp ; // everything falls at a constant rate } public void move() { x+=vx; y+=vy; } } class Tile : Entity { public Tile( int width, int height ) { w=width; h=height; color = baseColor; } } public class CollisionDemo2 : Microsoft.Xna.Framework.Game { GraphicsDevice gpu; GraphicsDeviceManager graphics; SpriteBatch sb; //SpriteFont sf; Player p; Tile movingTile; List tiles; public CollisionDemo2() { graphics = new GraphicsDeviceManager( this ); Content.RootDirectory = "Content"; } protected override void Initialize() { base.Initialize(); } private void wallTest() { //Console.WriteLine( "move" ) ; p.move() ; p.groundContact = false ; List intns = new List(); foreach( Tile t in tiles ) { t.color = Entity.baseColor; // reset if( p.intersects( t ) ) { t.color = Color.Red ; intns.Add( t ) ; } } // categorize intersections: List groundTiles = new List() ; List ceilingTiles = new List() ; foreach( Tile t in intns ) { if( p.hitsTopOf( t ) ) { // does this count as grounding? // define grounding as 1/4 tile width if( p.XOverlap( t ) > t.w / 4 ) { //Console.WriteLine( "p grounded" ) ; p.groundContact =true ; t.color = Color.Black ; groundTiles.Add( t ) ; } } // Do the same thing for ceiling tiles if( p.hitsBottomOf( t ) ) { if( p.XOverlap( t ) > t.w / 4 ) { //Console.WriteLine( "OW MY HEAD!" ) ; t.color = Color.Plum ; ceilingTiles.Add( t ) ; p.jumpJuiceRem = 0 ; // kill jump } } } // push off the ceiling and remove those tiles // from the main collection // 1 RESOLVE CEILING BUMPS (and remove these tiles from further consideration) foreach( Tile t in ceilingTiles ) { if( p.intersects( t ) ) // check this again since player will be moving between iterations p.pushOffY( t ) ; intns.Remove( t ) ; // remove it from the main collection once we push off it } // 2 RESOLVE MID-AIR WALL COLLISIONS if( !p.groundContact ) // then push x off first { //sb.DrawString( sf, "Flying", new Vector2( 0, 20 ), Color.Red ) ; // now that's done, we can push off x foreach( Tile t in intns ) { if( p.intersects( t ) ) if( p.hitsLeftOf( t ) || p.hitsRightOf( t ) ) { //Console.WriteLine( "Push t side " + p.XOverlap( t ) ); t.color = Color.Olive; p.pushOffX( t ); } } } // 3 RESOLVE SINK-INTO-GROUND COLLISIONS else { // push off y foreach( Tile t in groundTiles ) { if( p.intersects( t ) ) { if( p.hitsTopOf( t ) ) { //Console.WriteLine( "Push t bottom " + p.YOverlap( t ) ); p.pushOffY( t ) ; intns.Remove( t ) ; } } } } // now for the rest of the tiles we intersected with // that were neither ground nor ceiling tiles // 4 RESOLVE ALL OTHER COLLISIONS (GROUNDED-WALL COLLISIONS) foreach( Tile t in intns ) { if( p.intersects( t ) ) if( p.hitsRightOf( t ) || p.hitsLeftOf( t ) ) { //Console.WriteLine( "Push t side#2: " + p.XOverlap( t ) ); t.color = Color.Aqua ; p.pushOffX( t ) ; } } } protected override void LoadContent() { gpu = GraphicsDevice; sb = new SpriteBatch( GraphicsDevice ); //sf = Content.Load( "SpriteFont1" ) ; //User32.SetWindowPos( (uint)Window.Handle, 0, 0, 0, 800, 600, 1 ) ; int s = 40; Entity.init( gpu, s, s ); p = new Player( s, s ); // the world is an array of Tiles int w = gpu.Viewport.Width; int h = gpu.Viewport.Height; int tw = w / s; int th = h / 2 / s; tiles = new List(); Tile t; for( int row = 0 ; row < h / 2 ; row += s ) { for( int col = 0 ; col < w ; col += s ) { t = new Tile( s, s ); t.Top = gpu.Viewport.Height - row; t.Left = col; tiles.Add( t ); } } // make a wall for( int row = h / 2 ; row < gpu.Viewport.Height ; row += s ) { t = new Tile( s, s ); t.Top = gpu.Viewport.Height - row; t.Left = 6 * s; tiles.Add( t ); } Random r = new Random(); // some random tiles for( int i = 0 ; i < 10 ; i++ ) { t = new Tile( s, s ); t.Top = ( float )( s * th * r.NextDouble() ); t.Left = ( float )( s * tw * r.NextDouble() ); tiles.Add( t ); } movingTile = new Tile( s, s ); movingTile.Left = 4 * s; movingTile.Top = h / 2 - s; tiles.Add( movingTile ); } double a = 0.0f; protected override void Update( GameTime gameTime ) { base.Update( gameTime ); } protected override void Draw( GameTime gameTime ) { gpu.Clear( Color.Gray ); sb.Begin( SpriteSortMode.BackToFront, BlendState.AlphaBlend ); #region computation: needs move update KeyboardState ks = Keyboard.GetState(); if( ks.IsKeyDown( Keys.Escape ) ) this.Exit(); a += 0.01f; if( a >= 2 * Math.PI ) a -= 2 * Math.PI; // Before moving the tile.. should see if the move's allowed movingTile.y += ( float )Math.Sin( a ); p.computeVel( gameTime ); if( ks.IsKeyDown( Keys.Up ) ) p.doJump(); if( ks.IsKeyDown( Keys.Left ) ) p.vx = -p.sp; if( ks.IsKeyDown( Keys.Right ) ) p.vx = p.sp; if( !ks.IsKeyDown( Keys.Space ) ) wallTest(); #endregion sb.Draw( Entity.tex, p.getRect(), Color.Blue ); foreach( Tile t in tiles ) sb.Draw( Entity.tex, t.getRect(), t.color ); sb.End(); base.Draw( gameTime ); } } //launcher static class Program { static void Main( string[] args ) { using( CollisionDemo2 game = new CollisionDemo2() ) { game.Run(); } } } }