Collision Detection, player correction - c#

I am having some problems with collision detection, 2 have 2 types of objects aside from the player it self. Tiles and what I Call MapObjects.
The tiles are all 16x16, where the MapObjects can be any since, but is my problem case are they as well 16x16.
When my player runs apon the mapobjects or tiles dose it get very jaggy. The player is unable to move right, and will get warped forward when moving left.
I have found the problem, and that is my collision detection will move the player left/right if colliding the object from the side, and up/down if collision from up/down.
Now imagen that my player is sating on 2 tiles, at 10,12 and 11,12 and the player is mostly standing on the 11,12 tile. The collision detection will first run on then 10,12 tile, it calculates the collision depth, and finds that is is a collision from the side, and therefore more the object to the right. After will it do collision detection with 11,12 here will it move the character up. So the player will not fall down, but are unable to move right. And when moving left will the same problem make the player warp forward.
This problem have been bugging me for a few days now, and I just can't find a solution!
Here is my code that dose the collision detection.
public void ApplyObjectCollision(IPhysicsObject obj, List<IComponent> mapObjects, TileMap map)
{
PhysicsVaraibales physicsVars = GetPhysicsVariables();
Rectangle bounds = ((IComponent)obj).GetBound();
int leftTile = (int)Math.Floor((float)bounds.Left / map.GetTileSize());
int rightTile = (int)Math.Ceiling(((float)bounds.Right / map.GetTileSize())) - 1;
int topTile = (int)Math.Floor((float)bounds.Top / map.GetTileSize());
int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / map.GetTileSize())) - 1;
// Reset flag to search for ground collision.
obj.IsOnGround = false;
// For each potentially colliding tile,
for (int y = topTile; y <= bottomTile; ++y)
{
for (int x = leftTile; x <= rightTile; ++x)
{
IComponent tile = map.Get(x, y);
if (tile != null)
{
bounds = HandelCollision(obj, tile, bounds, physicsVars);
}
}
}
// Handel collision for all Moving objects
foreach (IComponent mo in mapObjects)
{
if (mo == obj)
continue;
if (mo.GetBound().Intersects(((IComponent)obj).GetBound()))
{
bounds = HandelCollision(obj, mo, bounds, physicsVars);
}
}
}
private Rectangle HandelCollision(IPhysicsObject obj, IComponent objb, Rectangle bounds, PhysicsVaraibales physicsVars)
{
// If this tile is collidable,
SpriteCollision collision = ((IComponent)objb).GetCollisionType();
if (collision != SpriteCollision.Passable)
{
// Determine collision depth (with direction) and magnitude.
Rectangle tileBounds = ((IComponent)objb).GetBound();
Vector2 depth = bounds.GetIntersectionDepth(tileBounds);
if (depth != Vector2.Zero)
{
float absDepthX = Math.Abs(depth.X);
float absDepthY = Math.Abs(depth.Y);
// Resolve the collision along the shallow axis.
if (absDepthY <= absDepthX || collision == SpriteCollision.Platform)
{
// If we crossed the top of a tile, we are on the ground.
if (obj.PreviousBound.Bottom <= tileBounds.Top)
obj.IsOnGround = true;
// Ignore platforms, unless we are on the ground.
if (collision == SpriteCollision.Impassable || obj.IsOnGround)
{
// Resolve the collision along the Y axis.
((IComponent)obj).Position = new Vector2(((IComponent)obj).Position.X, ((IComponent)obj).Position.Y + depth.Y);
// If we hit something about us, remove all velosity upwards
if (depth.Y > 0 && obj.IsJumping)
{
obj.Velocity = new Vector2(obj.Velocity.X, 0);
obj.JumpTime = physicsVars.MaxJumpTime;
}
// Perform further collisions with the new bounds.
return ((IComponent)obj).GetBound();
}
}
else if (collision == SpriteCollision.Impassable) // Ignore platforms.
{
// Resolve the collision along the X axis.
((IComponent)obj).Position = new Vector2(((IComponent)obj).Position.X + depth.X, ((IComponent)obj).Position.Y);
// Perform further collisions with the new bounds.
return ((IComponent)obj).GetBound();
}
}
}
return bounds;
}

Related

OverlapSphere centering incorrectly

I am attempting to use an overlapSphere to determine whether a tile within a tile map contains a game object. Then, if the tile is not already occupied, a new game object will be spawned in that tile (if certain other conditions are met). I'm trying to prevent game objects from overlapping with one another. However, I am having trouble centering the spheres about the right location.
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Vector3 tilePos = tileMap.GetCellCenterWorld(new Vector3Int(x, y, 0));
if (terrainMap[x, y] == blocked)
{
bool spawn = detectOverlap(tilePos, obstCheckRad);
if (spawn)
{
GameObject obstacle = Instantiate(obstaclePrefab) as GameObject;
obstacle.transform.position = tilePos;
obstacle.transform.eulerAngles = new Vector3(0, 0, (Random.Range(0, 359)));
}
}
}
}
}
public bool detectOverlap (Vector3 center, float radius)
{
Collider[] collider = Physics.OverlapSphere(center, radius);
if (collider.Length == 0)
{
return true;
}
else
{
return false;
}
}
I am using "GetCellCenterWorld" to as the center of my overlapSpheres, however they always seem to originate in the bottom left corner of the map, causing this region of the map to be completely empty, and objects elswhere in the map to still overlap with one another. (see image)
My confusion is based around the fact that I am using the same vector3 (tilePos) to place down the game objects. The objects are all moved to the correct position, however, the overlapSpheres all stay at the bottom left.

Can't seem to get smooth 3D collision XNA

I am making a 3D game. The player is the "camera". I want it not to go through walls which is achieved. But now I want it to be able to "glide" along the wall as in any other fps. Here is the code: and thanks in advance:
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
keyState = Keyboard.GetState();
camera.Update(gameTime);
if (keyState.IsKeyDown(Keys.W)) camera.moveVector.Z = 1;
if (keyState.IsKeyDown(Keys.S)) camera.moveVector.Z = -1;
if (keyState.IsKeyDown(Keys.A)) camera.moveVector.X = 1;
if (keyState.IsKeyDown(Keys.D)) camera.moveVector.X = -1;
if (keyState.IsKeyDown(Keys.Space)&&camera.Position.Y>=0.5f) camera.moveVector.Y = 0.5f;
if (camera.moveVector != Vector3.Zero)
{
//We don't want to make the player move faster when it is going diagonally.
camera.moveVector.Normalize();
//Now we add the smoothing factor and speed factor
camera.moveVector *= (dt * camera.cameraSpeed);
Vector3 newPosition = camera.PreviewMove(camera.moveVector);
bool moveTrue = true;
if (newPosition.X < 0 || newPosition.X > Map.mazeWidth) moveTrue = false;
if (newPosition.Z < 0 || newPosition.Z > Map.mazeHeight) moveTrue = false;
foreach (BoundingBox boxes in map.GetBoundsForCell((int)newPosition.X, (int)newPosition.Z))
{
if (boxes.Contains(newPosition) == ContainmentType.Contains)
{
moveTrue = false;
}
}
if (moveTrue) camera.Move(camera.moveVector);
base.Update(gameTime);
And here is the code for excecuting the movement:
//Updating the look at vector
public void UpdateLookAt()
{
//Built a rotation matrix to rotate the direction we are looking
Matrix rotationMatrix = Matrix.CreateRotationX(cameraRotation.X) * Matrix.CreateRotationY(cameraRotation.Y);
// Build a look at offset vector
Vector3 lookAtOffset = Vector3.Transform(Vector3.UnitZ, rotationMatrix);
//Update our camera's look at the vector
cameraLookAt = (cameraPosition + lookAtOffset);
}
//Method to create movement and to check if it can move:)
public Vector3 PreviewMove(Vector3 amount)
{
//Create a rotation matrix to move the camera
Matrix rotate = Matrix.CreateRotationY(cameraRotation.Y);
//Create the vector for movement
Vector3 movement = new Vector3(amount.X, amount.Y, amount.Z);
movement = Vector3.Transform(movement, rotate);
// Give the value of the camera position +ze movement
return (cameraPosition+movement);
}
//Method that moves the camera when it hasnt'collided with anything
public void Move(Vector3 scale)
{
//Moveto the location
MoveTo(PreviewMove(scale), Rotation);
}
Already thought of using the invert method given by xna. But I can't seem to find the normal. And I have tried to move the camera parallel to the wall. But I was unable to achieve that. Any help is appreicated.
If you have found that a point intersects a bounding box, you have to check which of the six faces the entry point lies in. This can be done as follows: Construct a line segment between the old camera position and the new one:
p = (1 - t) * oldPos + t * newPos
, where you use only the dimension of oldPos and newPos, which is interesting for the face (e.g. for the left/right face, take the x-coordinate). p is the according coordinate for the face. Calculate t for every face and find the face for which t is maximal, ignoring faces behind which the point already lies (i.e. the dot product of the face normal and the direction from the face to the point is negative). This will be the face, in which your entry point lies. All you then need to do is adapt the relevant coordinate (again the x-coordinate for left/right face etc.), such that it does not lie within the bounds (e.g. set newPos.x = boundingBox.MaxX for the right face). This is equal to a projection of the point onto the bounding box surface and equivalent to using only the component of the movement vector that is parallel to the box if an intersection would occur).
Btw, the solution of the above formula is:
t = (p - oldPos) / (newPos - oldPos)

Difficulties selecting a moving gameobject

I am developing a mobile game with touch controls. I can select a non-moving gameobject very easily and it responds, however it's very difficult to select it when it's moving since it's kind of small (falling for example, it's all physics based). Is there a way to increase the touch radius of the gameobject so that it can be pressed more easily, or is there another solution?
private void Update() {
//User input (touches) will select cubes
if(Input.touchCount > 0) {
Touch touch = Input.GetTouch(0);
}
Touch[] touches = Input.touches;
foreach(var touchInput in touches) {
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint((Input.GetTouch(0).position)), Vector2.zero);
if (hit.collider != null) {
selectedCube = hit.collider.gameObject;
selectedCube.GetComponent<SpriteRenderer>().color = Color32.Lerp(defaultColor, darkerColor, 1);
}
}
}
Rather than increasing the object's collider size (which you discussed in the comments), how about approaching this the opposite way? Check in an area around the touch for a collision, instead of just the single point, using Physics2D.CircleCast:
private void Update() {
//User input (touches) will select cubes
if(Input.touchCount > 0) {
Touch touch = Input.GetTouch(0);
}
Touch[] touches = Input.touches;
foreach(var touchInput in touches) {
float radius = 1.0f; // Change as needed based on testing
RaycastHit2D hit = Physics2D.CircleCast(Camera.main.ScreenToWorldPoint((Input.GetTouch(0).position)), radius, Vector2.zero);
if (hit.collider != null) {
selectedCube = hit.collider.gameObject;
selectedCube.GetComponent<SpriteRenderer>().color = Color32.Lerp(defaultColor, darkerColor, 1);
}
}
}
Note that this won't be great if you've got tons of selectable objects flush against each other...but then again, increasing collider size in that case wouldn't help either. (I'd say just increase object size. No way to otherwise improve user accuracy. Or allow multi-select, and use Physics2D.CircleCastAll).
Hope this helps! Let me know if you have any questions.
EDIT: For better accuracy, since the "first" result returned by Physics2D.CircleCast may be arbitrarily selected, you can instead use Physics2D.CircleCastAll to get all objects within the touch radius, and only select the one which is closest to the original touch point:
private void Update() {
//User input (touches) will select cubes
if(Input.touchCount > 0) {
Touch touch = Input.GetTouch(0);
}
Touch[] touches = Input.touches;
foreach(var touchInput in touches) {
float radius = 1.0f; // Change as needed based on testing
Vector2 worldTouchPoint = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
RaycastHit2D[] allHits = Physics2D.CircleCastAll(worldTouchPoint, radius, Vector2.zero);
// Find closest collider that was hit
float closestDist = Mathf.Infinity;
GameObject closestObject = null;
foreach (RaycastHit2D hit in allHits){
// Record the object if it's the first one we check,
// or is closer to the touch point than the previous
if (closestObject == null ||
Vector2.Distance(closestObject.transform.position, worldTouchPoint) < closestDist){
closestObject = hit.collider.gameObject;
closestDist = Vector2.Distance(closestObject.transform.position, worldTouchPoint);
}
}
// Finally, select the object we chose based on the criteria
if (closestObject != null) {
selectedCube = closestObject;
selectedCube.GetComponent<SpriteRenderer>().color = Color32.Lerp(defaultColor, darkerColor, 1);
}
}
}

Remembering a direction, moving to a point, then changing direction. XNA

I'm making a pacman clone in XNA.
So far I've drawn the tile map using 2D array, added the pills using another 2D array and made a 2D array that allows movement of pacman.
In the actual game you can press right whilst moving up, and it will wait until you're able to move right and the turn.
I have a system in place that allows a turn only when the spritePosition % 32 = 16.
This means the sprite will be centred between the walls.
I need the program to remember the last key pressed or move to the right position before turning, but i cant find a way of doing it.
Here is a bit of the code that covers what I'm trying.
public void MovementCheck()
{
presentKey = Keyboard.GetState();
spritePosition = spriteVelocity + spritePosition;
pacManRec = new Rectangle((int)spritePosition.X, (int)spritePosition.Y, pacManTex.Width, pacManTex.Height);
spriteOrigin = new Vector2(pacManRec.Width / 2, pacManRec.Height / 2);
//Press Right
if (presentKey.IsKeyDown(Keys.Right) && pastKey.IsKeyUp(Keys.Right))
{
Right();
}
}
private void Right()
{
direction = "right";
//if the next block in tile map to the right is a 1, and the sprite is centred - allow a turn
if (inputMap[(int)Math.Floor(spritePosition.Y / 32), (int)Math.Floor(spritePosition.X / 32) + 1] == 1 && (spritePosition.Y % 32 == 16))
{
rotation = ((float)Math.PI / 180);
spriteVelocity.X = 0;
spriteVelocity.Y = 0;
spriteVelocity.X = movementSpeed;
}
}
Only the right key is shown, the others are similar but the directions all change and the checks to the tile map are changed accordingly. (+1 on the X here)
ive tried things like
while (spritePosition.Y % 32 != 16)
{ spritePosition = spriteVelocity + spritePosition; }
but that just makes the sprite shoot up the screen, (kinda obviously) :(
and I tried a new Method before the Right() call
bool RightCheck()
{
if ( CONDITIONS MET HERE )
return true
else
{
//dont remember if I used this line, but something similar
spritePosition = spriteVelocity + spritePosition;
RightCheck()
}
return false; //allows program to build
}
Just an causes infinite recursion.
One solution is adding a int counter = 0; which you update in your gameloop (with counter++;) every frame/time-step. Set to 0 everytime you make a valid input and save that input.
Outlined code:
public class GameClassWhereUpdateIsDone
{
private enum Directions { None, Up, Down, Left, Right };
private int counter = 0;
private Directions currentDirection; // Current movement-direction
private Directions lastInput; // Last direction from input
public void Update(...)
{
var keyboardState = Keyboard.GetState();
if(keyboardState.IsKeyPressed(Keys.Right))
{
counter = 0;
direction = Directions.Right;
}
if(currentDirection != lastInput && counter < 5) // Allow turning 5 updates ahead.
{
// Player want to turn
if(AllowedToTurn(lastInput)
{
currentDirection = lastInput;
}
}
MoveDirection(currentDirection);
counter++;
}
private bool AllowedToTurn(Directions direction)
{
if(direction == Directions.Right)
{
return RightCheck();
}
}
}
The key idea is to keep track of movement direction and last direciton that was input...
In the original Pac-Man "pre-turning" was actually used, meaning you would start moving diagonally if you turned ahead of a corner according to: http://home.comcast.net/~jpittman2/pacman/pacmandossier.html which is an interesting read.

having trouble implementing physics to ball collision program

I'm currently working on a simple ball collision program in C#, where an indeterminate amount of balls bounce around the screen, each being added to the program on a button click. So far I've gotten everything working, and I have a basic collision system. Now, I'm trying to add more realistic physics to the balls.
The thing is, I have little understanding of physics. I've been reading a bit about vector physics and I get the gist of it, but I'm not quite making the connection between what I've read and how I'm supposed to implement these concepts programmatically. Could anybody provide me some resources on how one is actually supposed to implement vector physics to something like a simple ball collision program like I've written?
Here's what I have right now. It's basically just two booleans determining whether my ball goes in one of four directions. When two balls collide, they just pass their directions to each other.
public void newball(PaintEventArgs e, int panelWidth, int panelHeight)
{
//Detects walls of form. if it detects a wall or another ball, it bounces
if (X >= panelWidth)
{
bounceX = true;
}
else if (X <= 25)
{
bounceX = false;
}
if (Y >= panelHeight )
{
bounceY = true;
}
else if (Y <= 25)
{
bounceY = false;
}
//balls only go in four directions right now have to implement vector physics.*/
if (bounceX == false)
{
X = X+2;
}
else if (bounceX == true)
{
X = X -2;
}
if (bounceY == false)
{
Y = Y+2;
}
else if (bounceY == true)
{
Y = Y-2;
}
//this draws the ball on the panel.
Pen clsPen = Pens.Black;
Color color = Color.Black;
SolidBrush brush = new SolidBrush(color);
e.Graphics.DrawEllipse(clsPen, X - 25, Y -25, 25, 25);
e.Graphics.FillEllipse(brush, X-25, Y-25, 25, 25);
}
And here are some of the things I've read:
2D Collisions, Vectors

Categories