private static Collision CircleVsCircle( PhysCircle bodyA, PhysCircle bodyB, float elapsed ) { Collision result = new Collision(); Vector2 normal; // if intersecting at t = 0 Vector2 popoutPos = Vector2.Zero; bool popout = false; float totalRadius = bodyA.Radius + bodyB.Radius; if ( Vector2.DistanceSquared( bodyA.Position, bodyB.Position ) < .95f * ( totalRadius * totalRadius ) ) { if ( !bodyA.Flags.HasFlags( BodyFlags.Ghost ) && !bodyB.Flags.HasFlags( BodyFlags.Ghost ) ) { normal = Vector2.Normalize( bodyA.Position - bodyB.Position ); popoutPos = bodyB.Position + 1.0001f * totalRadius * normal - bodyA.Velocity * elapsed; popout = true; } else { return new Collision( true, 0f, bodyA, bodyB, Vector2.Zero, Vector2.Zero ); } } // if not intersecting at t = 0 Vector2 relVel = Vector2.Subtract( bodyA.Velocity, bodyB.Velocity ); Vector2 relVelByT = Vector2.Multiply( relVel, elapsed ); Vector2 posAtT = Vector2.Add( bodyA.Position, relVelByT ); float time; if ( Geometry.SegmentVsCircle( out time, out normal, bodyA.Position, posAtT, bodyB.Position, bodyA.Radius + bodyB.Radius ) ) { float timeStep = Math.Max( 0f, time * elapsed ); result.Time = timeStep; result.Collided = true; result.Normal = normal; result.BodyA = bodyA; result.BodyB = bodyB; Vector2 dispAtCollision = ( bodyB.Position + bodyB.Velocity * timeStep ) - ( bodyA.Position + bodyA.Velocity * timeStep ); result.Intersection = bodyA.Position + ( bodyA.Radius / ( bodyA.Radius + bodyB.Radius ) ) * dispAtCollision; } if ( popout && !result.Collided ) bodyA.Position = popoutPos; return result; }
private static Collision CircleVsPolygon( PhysCircle bodyA, PhysPolygon bodyB, float elapsed ) { Vector2 relVel, relVelByT, posAtT; Vector2.Subtract( ref bodyA.Velocity, ref bodyB.Velocity, out relVel ); Vector2.Multiply( ref relVel, elapsed, out relVelByT ); Vector2.Add( ref bodyA.Position, ref relVelByT, out posAtT ); Vector2[] verts = bodyB.TransformedVertices; Vector2 lastVert = verts.Last(); Collision bestResult = new Collision(); bestResult.Time = float.MaxValue; Vector2 popoutPos = Vector2.Zero; Vector2 popoutNormal = Vector2.Zero; Vector2 popoutIsect = Vector2.Zero; int popoutPriority = 0; int nVerts = verts.Length; for ( int i = 0; i < nVerts; ++i ) { Vector2 vert = verts[i]; Vector2 edge = Vector2.Subtract( vert, lastVert ); Vector2 n = new Vector2( edge.Y, -edge.X ); float time; Vector2 normal; // ball is moving towards the segment if ( Vector2.Dot( n, relVel ) < 0.0f ) { n.Normalize(); Vector2 offset = Vector2.Multiply( n, bodyA.Radius ); Vector2 q0 = lastVert + offset; Vector2 q1 = vert + offset; // check if intersecting segment at elapsed = 0 if ( Geometry.SegmentVsCircle( out time, out normal, lastVert, vert, bodyA.Position, bodyA.Radius ) ) { if ( time < .95f && popoutPriority != 1 ) { float dot = Vector2.Dot( normal, -n ); if ( dot > 0f ) { popoutNormal = -normal; popoutIsect = edge * time; popoutPos = bodyA.Position + n * 1.0001f * bodyA.Radius * ( 1f - dot ) - bodyA.Velocity * elapsed; popoutPriority = 1; } } } if ( Geometry.SegmentVsSegment( out time, bodyA.Position, posAtT, q0, q1 ) ) { // if collision with segment (and polygon is convex), we're done if ( bodyB.Convex ) return new Collision( true, time * elapsed, bodyA, bodyB, n, bodyA.Position + elapsed * time * ( bodyA.Velocity ) - n * bodyA.Radius ); else if ( time * elapsed < bestResult.Time ) bestResult = new Collision( true, time * elapsed, bodyA, bodyB, n, bodyA.Position + elapsed * time * ( bodyA.Velocity ) - n * bodyA.Radius ); } } // CHECK CORNER // inside circle? if ( Vector2.DistanceSquared( bodyA.Position, vert ) < ( bodyA.Radius * bodyA.Radius ) ) { if ( popoutPriority == 0 ) { popoutPriority = 2; normal = Vector2.Normalize( bodyA.Position - vert ); popoutPos = vert + bodyA.Radius * normal; popoutNormal = normal; popoutIsect = vert; } } // intersecting circle if ( Geometry.SegmentVsCircle( out time, out normal, bodyA.Position, posAtT, vert, bodyA.Radius ) ) { // additional checks to see if hitting correct sector of circle if ( Vector2.Dot( normal, edge ) > 0.0f ) { Vector2 nextVert = verts[( i + 1 ) % nVerts]; Vector2 edge2; Vector2.Subtract( ref nextVert, ref vert, out edge2 ); if ( Vector2.Dot( normal, edge2 ) < 0.0f ) { if ( bodyB.Convex ) return new Collision( true, time * elapsed, bodyA, bodyB, normal, vert + elapsed * time * bodyB.Velocity ); else if ( time * elapsed < bestResult.Time ) bestResult = new Collision( true, time * elapsed, bodyA, bodyB, normal, vert + elapsed * time * bodyB.Velocity ); } } } lastVert = vert; } // hack to keep objects from penetrating in rare cases if ( !bestResult.Collided && popoutPriority != 0 ) { if ( !bodyA.Flags.HasFlags( BodyFlags.Ghost ) && !bodyB.Flags.HasFlags( BodyFlags.Ghost ) ) bodyA.Position = popoutPos; else return new Collision( true, 0, bodyA, bodyB, popoutNormal, popoutIsect ); } return bestResult; }
public bool HandlePowerupCollision( Collision result ) { if ( owner != null ) return true; // maybe have the powerup avoid the player? Player player = result.BodyB.Parent as Player; if ( player != null && player.Powerup == null ) { UpdateSelf -= SineWave; UpdateSelf += LockToPlayer; owner = player; LockToPlayerSpring.SetSource( Body.Position - player.BoundingCircle.Position ); LockToPlayerSpring.SetDest( new Vector2( 0f, .75f * Player.Size ) ); SizeSpring.SetDest( .3f ); RotationSpring.Active = false; CollectedAt = screen.AccumulatedTime; player.Powerup = this; owner.Position = new Vector3( owner.BoundingCircle.Position, 0 ); GameCore.Instance.AudioManager.Play3DCue( "sillySpin", owner, 1f ); } return true; }
public bool HandleShakeCollision( Collision result ) { if ( owner == null && result.BodyB.Parent is Player ) { owner = (Player)result.BodyB.Parent; if ( !Screen.GameOver ) owner.HUD.AddPoints( 1 ); Screen.PhysicsSpace.RemoveBody( result.BodyA ); Screen.ObjectTable.MoveToTrash( this ); alive = false; owner.Position = new Vector3( owner.BoundingCircle.Position, 0 ); GameCore.Instance.AudioManager.Play3DCue( "collect", owner, .8f ); // make sparkle particles PixieParticleSystem system = Screen.PixieParticleSystem; for ( int i = 0; i < 20; ++i ) { Vector3 pos = rand.NextVector3(); if ( pos != Vector3.Zero ) { pos.Normalize(); pos *= ( 1.25f * Body.Radius * (float)rand.NextDouble() ); } system.AddParticle( new Vector3( Body.Position, Player.Size ) + pos, Vector3.Zero/*.5f * pos*/ ); } } return true; }
private bool HandlePlayerCollision( Collision result ) { Player player = result.BodyB.Parent as Player; if ( player != null && player != parent && !player.Seizuring && !pwntPlayers.Contains( player ) ) { player.TakeLaserUpAss( result ); pwntPlayers.Add( player ); IAudioEmitter emitter = DummyAudioEmitter.InstanceAtPos( new Vector3( result.Intersection, 0 ) ); GameCore.Instance.AudioManager.Play3DCue( "laserHit", emitter, 1 ); } return true; }
public bool HandleGoldenShakeCollision( Collision result ) { if ( owner != null ) return true; // maybe have the powerup avoid the player? Player player = result.BodyB.Parent as Player; if ( player != null ) { if ( player.Powerup != null ) player.Powerup.Use(); UpdateSelf -= SineWave; UpdateSelf += LockToPlayer; owner = player; LockToPlayerSpring.SetSource( Body.Position - player.BoundingCircle.Position ); LockToPlayerSpring.SetDest( new Vector2( 0f, .75f * Player.Size ) ); SizeSpring.SetDest( .3f ); RotationSpring.Active = false; player.HUD.AddPoints( 7 ); player.Powerup = this; owner.Position = new Vector3( owner.BoundingCircle.Position, 0 ); GameCore.Instance.AudioManager.Play3DCue( "collect", owner, .9f ); GameCore.Instance.AudioManager.Play2DCue( "fanfare", 1f ); GameplayScreen.Instance.EndGame(); } return true; }
private bool KillSelfIfPwnt( Collision result ) { Player player = result.BodyB.Parent as Player; if ( player != null && ( player.Respawning || player.Crushing ) ) { if ( !BoundingPolygon.Flags.HasFlags( BodyFlags.Ghost ) ) { // remove the block BoundingPolygon.Flags |= BodyFlags.Ghost; Screen.PhysicsSpace.RemoveBody( BoundingPolygon ); Screen.ObjectTable.MoveToTrash( this ); alive = false; // rumble the controller if ( player.PlayerIndex.IsHuman() ) GameCore.Instance.Rumble.RumbleLow( player.PlayerIndex, .25f, .3f ); // add the exploding block particle system Vector3 position = new Vector3( BoundingPolygon.Position, 0f ); ModelExplosion explosion = ModelExplosion.CreateExplosion( position, Size, ShatteredModel, explosionSettings ); Screen.ParticleManager.Add( explosion ); GameCore.Instance.AudioManager.Play3DCue( "blockExplosion", DummyAudioEmitter.InstanceAtPos( position ), 1f ); return false; } } return true; }
public static void ResolveCollision( Collision result ) { PhysBody bodyA = result.BodyA; PhysBody bodyB = result.BodyB; bodyA.Touching = bodyB; bodyB.Touching = bodyA; bodyA.TouchNormal = result.Normal; bodyB.TouchNormal = -result.Normal; if ( !bodyA.OnCollision( result ) || !bodyB.OnCollision( result.GetInvert() ) ) return; float e = Math.Min( bodyA.Elasticity, bodyB.Elasticity ); float u = Math.Max( bodyA.Friction, bodyB.Friction ); Vector2 n = result.Normal; // adjust normal in case of floating point error if ( n.X == 0f && Math.Abs( n.Y ) != 1f ) n.Y = Math.Sign( n.Y ); else if ( n.Y == 0f && Math.Abs( n.X ) != 1f ) n.X = Math.Sign( n.X ); Vector2 rA = result.Intersection - bodyA.Position; Vector2 rB = result.Intersection - bodyB.Position; Vector2 vA = bodyA.Velocity + Geometry.Perp( rA ) * -bodyA.AngularVelocity; Vector2 vB = bodyB.Velocity + Geometry.Perp( rB ) * -bodyB.AngularVelocity; Vector2 vAB = vA - vB; Vector2 fricDir = -( vAB - Vector2.Dot( vAB, n ) * n ); if ( fricDir != Vector2.Zero ) fricDir.Normalize(); if ( float.IsInfinity( fricDir.X ) || float.IsInfinity( fricDir.Y ) ) fricDir = Vector2.Zero; float oneByMassA = 1f / bodyA.Mass; float oneByMassB = 1f / bodyB.Mass; float oneByIA = 1f / bodyA.MomentOfInertia; float oneByIB = 1f / bodyB.MomentOfInertia; if ( bodyB.Flags.HasFlags( BodyFlags.Anchored ) ) { oneByMassB = 0f; oneByIB = 0f; } float dotASq = Geometry.PerpDot( rA, n ); dotASq *= dotASq; float dotBSq = Geometry.PerpDot( rB, n ); dotBSq *= dotBSq; float jc = Vector2.Dot( vAB, n ) / ( oneByMassA + oneByMassB + dotASq * oneByIA + dotBSq * oneByIB ); if ( jc > -.7f ) jc = -.7f; dotASq = Geometry.PerpDot( rA, fricDir ); dotASq *= dotASq; dotBSq = Geometry.PerpDot( rB, fricDir ); dotBSq *= dotBSq; float jf = Vector2.Dot( vAB, fricDir ) / ( oneByMassA + oneByMassB + dotASq * oneByIA + dotBSq * oneByIB ); if ( Math.Abs( jf ) > Math.Abs( jc * u ) ) jf = Math.Abs( jc * u ) * Math.Sign( jc ); Vector2 impulse = ( jc * -( 1f + e ) ) * n - jf * fricDir; bodyA.LastImpulse = impulse; bodyB.LastImpulse = -impulse; bodyA.Velocity += ( impulse * oneByMassA ); bodyB.Velocity -= ( impulse * oneByMassB ); bodyA.AngularVelocity += ( Geometry.PerpDot( rA, impulse ) * oneByIA ); bodyB.AngularVelocity -= ( Geometry.PerpDot( rB, impulse ) * oneByIB ); bodyA.OnResponse( result ); bodyB.OnResponse( result.GetInvert() ); bodyA.CollisionList.Add( bodyB ); bodyB.CollisionList.Add( bodyA ); }
private bool HandleCollisionResponse( Collision result ) { PhysCircle circle = BoundingCircle; // keep track of last time of collision (for jumping) lastCollision = Screen.AccumulatedTime; // set emitter position SparkParticleSystem sparkSystem = Screen.SparkParticleSystem; string sound = null; if ( result.BodyB.Parent is FloorBlock ) sound = pvBlockSound; if ( result.BodyB.Parent is Boundary || result.BodyB.Parent is Shelves ) sound = pvCageSound; if ( sound != null ) { float impulse = result.BodyA.LastImpulse.Length(); if ( impulse > 10f ) { float volume = Math.Min( 1f, impulse / ( 100 * BoundingCircle.Mass ) ); soundPosition = new Vector3( result.Intersection, 0 ); GameCore.Instance.AudioManager.Play3DCue( sound, this, volume ); if ( PlayerIndex.IsHuman() ) GameCore.Instance.Rumble.RumbleHigh( PlayerIndex, .2f, .15f ); } } //// play collision sound //Player playerB = result.BodyB.Parent as Player; //if ( playerB != null ) //{ // float impulseMag = result.BodyB.LastImpulse.Length(); // //if ( impulseMag > .5f ) // { // float volume = Math.Min( .85f, impulseMag / 30f ); // soundPosition = new Vector3( result.Intersection, 0 ); // Screen.AudioManager.Play3DCue( plasticHitSound, this, volume ); // } //} Vector3 position = new Vector3( result.Intersection, 0f ); //emitter.Position = position; // spit some particles Vector2 r = Vector2.Normalize( result.Intersection - result.BodyA.Position ); Vector2 vp = circle.AngularVelocity * circle.Radius * new Vector2( -r.Y, r.X ); Vector2 dir = circle.Velocity; Vector2 vpn = Vector2.Normalize( vp ); Vector3 direction = new Vector3( particleCoordU * vpn + particleCoordV * -r, 0f ); //factory.Direction = new Vector3( particleCoordU * vpn + particleCoordV * -r, 0f ); Vector2 sum = vp + dir; if ( sum != Vector2.Zero ) { float sumLength = sum.Length(); if ( sumLength > .25f ) { float coneAngle = MathHelper.ToRadians( 30 ); Vector3 velocity = random.NextConeDirection( random.NextFloat( 2, 4 ) * direction, coneAngle ); sparkSystem.AddParticle( position, velocity ); } } return true; }
private bool HandleCollision( Collision result ) { //Player playerB = result.BodyB.Parent as Player; //if ( playerB != null ) //{ // // squash him if he's tiny and touching another object // if ( playerB.Scale < squashSize * Size )//&& result.BodyB.Touching != result.BodyA ) // { // Debug.WriteLine( "Squash!" ); // } //} // play collision sound Player playerB = result.BodyB.Parent as Player; if ( playerB != null ) { float relVelMag = ( result.BodyA.Velocity - result.BodyB.Velocity ).Length(); if ( relVelMag > 3f ) { float volume = Math.Min( .75f, relVelMag / 100f ); soundPosition = new Vector3( result.Intersection, 0 ); GameCore.Instance.AudioManager.Play3DCue( pvpSound, this, volume ); if ( PlayerIndex.IsHuman() ) GameCore.Instance.Rumble.RumbleHigh( PlayerIndex, .2f, .15f ); } } return true; }
public void TakeLaserUpAss( Collision result ) { if ( Respawning ) return; seizureBegin = Screen.AccumulatedTime; seizureCollision = result; BoundingCircle.Flags |= BodyFlags.Anchored; BoundingCircle.Velocity = Vector2.Zero; BoundingCircle.AngularVelocity = 0; if ( PlayerIndex.IsHuman() ) GameCore.Instance.Rumble.RumbleHigh( PlayerIndex, .4f, seizureDuration ); }