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<Color>( 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);
}
/// <summary>Its only intersecting if there is overlap in BOTH x and y.</summary>
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 ;
}
/// <summary>Mag px overlap, x</summary>
/// <param name="o"></param>
/// <returns></returns>
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;
}
/// <summary>Magnitude of pixel overlap in y</summary>
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<Tile> 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<Tile> intns = new List<Tile>();
foreach( Tile t in tiles )
{
t.color = Entity.baseColor; // reset
if( p.intersects( t ) )
{
t.color = Color.Red ;
intns.Add( t ) ;
}
}
// categorize intersections:
List<Tile> groundTiles = new List<Tile>() ;
List<Tile> ceilingTiles = new List<Tile>() ;
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<SpriteFont>( "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>();
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();
}
}
}
}