I have a problem with AABB collision resolution.
I resolve AABB intersection by resolving the X axis first, then the Y axis.
This is done to prevent this bug: http://i.stack.imgur.com/NLg4j.png
The current method works fine when an object moves into the player and the player has to be pushed horizontally. As you can see in the .gif, the horizontal spikes push the player correctly.
When the vertical spikes move into the player, however, the X axis is still resolved first. This makes "using the spikes as a lift" impossible.
When the player moves into the vertical spikes (affected by gravity, falls into them), he's pushed on the Y axis, because there was no overlap on the X axis to begin with.
Something I tried was the method described in the first answer of this link.
However the spikes and moving objects move by having their position changed, not velocity, and I don't calculate their next predicted position until their Update() method is called.
Needless to say this solution didn't work either. :(
I need to solve AABB collision in a way that both of the cases described above work as intended.
This is my current collision source code: http://pastebin.com/MiCi3nA1
I'd be really grateful if someone could look into this, since this bug has been present in the engine all the way back from the beginning, and I've been struggling to find a good solution, without any success. This is seriously making me spend nights looking at the collision code and preventing me from getting to the "fun part" and coding the game logic :(
I tried implementing the same collision system as in the XNA AppHub platformer demo (by copy-pasting most of the stuff). However the "jumping" bug occurs in my game, while it doesn't occur in the AppHub demo.
[ jumping bug: http://i.stack.imgur.com/NLg4j.png ]
To jump I check if the player is "onGround", then add -5 to Velocity.Y.
Since the player's Velocity.X is higher than Velocity.Y (refer to the fourth panel in the diagram), onGround is set to true when it shouldn't be, and thus lets the player jump in mid-air.
I believe this doesn't happen in the AppHub demo because the player's Velocity.X will never be higher than Velocity.Y, but I may be mistaken.
I solved this before by resolving on the X axis first, then on the Y axis. But that screws up the collision with the spikes as I stated above.
Why not resolve on the Y-axis first for vertical spikes, and on the X-axis first for horizontal spikes?
Nice graphic, by the way.
As I understand it, you're handling movement and collision something like this:
Move all objects.
For each object O, test for intersection between the player and O, and if necessary, eject the player horizontally or vertically so that it is no longer intersecting with O.
If the player is still intersecting with some object, then (something).
This means that when you come to step (2), you have forgotten which way the object O was moving, so you can't tell if it is trying to push the player upwards or sideways.
Solution: in step (1), store for each object the direction it is moving. When you find the player intersecting with an object, you can look to see whether the object is moving up, down, left or right, and that will tell you which way to perform the ejection.
As Gareth Rees already said, the issue is after a collision is detected, you need more information (both current location and either direction came from or last position) to perform the collision response.
It gets quite complicated if both objects are moving. Instead, choose one object to be the frame of reference and subtract its velocity from everything else.
A straight forward solution might be to create line segments for the movement/delta of the non-frame-of-reference object. Then intersect those segments with the 4 AABB edges. This gives the time of intersection and the normal at the point of intersection. Then you can apply the same response you have now.
One possible solution I found is sorting the objects before resolving based on the velocity of the player.
Related
I recently started having a look at game development with Unity and was trying to make a simple 2D character with basic movement abilities. This character is supposed to jump and move from side to side, but only if it is standing on something.
Now my question is: How do you check if a player is standing on something? / Get the distance to the next game object / collider beneath the player game object?
Would greatly apreciate any helpful answers and especially explanations on how exactly it works. Thanks!
To do this, you need to send a ray to detect the point of impact on the ground and then calculate the distance. The code below sends a ray from the center of your object down to the maximum height (3) and gives the size.
public LayerMask groundLayer;
public float maxRayLength = 3;
public void Update()
{
var hit = Physics2D.Raycast(transform.position, Vector3.down, maxRayLength, groundLayer.value);
if (hit) Debug.Log(hit.distance); // it will print current distance from pivot
}
If you want to calculate the height of the ray from the character's foot, there are two methods, one is subtracting half the height of the character from it.
Physics2D.Raycast(transform.position-transform.up*height, ....)
Next one is to use an empty object at the base of the character, which we refer to instead of the center.
public Transform pivot;
Then..
Physics2D.Raycast(pivot, ....)
There are a few ways of actually doing this.
The most usual although a bit complicated way of doing it for a beginner is using Raycasts. A Raycast is basically a small invisible line that starts and ends where you tell it to. If anything of a specific tag or layer is caught in it's crossfire you can basically pull that object from your code. Raycasts are used for a lot of things, most notably in Shooter games to shoot or in games like Skyrim to pickup objects and interact with them.
Another way to do this, which is a bit more popular in 2D games is to create a "feet" GameObject and make it the child of the player in the hierarchy. You can add a box collider on that GameObject and check the "IsTrigger". You can add a Tag to your ground objects and through your code using the OnTriggerEnter() and OnTriggerExit() Methods you can basically tell when your character is floating on air and when he is on ground.
Another popular method is to use the Physics.OverlapBox() Method which is pretty much the same as the Trigger Method but you are creating an invisible box (much like a raycast) and instead of only getting notified when Triggered (something enters or exits) you check if the invisible box is colliding with another object/tag/collider (which could be your ground).
There are also a few different things you can do with a Nav Mesh (mostly in 3D) but I think that for now these 3 should suffice!
I've create a movement script that moves a character (ship) in a topdown view.
Gravity is disabled, but the rest of the settings i left mostly alone.
I'm applying a constant force in the forward or backward direction (local, so relative to the character NOT the world). This however causes some movement in other directions if the character is rotated for some reason.
Debug.Log(nameof(force) + force);
rigidBody2D.AddRelativeForce(force);
Lets me explain with pictures.
Starting point:
Moving forward somewhat (works okay):
Back to starting point (stopping game, starting anew)
And then i manually change the rotation to -90 in the inspector.
I then move forward somewhat.
As we can see since the rotation is -90, moving forward in relative to the character (AddRelativeForce(..)) causes it to move to the right on the screen.
The X position increments accordingly.
However, also the Y component changes, the character gets velocity in this direction, even though i apply no force in that direction - which we can see in the output.
How can I move my character with force without this issue?
Edit:
Here are the project files. Runs under the latest unity 2020.1.14f1. The logic mentioned here is located in MoveExecuteSystem. I use a small ECS framework: https://github.com/Leopotam/ecs
https://wetransfer.com/downloads/b86dfb27b4388b85ac4fef285d87b39520201124193329/00af66
Take into account that AddRealtiveForce adds the force in the local coords system. If the force vector has got always the same value, the outcome is going to be different depending on the rotational position of the entity.
To solve this, you need to apply the force such as: Vector3 force = myShip.forward * forceValue where force value can be an int or a float.
It can be myShip.up or myShip.right depending on what you want. My point is that with the .forward, .up or .right you always will apply the force according to the instant local coordinate system rotational position.
EDIT:
This question seems deeper than I thought, I made the trial in a project myself, and actually there are variations in the position when you apply force only when the object is rotated. Had a quick look and don't know why this is. Seem to be a precisiĆ³n thinh regarding the rotations of the physics the rigidbody uses under the hood.
I tried to remove the friction which I thought might be also cause of the problem but seems not possible. +1 to this question and lookig forward to know the answer.
EDIT2:
Bounciness and friction can be set to 0 if a new physics material 2d is created, but the issue persists.
You can freeze position in determined axis as a solution, but this does not explain the position variation problem.
How can I make a gameObject rotate while jumping "in the air", then once it collides with another gameObject, return smoothly to its original rotation like shown in the following video: https://youtu.be/iOV0Apuwj94
I do not want the cube to abruptly return to its original rotation once it has collided with something. Like in the video, the cube's rotation is just right when it collides (when it hits the ground, it feels natural). I also want the cube to know where the future collision is, so that it can modify speed, rotation etc. depending on the positions of each gameObject (this way, the rotation would be always correct too).
I have tried many times fine tuning the rotation, but I always fail to get it just right (+predicting future collisions is unknown to me). I do not have the experience to accomplish such a task and searching the Web did not help either. I would appreciate any lines of code, guidance or help from the community. Thank you for your answers.
Here is a pseudocode I can think of which might work for you:
while(cube.y> 0)//or greater than the right value of y
{
if(cube.y= 0)//or the right value of y
{//stop performing rotation}
//perform rotation
}
This might just solve all your problems, since you are using a RigidBody for the cube which has a collider and it should automatically align to the ground due to gravity, in my opinion, making it feel even more natural.
I am testing out some ideas in Unity where a player can walk around a circle while staying on it (so the circle has its own gravity) and also being oriented properly. This game is currently being done in 2D, so all objects are sprites.
I do hope I can explain myself properly. Please ask if you need any further clarification...
It appeared that I succeeded with my idea until I noticed something odd.
So as expected, the player moves around the circle without falling off (custom gravity worked just fine) and its Z rotation is affected as it aligns itself with a direction:
// Align code:
// We reverse the direction so the object is standing up the right way.
private void Update()
{
transform.up = -(planet.position - transform.position);
}
It works... mostly. However, when the player object's rotation Z naturally reaches 180, it appears to flip horizontally (like a mirror effect) and then it returns to normal as rotation Z leaves 180. The visual flip happens because for some reason the object's Y becomes 180 at the same time too. At no other point does X or Y change in regards to rotation. Only Z. So the moment Z hits 180, Y is affected and the moment we leave Z 180, Y returns to 0.
I'm happy to provide a quick video of it happening in-game if anybody needs some visual understanding of what's going on.
The visibility of this bug tends to rely on how fast you're moving around the circle. If you're moving fast enough, you can probably skip over 180 and not see it happen at all, however if you move slow enough there's no denying it's there. It's also problematic for the fact that I simply make the camera a child of the player so when the player flips, so does the camera causing the entire scene to flip which can look extremely glitchy for a player to see.
I really have no idea how to tackle this issue as I have no clue why it would do such a thing. At every other rotation value it behaves just fine. It's only at Z = 180 (so the object is exactly upside down) does it decide to rotate in the wrong ways.
EDIT: Changed tag to Unity3D
It's probably because of converting quaternions (which is what's actually used internally for rotations) to euler angles for display. Can you try to use Quaternion.Slerp to rotate?
I am writting a collision detection engine for my game and I have some problems.
Indeed, since I have several fixed rectangle and one moving (the player), I need to know which side of the fixed one was collided at first by the player, to replace him correctly.
The fixed rectangle are NOT in a grid so they can be placed anywhere on the map and they can have diffents size. They are not rotated.
The player class stores it's direction vector.
Any idea?
KiTe
In a nutshell:
You'll compare the Y and X components of the bounding rectangles to eachother to check for a collision. If the top(Y) of the player is less than the bottom of an enemy then you don't need to check anymore because it's not possible that they're colliding. If the right(X) side of the player is less than the left side of the enemy then they can't be colliding. It would help to define top, right, bottom, left of each object you intend to check inside the class. This will allow you to know which side is hit also. This should be enough to get you thinking and experimenting.
Have fun!
The name is "Axis-Aligned Bounding Box" collision detection.
Now you know the name, you can Google for the rest.
thanks to both of you for your help.
I've heard about AABB, but at first sight it didnt seem to fit to my needs (since I didn't understand it well).
But after writing down everything on papper, the solution I found appeared to be exactly the same as AABB !