Space Invaders stick to wall - c#

So I have to develop an XNA game for school and I thought Space Invaders was a nice thing to make. But now I'm stuck with a problem. I have a List filled with 'Aliens' from another class. And every second it moves 20 px to the right and I want it to descend when it touches the right side of the screen (not the problem). But I want it to move the opposite direction (to the left) as soon as it touches the wall. Here lies the problem.
As soon as it touches the wall it still moves 20px once, changes direction, moves 20px back and changes direction again. This keeps repeating.
So where lies the problem. I think it has to check earlier if one of the aliens touches the screen but don't know how to accomplish that and thought maybe one of you could help me out because it is very frustrating!
I included the update method
if (xPos % step == 0)
{
if (!isDescending)
{
for (int k = 0; k < sprites.Count; k++)
{
Sprite sprite = sprites[k];
if (touchedRight) sprite.position.X += step;
else sprite.position.X -= step;
}
for (int k = 0; k < sprites.Count; k++)
{
Sprite sprite = sprites[k];
bool hitLeft = sprite.position.X == 0;
bool hitRight = sprite.rect.Right == screenWidth;
if ((hitLeft) || (hitRight))
{
touchedRight = !touchedRight;
isDescending = true;
}
}
}
else
{
isDescending = false;
for (int k = 0; k < sprites.Count; k++)
{
sprites[k].position.Y += sprites[k].rect.Height;
}
}
}
// CheckCollision(alienBounds, k-1);
// sprite.rect = new Rectangle((int)sprite.position.X, (int)sprite.position.Y, 20, 20);
// alienBounds = sprite.rect;
xPos += 1;

Well, this is wrong:
if ((hitLeft) || (hitRight))
When moving right, you only care about hitRight. And when moving left, you care only about hitLeft.
So try
if (touchedRight? hitLeft: hitRight)
This will also fix the issue where multiple aliens can hit the wall at the same time.

Im not sure but I bellieve in Space invaders, if you kill all columns but the most left, it doesnt move completely to the right side of the screen before switching its direction.
So what I suggest is you create a derivate of a Sprite called Invader:
class Invader : Sprite
{
readonly float distanceFromCenterHorizontal;
public Invader(float distanceFromCenterHorizontal)
{
this.distanceFromCenterHorizontal= distanceFromCenterHorizontal;
}
public void UpdatePosition(Point center)
{
this.Position.X = center.X + distanceFromCenterHorizontal;
this.Position.Y = center.Y;
}
}
// global variables:
Point center = ....
bool moveLeft = false;
void createInvaders()
{
int invaderWidth = 20; // whatever your value is
float columnCenter = ((float) columnMax) / 2 * invaderWidth;
for(var column = 0; column < columnMax ; column++)
{
float invaderX = (column * invaderWidth);
float distanceFromCenterHorizontal = invaderX - columnCenter;
for(var row = 0; row < rowMax; row ++)
{
Invader invader = new Invader(distanceFromCenterHorizontal);
invader.Position.Y = row * invaderHeight // Needs adjustment here!
}
}
}
void update()
{
if(center.X == screenWidth) moveLeft = true;
else if(center.X <= 0) moveLeft = false;
center.X += moveLeft? -step : step;
foreach(var invader in invaders) invader.UpdatePosition(center);
}
You want to move the "centerPoint" and all Invaders update According to the new Center.
Im not sure if XNA uses double or float values, so youmight need to adjust this.
You can then easily expand this to create some up / down wobbling.

So i made a new rectangle called 'bounds' which is as wide as the aliens and moves the same as the aliens do. And got it working! (half of it). It moves to the right until it touches the right side, then it goes to the left but keeps going to the left and never goes back to the right. So where am i going wrong this time?
if (xPos % step == 0)
{
if (!isDescending)
{
for (int k = 0; k < sprites.Count; k++)
{
Sprite sprite = sprites[k];
sprite.position.X += step;
}
if (bounds.Right == screenWidth || bounds.Left == 0)
{
touchedRight = !touchedRight;
step *= -1;
}
if (!touchedRight) bounds.X += step;
if (touchedRight) bounds.X -= step;
// for (int k = 0; k < sprites.Count; k++)
// {
// Sprite sprite = sprites[k];
// bool hitLeft = sprite.position.X == 0;
// bool hitRight = sprite.rect.Right == screenWidth;
// if ((hitLeft) || (hitRight))
// {
// touchedRight = !touchedRight;
// isDescending = true;
// }
// }
}

Related

How to process multiple touch swipes unity

I am a fairly new programmer and after learning my c# basics i am trying to get into game development with Unity. Right now i am coding a small mobile game. I want to add an one screen multiplayer to it. I had an easy script set up which gathered the starting and ending position of the swipe and calculated the path between the two points. then it compared the absolute of Δx with the absolute of Δy and finally it testet whether the coordinate is below or above 0. this works quite nicely but obviously it would not work for my planned one screen multiplayer. first i tried just testing whether the starting point of a swipe is located to the left or right of the screen center. this also worked quite nicely but it could obviously not track two swipes at the same time. then i tried changing the variables to arrays and adding a for (later a foreach loop). now whenever i swipe in one direction it repeats the signal every frame even if i clear the slot in the array at the beginning of the loop... after literally one and a half days of trying stuff out and searching for answers i now came around to adding my own question. thank you in advance.
public class HopefullyWorkingSwipeDetector
{
Vector2[] av2_TouchPos1L = new Vector2[5];
Vector2[] av2_TouchPos2L = new Vector2[5];
Vector2[] av2_TouchPathL = new Vector2[5];
public int[] aint_TouchDirectionL = new int[5]; // Up: 0; Down: 1; Right: 2; Left: 3; n/a: 4;
public int int_SwipePendingL;
public int int_CountTouchesL = 0;
public void ListenForSwipeP1()
{
int_CountTouchesL = 0;
int_SwipePendingL = 0;
foreach (Touch touch in Input.touches)
{
if (Input.touchCount > 0 && Input.GetTouch(int_CountTouchesL).phase == TouchPhase.Began && Input.GetTouch(int_CountTouchesL).position.x < Screen.width / 2)
{
int_SwipePendingL = int_CountTouchesL;
av2_TouchPos1L[int_SwipePendingL] = Input.GetTouch(int_SwipePendingL).position;
}
else if (Input.touchCount > 0 && Input.GetTouch(int_SwipePendingL).phase == TouchPhase.Ended && Input.GetTouch(int_SwipePendingL).position.x < Screen.width / 2)
{
av2_TouchPos2L[int_SwipePendingL] = Input.GetTouch(int_SwipePendingL).position;
av2_TouchPathL[int_SwipePendingL] = av2_TouchPos2L[int_SwipePendingL] - av2_TouchPos1L[int_SwipePendingL];
if (Mathf.Abs(av2_TouchPathL[int_SwipePendingL].x) < Mathf.Abs(av2_TouchPathL[int_SwipePendingL].y)) //Vertical
{
if (av2_TouchPathL[int_SwipePendingL].y > 0) //up
{
aint_TouchDirectionL[int_SwipePendingL] = 0;
}
else if (av2_TouchPathL[int_SwipePendingL].y < 0) //Down
{
aint_TouchDirectionL[int_SwipePendingL] = 1;
}
}
else if (Mathf.Abs(av2_TouchPathL[int_SwipePendingL].x) > Mathf.Abs(av2_TouchPathL[int_SwipePendingL].y)) //horizontal
{
if (av2_TouchPathL[int_SwipePendingL].x > 0) //Right
{
aint_TouchDirectionL[int_SwipePendingL] = 2;
}
else if (av2_TouchPathL[int_SwipePendingL].x < 0) //left
{
aint_TouchDirectionL[int_SwipePendingL] = 3;
}
}
}
else
{
aint_TouchDirectionL[int_CountTouchesL] = 4;
}
int_CountTouchesL++;
}
}
}

Xna Intersect is to slow

Hello i have a problem with this code:
for (int i = 0; i < map.collision_rects.Count(); i++)
{
if (player.collision_rect.Intersects(map.collision_rects[i]))
{
Debug.WriteLine("INTERSERCTED:" + player.velocity.Y);
player.velocity.Y = 0;
player.has_jumped = false;
}
}
player.Update(gameTime);
The problem is that it Detects that it intersected but the velocity is set to zero a frame later than it detected
if (has_jumped == true)
{
float i = 1;
velocity.Y += 0.15f*i;
}
The code that sets the velocity

Enemies in a list: issue with colliding the enemies with the other enemies

I'm currently working on a game in which a list of enemies is spawned into the level randomly on the x-axis using a list up to the point of the maximum number to spawn (currently set at 10) I have a large portion of the game working however I currently have the problem that the enemies can overlap/stack on top of each other.
What I want is if the collision box of the enemy is intersecting another enemy in the list, for it to no overlap. (if that makes sense) this is the code I have for it so far.
foreach (EnemyClass enemy1 in enemies)
{
if (enemy1.position.X < myPlayer.position.X - 10 && !enemy1.stopMoving)
{
enemy1.position.X += enemy1.amountToMove;
}
if (enemy1.position.X > myPlayer.position.X - 50 && !enemy1.stopMoving)
{
enemy1.position.X -= enemy1.amountToMove;
}
foreach (EnemyClass enemy2 in enemies)
{
if (enemy1 != enemy2 && enemy1.collisionBox.Intersects(enemy2.collisionBox))
{
System.Console.WriteLine("Collision Detected");
}
}
enemy1.collisionX = (int)enemy1.position.X;
enemy1.Update(graphics.GraphicsDevice);
}
Any help would be greatly appreciated.
EDIT: What I missed off was that they technically do collide, however they collide when they are 1000 pixels apart, when the sprite is actually 50 pixels wide.
EDIT: This is the changed collision code..
foreach (EnemyClass enemy in enemies)
{
enemy.collisionX = (int)enemy.position.X;
if (enemy.position.X < myPlayer.position.X - 10 && !enemy.stopMoving)
{
enemy.position.X += enemy.amountToMove;
}
if (enemy.position.X > myPlayer.position.X - 50 && !enemy.stopMoving)
{
enemy.position.X -= enemy.amountToMove;
}
for (int i = 0; i < enemies.Count; i++)
{
for (int j = i + 1; j < enemies.Count; j++)
{
if (enemies[i].collisionBox.Intersects(enemies[i].collisionBox))
{
System.Console.WriteLine("Collision Detected");
}
}
}
enemy.Update(graphics.GraphicsDevice);
}
EDIT: Here is the EnemyClass, which is where the collisionBox is contained:
public EnemyClass(Texture2D newTexture, Vector2 newPosition)
{
texture = newTexture;
position = newPosition;
collisionBox = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
randX = random.Next(1, 3);
speed = new Vector2(randX, 0);
}
public void Update(GraphicsDevice graphics)
{
collisionBox = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, Color.White);
}
It seems that the collision is being detected before all of the collision boxes have been updated.
My suggestions for how to order things:
Define the collision box in terms of position.X and position.Y so
that it always accurately reflects the correct position.
Update all the enemies x- and y-positions.
Execute the snippet below after all the enemy positions have been updated.
foreach (EnemyClass enemy in enemies)
{
if (enemy.position.X < myPlayer.position.X - 10 && !enemy.stopMoving)
{
enemy.position.X += enemy.amountToMove;
}
if (enemy.position.X > myPlayer.position.X - 50 && !enemy.stopMoving)
{
enemy.position.X -= enemy.amountToMove;
}
enemy.Update(graphics.GraphicsDevice);
}
for (int i = 0; i < enemies.Count; i++)
{
for (int j = i + 1; j < enemies.Count; j++)
{
if (enemies[i].collisionBox.Intersects(enemies[j].collisionBox))
{
System.Console.WriteLine("Collision Detected");
}
}
}
The above code also has an advantage in not detecting the same collision twice (i.e. enemy1/enemy2 and enemy2/enemy1).

(C#/XNA) No matter what, collision never works

I've been racking my mind on this for days. When updating entities, I check for collision, and if they collide according to the code below, their appropriate motion value is set to 0. Except for some reason, this code doesn't work for checking with collision to its right. If there is a 2 tall wall in front of the entity, it will disregard it. However, it seems to work right if I check below it, but then it will stop completely.
Collision code:
public static bool CollidesRight(Level level, Vector2 position, Vector2 motion, int width, int height)
{
Vector2 result = position + motion;
int x1 = (int)(result.X / (float)Tile.TILE_WIDTH);
int x2 = (int)((result.X + (float)width) / (float)Tile.TILE_WIDTH) + 1;
int y1 = (int)(result.Y / (float)Tile.TILE_HEIGHT);
int y2 = (int)((result.Y + (float)height) / (float)Tile.TILE_HEIGHT);
AABB resultBB = new AABB(result, width, height);
for (int i = x1; i < x2; i++)
{
for (int j = y1; j < y2; j++)
{
if (level.GetTileAt(i, j) != 0 && (Tile.TileList[level.GetTileAt(i, j)].IsSolid() || Tile.TileList[level.GetTileAt(i, j)].HasSolidTop()))
{
AABB tile = new AABB(new Vector2(i * 64f, j * 64f), 64, 64);
Console.WriteLine(tile);
return resultBB.Intersects(tile);
}
}
}
return false;
}
Here's my entity's updating code:
public virtual void Tick()
{
lastMotion = motion;
lastPosition = position;
if (Collision.CollidesBottom(level, position, motion, width, height))
{
onGround = true;
}
else if(!Collision.CollidesBottom(level, position, motion, width, height))
{
onGround = false;
}
if (onGround && !isJumping)
{
motion.Y = 0f;
fallDistance = 0;
}
else if(!onGround)
{
fallDistance++;
motion.Y += gravity * (fallDistance * 0.005f);
}
if (isJumping)
{
timeJumping++;
if (timeJumping > 32)
{
timeJumping = 0;
isJumping = false;
}
else
{
motion.Y = -2.25f;
}
}
position.Y += motion.Y;
if (motion.X != 0)
{
if (motion.X < 0)
{
motion.X += friction;
}
if (motion.X > 0)
{
motion.X -= friction;
}
}
if ((motion.X > 0 && Collision.CollidesRight(level, position, motion, width, height)) || (motion.X < 0 && Collision.CollidesLeft(level, position, motion, width, height)))
{
motion.X = 0f;
}
position.X += motion.X;
//MoveEntity(motion);
}
I have 3 options for you.
First, Edit - nevermind about this one, i misread your code I think you should add your X motion before doing the collision check, since the collision check uses it to work. If you don't want to add it to the actual player motion, at least pass the potential motion into the function. That alone might fix everything. But there's another problem.
Second, and I'm pretty sure it's this one, I suspect that your position.Y is frequently truncated to an Int when your OnGround bool is set. Which means it's minimum will be a whole unit and the next unit up will be a whole unit. And in your AABB code from pastebin:
if (bb.max.Y <= min.Y || bb.min.Y >= max.Y)
{
return false;
}
This code will return false even if the X-coordinate related code detects a collision. You should modify the logic in here so that the function returns true if there is a collision, instead of returning false on two independent checks.
Third, assuming your width is 1 unit and your tiles are also 1 unit wide, with a block at x=2 and position.x = 0.5, and a very small xMotion (or one that hasn't been added in yet, see answer 1):
(occupying some of block 0 and some of block 1, like so) [.][ ][X]
You want to stop when you hit x = 1, because then you'll be up against your obstruction. But:
x1 = (int)(0.5) = 0
x2 = (int)(0.5 + 1) + 1 = 2
so your loop:
for (int i = x1; i < x2; i++)
checks block 0 (which is empty, then checks block 1 (which is empty) and then stops (because i == x2) and never checks block 2. So the block you care about never gets its collision check. I think if you change it to
for (int i = x1; i <= x2; i++)
then everything will work better at detecting an oncoming collision. You should probably do something similar for your future CollidesTop function.
P.S. Try these answers one at a time, of course. :)

Platform jumping problems with AABB collisions

When my AABB physics engine resolves an intersection, it does so by finding the axis where the penetration is smaller, then "push out" the entity on that axis.
Considering the "jumping moving left" example:
If velocityX is bigger than velocityY, AABB pushes the entity out on the Y axis, effectively stopping the jump (result: the player stops in mid-air).
If velocityX is smaller than velocitY (not shown in diagram), the program works as intended, because AABB pushes the entity out on the X axis.
How can I solve this problem?
Source code:
public void Update()
{
Position += Velocity;
Velocity += World.Gravity;
List<SSSPBody> toCheck = World.SpatialHash.GetNearbyItems(this);
for (int i = 0; i < toCheck.Count; i++)
{
SSSPBody body = toCheck[i];
body.Test.Color = Color.White;
if (body != this && body.Static)
{
float left = (body.CornerMin.X - CornerMax.X);
float right = (body.CornerMax.X - CornerMin.X);
float top = (body.CornerMin.Y - CornerMax.Y);
float bottom = (body.CornerMax.Y - CornerMin.Y);
if (SSSPUtils.AABBIsOverlapping(this, body))
{
body.Test.Color = Color.Yellow;
Vector2 overlapVector = SSSPUtils.AABBGetOverlapVector(left, right, top, bottom);
Position += overlapVector;
}
if (SSSPUtils.AABBIsCollidingTop(this, body))
{
if ((Position.X >= body.CornerMin.X && Position.X <= body.CornerMax.X) &&
(Position.Y + Height/2f == body.Position.Y - body.Height/2f))
{
body.Test.Color = Color.Red;
Velocity = new Vector2(Velocity.X, 0);
}
}
}
}
}
public static bool AABBIsOverlapping(SSSPBody mBody1, SSSPBody mBody2)
{
if(mBody1.CornerMax.X <= mBody2.CornerMin.X || mBody1.CornerMin.X >= mBody2.CornerMax.X)
return false;
if (mBody1.CornerMax.Y <= mBody2.CornerMin.Y || mBody1.CornerMin.Y >= mBody2.CornerMax.Y)
return false;
return true;
}
public static bool AABBIsColliding(SSSPBody mBody1, SSSPBody mBody2)
{
if (mBody1.CornerMax.X < mBody2.CornerMin.X || mBody1.CornerMin.X > mBody2.CornerMax.X)
return false;
if (mBody1.CornerMax.Y < mBody2.CornerMin.Y || mBody1.CornerMin.Y > mBody2.CornerMax.Y)
return false;
return true;
}
public static bool AABBIsCollidingTop(SSSPBody mBody1, SSSPBody mBody2)
{
if (mBody1.CornerMax.X < mBody2.CornerMin.X || mBody1.CornerMin.X > mBody2.CornerMax.X)
return false;
if (mBody1.CornerMax.Y < mBody2.CornerMin.Y || mBody1.CornerMin.Y > mBody2.CornerMax.Y)
return false;
if(mBody1.CornerMax.Y == mBody2.CornerMin.Y)
return true;
return false;
}
public static Vector2 AABBGetOverlapVector(float mLeft, float mRight, float mTop, float mBottom)
{
Vector2 result = new Vector2(0, 0);
if ((mLeft > 0 || mRight < 0) || (mTop > 0 || mBottom < 0))
return result;
if (Math.Abs(mLeft) < mRight)
result.X = mLeft;
else
result.X = mRight;
if (Math.Abs(mTop) < mBottom)
result.Y = mTop;
else
result.Y = mBottom;
if (Math.Abs(result.X) < Math.Abs(result.Y))
result.Y = 0;
else
result.X = 0;
return result;
}
It's hard to read code of other people, but I think this is one possible (purely brainstormed) workaround, although of course I'm not able to test it:
Before the collision detection happens, save the players velocity to some temporary variable.
After you've done your collision response, check if the players X or Y position has been corrected
If X position has been changed, manually reset (as a kind of "safety reset") the players Y velocity to the one that he had before the response.
By the way, what happens with your current code when your players hits the roof while jumping?
I had that same problem. Even the Microsoft platformer starterskit seems to have that bug.
The solution I found so far is to either use multisampling (argh) or to use the movedirection of the object: only move the distance between those 2 objects and no further when a collision is detected (and do that for each axis).
One possible solution I found is sorting the objects before resolving based on the velocity of the player.

Categories

Resources