I've formulated my own collision system in a modified version of OpenTK. It works by running a foreach loop which checks all the quads in the game (this runs when something moves) and sets their position back to where it was last frame, that is if it intersects this frame. I might not have explained that well, so here is the SetXVelocity code, called when a player moves right.
public void SetXVelocity(float x)
{
foreach (Quad quad in quads)
{
if (!quad.Equals(this))
{
if (Intersects(quad))
{
Velocity.x = 0;
Position = OldPosition;
continue;
}
if (!Intersects(quad))
{
OldPosition = Position;
Velocity.x = x;
continue;
}
}
else
{
OldPosition = Position;
continue;
}
OldPosition = Position;
continue;
}
}
Here is the code for Intersects:
public bool Intersects(Quad quad)
{
#region
if (TLCorner.x > quad.TLCorner.x && TLCorner.x < quad.BRCorner.x && TLCorner.y < quad.TLCorner.y && TLCorner.y > quad.BRCorner.y)
{
return true;
}
if (BRCorner.x < quad.BRCorner.x && BRCorner.x > quad.TLCorner.x && BRCorner.y > quad.BRCorner.y && BRCorner.y < quad.TLCorner.y)
{
return true;
}
if (TRCorner.x < quad.TRCorner.x && TRCorner.x > quad.BLCorner.x && TRCorner.y < quad.TRCorner.y && TRCorner.y > quad.BLCorner.y)
{
return true;
}
if (BLCorner.x > quad.BLCorner.x && BLCorner.x < quad.TRCorner.x && BLCorner.y > quad.BLCorner.y && BLCorner.y < quad.TRCorner.y)
{
return true;
}
#endregion // Corner Intersection
if (Math.Round(Left, 2) == Math.Round(quad.Right, 2) && TRCorner.y > quad.TRCorner.y && BRCorner.y < quad.BRCorner.y)
return true;
if (Math.Round(Right, 2) == Math.Round(quad.Left, 2) && TRCorner.y > quad.TRCorner.y && BRCorner.y < quad.BRCorner.y)
return true;
return false;
}
By the way, TRCorner is a Vector3 representing the Top Right Corner etc.
Last block of code is the Quad class, keep in mind, the actual class is huge, so I'll try not include all of it:
public Vector3 Velocity;
public Vector3 Position;
public int Index;
public VBO<Vector3> VertexData
{
get;
set;
}
public VBO<int> VertexCount
{
get;
set;
}
public VBO<Vector2> VertexTexture
{
get;
set;
}
For some reason, a few quads have no collision and some do. Some even have collision that makes the player stick to them.
I think some quads are missing because all possible conditions are not covered in your comparisons.
Since you && all your conditions, a collision needs to meet all your conditions to get detected.
if (TLCorner.x > quad.TLCorner.x && TLCorner.x < quad.BRCorner.x && TLCorner.y < quad.TLCorner.y && TLCorner.y > quad.BRCorner.y)
{
return true;
}
if( TLCorner.x == quad.TLCorner.x && TLCorner.y < quad.TLCorner.y && TLCorner.y > quad.BRCorner.y ) the rectangle still collides, but undetected.
Same for other conditions.
You might want to use >= and <= in your code in order to cover all conditions.
As for why they get stuck in one another, your velocity probably pushes them into a condition which makes them collide forever, since you detect the collision after they went into each other and not when the boundaries overlap.
Also you might want to reverse the velocity and "un-stuck" them when collision is detected in a way that does not make them stuck in other nearby rectangles.
I highly recommend you use better logic for collision detection
Link: https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
You can test it here http://jsfiddle.net/knam8/
Related
I'm trying to get the velocity of my character to be zero when it collides with another sprite. It does this, however the distance between the textures increases when it collides if I choose a higher value for speed, the higher the value the further the distance between the textures when collision occurs.
At a speed of 1f the collision seems to work fine (both textures touching) but 1f is too slow for my game and I can't figure out how to increase it without increasing the distance of collision.
I believe this is because I use: rectangle.Right + Velocity.X in my Collision region but I'm not sure how to alter this to fix my problem. Help would be appreciated.
//In Sprite class
#region Collision
protected bool IsTouchingLeft(Sprite sprite)
{
return rectangle.Right + Velocity.X > sprite.rectangle.Left &&
rectangle.Left < sprite.rectangle.Left &&
rectangle.Bottom > sprite.rectangle.Top &&
rectangle.Top < sprite.rectangle.Bottom;
}
protected bool IsTouchingRight(Sprite sprite)
{
return rectangle.Left + Velocity.X < sprite.rectangle.Right &&
rectangle.Right > sprite.rectangle.Right &&
rectangle.Bottom > sprite.rectangle.Top &&
rectangle.Top < sprite.rectangle.Bottom;
}
protected bool IsTouchingTop(Sprite sprite)
{
return rectangle.Bottom + Velocity.Y > sprite.rectangle.Top &
rectangle.Top < sprite.rectangle.Top &&
rectangle.Right > sprite.rectangle.Left &&
rectangle.Left < sprite.rectangle.Right;
}
protected bool IsTouchingBottom(Sprite sprite)
{
return rectangle.Top + Velocity.Y < sprite.rectangle.Bottom &
rectangle.Bottom > sprite.rectangle.Bottom &&
rectangle.Right > sprite.rectangle.Left &&
rectangle.Left < sprite.rectangle.Right;
}
#endregion
//In MainChar class
foreach (var sprite in sprites)
{
if (sprite == this)
{
continue;
}
if (Velocity.X > 0 && IsTouchingLeft(sprite) || Velocity.X < 0 && IsTouchingRight(sprite))
{
Velocity.X = 0;
}
if (Velocity.Y > 0 && IsTouchingTop(sprite) || Velocity.Y < 0 && IsTouchingBottom(sprite))
{
Velocity.Y = 0;
}
if (sprite.rectangle.Intersects(rectangle))
{
hasDied = true;
}
}
Handling multiple collisions correctly will require sub-stepping, repeating the collision checks until there are no more collisions or a counter expires. This is how physics engines operate.
I am going to show a different method. It requires not doing the move that caused the collision and adjusting the velocity by half each step. Slow down until you get there a maximum of log2(Velocity) steps(for velocities < 32, 5 steps):
foreach (var sprite in sprites)
{
if (sprite == this)
{
continue;
}
// it is good practice to use () for mixed && and ||
if ((Velocity.X > 0 && IsTouchingLeft(sprite)) || (Velocity.X < 0 && IsTouchingRight(sprite)))
{
Velocity.X *= 0.5f;
// by halving, the velocity will exponentially decay to 0
}
if ((Velocity.Y > 0 && IsTouchingTop(sprite)) || (Velocity.Y < 0 && IsTouchingBottom(sprite)))
{
Velocity.Y *= 0.5f;
}
if (sprite.rectangle.Intersects(rectangle))
{
hasDied = true;
}
// use new velocity to update the position.
}
You may have to fix the death condition, it is unclear when it should be triggered. The code as written will move up to the other object, but never overlap on its own.
The following code is what I believe you intended(move at a speed of 1 until overlap)
foreach (var sprite in sprites)
{
if (sprite == this)
{
continue;
}
// it is good practice to use () for mixed && and ||
if ((Velocity.X > 0 && IsTouchingLeft(sprite)) || (Velocity.X < 0 && IsTouchingRight(sprite)))
{
if (Velocity.X >= 2) // cap it at a minimum above 1
{
Velocity.X *= 0.5f;
// by halving, the velocity will exponentially decay to 0
}
if (sprite.rectangle.Intersects(rectangle))
{
Velocity.X = 0; // probably need to do this for Y here as well.
hasDied = true;
}
}
if ((Velocity.Y > 0 && IsTouchingTop(sprite)) || (Velocity.Y < 0 && IsTouchingBottom(sprite)))
{
if (Velocity.Y >= 2) // cap it at a minimum above 1
{
Velocity.Y *= 0.5f;
// by halving, the velocity will exponentially decay to 0
}
if (sprite.rectangle.Intersects(rectangle))
{
Velocity.Y = 0; // probably need to do this for X here as well.
hasDied = true;
}
}
}
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++;
}
}
}
How do you add multi-tap (not multi-touch!) input to a Unity3D game? I have a hard time finding any useful information about this.
What I got so far is multi-touch input support. this is what I'm using for that:
private Vector2 _touchOrigin = -Vector2.one;
public bool TouchEnded(int touchCount = 1)
{
if (Input.touchCount != touchCount) return false;
Touch lastTouch = Input.GetTouch(touchCount - 1);
if (lastTouch.phase == TouchPhase.Began)
{
_touchOrigin = lastTouch.position;
}
else if (lastTouch.phase == TouchPhase.Ended && _touchOrigin.x >= 0)
{
_touchOrigin.x = -1;
return true;
}
return false;
}
What I would like to do is write a similar method but for multi-tap. I.e. the user has to tap several fingers (touchCount) simultaneously a number of times (tapCount). This would be the method signature:
public bool TapEnded(int touchCount = 1, int tapCount = 2)
{
}
Can somebody give me some help how to get this requirement working?
You can use Input.Touches Returns list of objects representing status of all touches during last frame. reference to link http://docs.unity3d.com/ScriptReference/Input-touches.html
void Update() {
int fingerCount = 0;
foreach (Touch touch in Input.touches) {
if (touch.phase != TouchPhase.Ended && touch.phase != TouchPhase.Canceled)
fingerCount++;
}
if (fingerCount > 0)
print("User has " + fingerCount + " finger(s) touching the screen");
}
i have problem with blocking movement i already read question on Blocking Movement On Collision
but i dont have any idea for my own problem.
if you can give me the logic i will try to solve my own problem.
i hope you can help me
this my player class update
:EDIT: thanks for Kai Hartmann for reply
my problem is for 2D graphic and i want to know how to stop movement when object1 collision with object2
public void Update(GameTime gameTime)
{
// Ani Test
up_ani.Update(gameTime);
down_ani.Update(gameTime);
left_ani.Update(gameTime);
right_ani.Update(gameTime);
position += velocity;
if (Keyboard.GetState().IsKeyDown(key.MoveUp) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Up;
velocity.Y = -3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Down;
velocity.Y = 3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveRight) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveLeft))
{
currentFace = FacePosition.Right;
velocity.X = 3;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveLeft) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveUp))
{
currentFace = FacePosition.Left;
velocity.X = -3;
}
else
{
//currentFace = FacePosition.Down;
velocity = Vector2.Zero;
}
prevState = Keyboard.GetState();
}
and this my collision
public void IntersectWithScore(Vector2 vector1, Vector2 vector2, Texture2D object1, Texture2D object2, int doSomethingToScore, bool isIncrease)
{
if (vector1.X + object1.Width < vector2.X || vector1.X > vector2.X + object2.Width ||
vector1.Y + object1.Height < vector2.Y || vector1.Y > vector2.Y + object2.Height)
{
}
else
{
player1.Velocity = Vector2.Zero;
}
}
No need for a collision check Method as far as I'm concerned :) I've written this code really quickly but should work okay?(ish). I've assumed you want to check if something is in the player's way and also assumed that there are more than one objects that could be in your players way so to do this I suggest making a class for the 'Solid' object and giving it a position, width and height. Then make a list of this class to contain the multiple 'solid' objects.
// collidable object's Class name = SolidBlock
SolidBlock newSolidBlock;
List<SolidBlock> solidBlocksList = new List<SolidBlock>();
// This will create 10 'solid' objects and adds them all to a list
// This would preferably go in the Load() function so you can set the positions of
// the blocks after creating them (after the loop)
int i = 0;
while (i < 10)
{
newSolidBlock = new SolidBlock(Vector2, position) // Assuming width and height
// are set within class
solidBlocksList.Add(newSolidBlock);
i++;
}
// It doesn't matter how you create them, the important lines here are the creation of a
// newSolidBlock and then the line adding it to the list of blocks.
solidBlocksList[0].position = new Vector (20, 50); // < for example this is how you would set
// a block's position.
Then in your players update function you refer to this list of objects.
// Input a list of the objects you want your player to collide with
// this is a list in case you have multiple object2s you want to check
// for collision.
// An Object2 should have a position, height, and width, yes?
public void Update(GameTime gameTime, List<Object2> object2List)
{
// Ani Test
up_ani.Update(gameTime);
down_ani.Update(gameTime);
left_ani.Update(gameTime);
right_ani.Update(gameTime);
position += velocity;
if (Keyboard.GetState().IsKeyDown(key.MoveUp) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.Y <= o.position.Y + o.height)
{
velocity.Y = 0;
}
else
{
velocity.Y = -3;
}
}
currentFace = FacePosition.Up;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.Y + playerWidth >= o.position.Y)
{
velocity.Y = 0;
}
else
{
velocity.Y = 3;
}
}
currentFace = FacePosition.Down;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveRight) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveUp) && prevState.IsKeyUp(key.MoveLeft))
{
foreach (Object2 o in object2List)
{
if (position.X + playerWidth >= o.position.X)
{
velocity.X = 0;
}
else
{
velocity.X = 3;
}
}
currentFace = FacePosition.Right;
}
else if (Keyboard.GetState().IsKeyDown(key.MoveLeft) && prevState.IsKeyUp(key.MoveDown) && prevState.IsKeyUp(key.MoveRight) && prevState.IsKeyUp(key.MoveUp))
{
foreach (Object2 o in object2List)
{
if (position.X <= o.position.X + o.width)
{
velocity.X = 0;
}
else
{
velocity.X = -3;
}
}
currentFace = FacePosition.Left;
}
else
{
velocity = Vector2.Zero;
}
prevState = Keyboard.GetState();
}
Sorry if this isn't much help, especially the first block of code, that was very much an example of how you could create multiple object2s.
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.