Particle and Physics problem.
- by Quincy
This was originally a forum post so I hope you guys don't mind it being 2 questions in one.
I am making a game and I got some basic physics implemented. I have 2 problems, 1 with particles being drawn in the wrong place and one with going through walls while jumping in corners.
Skip over to about 15 sec
video showing the 2 problems : http://youtube.com/watch?v=Tm9nfWsWfiM
So the problem with the particles seems to be coming from the removal, as soon as I remove that piece of code it instantly works, but there shouldn't be a problem since they shouldn't even draw when their energy gets to 0 (and then they get removed)
So my first question is, how are these particles getting warped all over the screen ?
Relevant code :
Particle class :
class Particle
{
//Physics
public Vector2 position = new Vector2(0,0);
public float direction = 180;
public float speed = 100;
public float energy = 1;
protected float startEnergy = 1;
//Visual
public Sprite sprite;
public float rotation = 0;
public float scale = 1;
public byte alpha = 255;
public BlendMode blendMode
{
get
{
return sprite.BlendMode;
}
set
{
sprite.BlendMode = value;
}
}
public Particle()
{
}
public virtual void Think(float frameTime)
{
if (energy - frameTime < 0)
energy = 0;
else
energy -= frameTime;
position += new Vector2((float)Math.Cos(MathHelper.DegToRad(direction)), (float)Math.Sin(MathHelper.DegToRad(direction))) * speed * frameTime;
alpha = (byte)(255 * energy / startEnergy);
sprite.Rotation = rotation;
sprite.Position = position;
sprite.Color = new Color(sprite.Color.R, sprite.Color.G, sprite.Color.B, alpha);
}
public virtual void Draw(float frameTime)
{
if (energy > 0)
{
World.camera.DrawSprite(sprite);
}
}
// Basic particle implementation
class BasicSprite : Particle
{
public BasicSprite(Sprite _sprite)
{
sprite = _sprite;
}
}
Emitter :
class Emitter
{
protected static Random rand = new Random();
protected List<Particle> particles = new List<Particle>();
public BaseEntity target = null;
public Vector2 position = new Vector2(0, 0);
public bool Active = true;
public float timeAlive = 0;
public int particleCount = 0;
public int ParticlesPerSeccond
{
get
{
return (int)(1 / particleSpawnTime);
}
set
{
particleSpawnTime = 1 / (float)value;
}
}
public float dieTime = float.MaxValue;
float particleSpawnTime = 0.05f;
float spawnTime = 0;
public Emitter()
{
}
public virtual void Think(float frametime)
{
spawnTime += frametime;
if (dieTime != float.MaxValue)
{
timeAlive += frametime;
if (timeAlive >= dieTime)
Active = false;
}
if (Active)
{
if (target != null)
position = target.Position;
while (spawnTime > particleSpawnTime)
{
spawnTime -= particleSpawnTime;
AddParticle();
particleCount++;
}
}
for (int i = 0; i < particles.Count; i++)
{
particles[i].Think(frametime);
if (particles[i].energy <= 0)
{
particles.Remove(particles[i]); // As soon as this is removed, it works
particleCount--;
}
}
}
public virtual void AddParticle()
{
}
public virtual void Draw(float frametime)
{
foreach (Particle particle in particles)
{
particle.Draw(frametime);
}
}
}
class BloodEmitter : Emitter
{
Image image;
public BloodEmitter()
{
image = new Image(@"Content/Particles/TinyCircle.png");
image.CreateMaskFromColor(new Color(255, 0, 255, 255));
this.dieTime = 0.5f;
this.ParticlesPerSeccond = 100;
}
public override void AddParticle()
{
Sprite sprite = new Sprite(image);
sprite.Color = new Color((byte)(rand.NextDouble() * 255), (byte)(rand.NextDouble() * 255), (byte)(rand.NextDouble() * 255));
BasicSprite particle = new BasicSprite(sprite);
particle.direction = (float)rand.NextDouble() * 360;
particle.position = position;
particle.blendMode = BlendMode.Alpha;
particles.Add(particle);
}
}
The seccond problem is the physics problem, for some reason I can get through the right bottom corner while jumping. I think this is coming from me switching animations but I thought I made it compensate for that.
Relevant code :
PhysicsEntity :
class PhysicsEntity : BaseEntity
{
// Horizontal movement constants
protected const float maxHorizontalSpeed = 1000;
protected const float horizontalAcceleration = 15;
protected const float horizontalDragAir = 0.95f;
protected const float horizontalDragGround = 0.95f;
// Vertical movement constants
protected const float maxVerticalSpeed = 1000;
protected const float verticalAcceleration = 20;
// Everything needed for movement and correct animations
protected float movement = 0;
protected bool onGround = false;
protected Vector2 Velocity = new Vector2(0, 0);
protected float maxSpeed = 0;
float lastThink = 0;
float thinkTime = 1f/60f;
public PhysicsEntity(Vector2 position, Sprite sprite) :
base(position, sprite)
{
}
public override void Draw(float frameTime)
{
base.Draw(frameTime);
}
public override void Think(float frameTime)
{
CalculateMovement(frameTime);
base.Think(frameTime);
}
protected void CalculateMovement(float frameTime)
{
lastThink += frameTime;
while (lastThink > thinkTime)
{
onGround = false;
Velocity.X = MathHelper.Clamp(Velocity.X + horizontalAcceleration * movement, -maxHorizontalSpeed, maxHorizontalSpeed);
if (onGround)
Velocity.X *= horizontalDragGround;
else
Velocity.X *= horizontalDragAir;
if (maxSpeed < Velocity.X)
maxSpeed = Velocity.X;
Velocity.Y = MathHelper.Clamp(Velocity.Y + verticalAcceleration, -maxVerticalSpeed, maxVerticalSpeed);
lastThink -= thinkTime;
DoCollisions(thinkTime);
DoAnimations(thinkTime);
}
}
public virtual void DoAnimations(float frameTime)
{
}
public void DoCollisions(float frameTime)
{
Position.Y += Velocity.Y * frameTime;
Vector2 tileCollision = GetTileCollision();
if (tileCollision.X != -1 || tileCollision.Y != -1)
{
Vector2 collisionDepth = CollisionRectangle.DepthIntersection(
new Rectangle(
tileCollision.X * World.tileEngine.TileWidth,
tileCollision.Y * World.tileEngine.TileHeight,
World.tileEngine.TileWidth,
World.tileEngine.TileHeight
)
);
Position.Y += collisionDepth.Y;
if (collisionDepth.Y < 0)
onGround = true;
Velocity.Y = 0;
}
Position.X += Velocity.X * frameTime;
tileCollision = GetTileCollision();
if (tileCollision.X != -1 || tileCollision.Y != -1)
{
Vector2 collisionDepth = CollisionRectangle.DepthIntersection(
new Rectangle(
tileCollision.X * World.tileEngine.TileWidth,
tileCollision.Y * World.tileEngine.TileHeight,
World.tileEngine.TileWidth,
World.tileEngine.TileHeight
)
);
Position.X += collisionDepth.X;
Velocity.X = 0;
}
}
public void DoCollisions(Vector2 difference)
{
CollisionRectangle.Y = Position.Y - difference.Y;
CollisionRectangle.Height += difference.Y;
Vector2 tileCollision = GetTileCollision();
if (tileCollision.X != -1 || tileCollision.Y != -1)
{
Vector2 collisionDepth = CollisionRectangle.DepthIntersection(
new Rectangle(
tileCollision.X * World.tileEngine.TileWidth,
tileCollision.Y * World.tileEngine.TileHeight,
World.tileEngine.TileWidth,
World.tileEngine.TileHeight
)
);
Position.Y += collisionDepth.Y;
if (collisionDepth.Y < 0)
onGround = true;
Velocity.Y = 0;
}
CollisionRectangle.X = Position.X - difference.X;
CollisionRectangle.Width += difference.X;
tileCollision = GetTileCollision();
if (tileCollision.X != -1 || tileCollision.Y != -1)
{
Vector2 collisionDepth = CollisionRectangle.DepthIntersection(
new Rectangle(
tileCollision.X * World.tileEngine.TileWidth,
tileCollision.Y * World.tileEngine.TileHeight,
World.tileEngine.TileWidth,
World.tileEngine.TileHeight
)
);
Position.X += collisionDepth.X;
Velocity.X = 0;
}
}
Vector2 GetTileCollision()
{
int topLeftTileX = (int)(CollisionRectangle.TopLeft.X / World.tileEngine.TileWidth);
int topLeftTileY = (int)(CollisionRectangle.TopLeft.Y / World.tileEngine.TileHeight);
int BottomRightTileX = (int)(CollisionRectangle.DownRight.X / World.tileEngine.TileWidth);
int BottomRightTileY = (int)(CollisionRectangle.DownRight.Y / World.tileEngine.TileHeight);
if (CollisionRectangle.DownRight.Y % World.tileEngine.TileHeight == 0) // If your exactly against the tile don't count that as being inside the tile
BottomRightTileY -= 1;
if (CollisionRectangle.DownRight.X % World.tileEngine.TileWidth == 0) // If your exactly against the tile don't count that as being inside the tile
BottomRightTileX -= 1;
for (int i = topLeftTileX; i <= BottomRightTileX; i++)
{
for (int j = topLeftTileY; j <= BottomRightTileY; j++)
{
if (World.tileEngine.TileIsSolid(i, j))
{
return new Vector2(i, j);
}
}
}
return new Vector2(-1, -1);
}
}
Player :
enum State
{
Standing,
Running,
Jumping,
Falling,
Sliding,
WallSlide
}
class Player : PhysicsEntity
{
private State state
{
get
{
return currentState;
}
set
{
if (currentState != value)
{
currentState = value;
animationChanged = true;
}
}
}
private State currentState = State.Standing;
private BasicEmitter basicEmitter = new BasicEmitter();
public bool flipped;
public bool animationChanged = false;
protected const float jumpPower = 600;
AnimationManager animationManager;
Rectangle DrawRectangle;
public override Rectangle CollisionRectangle
{
get
{
return new Rectangle(
Position.X - DrawRectangle.Width / 2f,
Position.Y - DrawRectangle.Height / 2f,
DrawRectangle.Width,
DrawRectangle.Height
);
}
}
public Player(Vector2 position, Sprite sprite) :
base(position, sprite)
{
// Only posted the relevant bit
DrawRectangle = animationManager.currentAnimation.drawingRectangle;
}
public override void Draw(float frameTime)
{
World.camera.DrawSprite(
Sprite,
Position + new Vector2(DrawRectangle.X, DrawRectangle.Y),
animationManager.currentAnimation.drawingRectangle
);
}
public override void Think(float frameTime)
{
//I only posted the relevant stuff
if (animationChanged)
{
// if the animation has changed make sure we compensate for the change in with and height
animationChanged = false;
DoCollisions(animationManager.getSizeDifference());
}
DoCustomMovement();
base.Think(frameTime);
if (!onGround && Velocity.Y > 0)
{
state = State.Falling;
}
}
void DoCustomMovement()
{
if (onGround)
{
if (World.renderWindow.Input.IsKeyDown(KeyCode.W))
{
Velocity.Y = -jumpPower;
state = State.Jumping;
}
}
}
public override void DoAnimations(float frameTime)
{
string stateName = Enum.GetName(typeof(State), state);
if (!animationManager.currentAnimationIs(stateName))
{
animationManager.PlayAnimation(stateName);
}
animationManager.Think(frameTime);
DrawRectangle = animationManager.currentAnimation.drawingRectangle;
Sprite.Center = new Vector2(
DrawRectangle.X + DrawRectangle.Width / 2,
DrawRectangle.Y + DrawRectangle.Height / 2
);
Sprite.FlipX(flipped);
}
So why am I warping through walls ?
I have given this some thought but I just can't seem to find out why this is happening.
Full source if needed :
source : http://www.mediafire.com/?rc7ddo09gnr68zd (download link)