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!
Related
I'm making a 2D platformer and decided to add sticky platforms. I've made the platforms move, but the player doesn't move with them.
However, after parenting the player to the platform, the player still falls through. I have added two BoxCollider2Ds and set one of them as a Trigger. None of the colliders have a RigidBody2D
public class StickyPlatform : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.name == "Player")
collision.gameObject.transform.SetParent(transform);
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.gameObject.name == "Player")
collision.gameObject.transform.SetParent(null);
}
}
trigger does not stop object move through the box, you can try OnCollisionEnter.
Usually I would have a ground check and you can say if(isGrounded&&onPlatform) //move with platform
So it sounds you have two problems:
Player is falling through platform
Player isn't sticking to the platform
Falling through the floor is a super common bug, with many causes. Here is a checklist I found:
does Object have a Collider? If not, select Object
go to the top bar
components
physics
choose appropriate collider
(if Terrain, check the last tab, the little cog-wheel)
Note: mesh-collider may cause problems
Particularly, if both FallingObject and GroundObject have mesh-collider
Particularly, if the mesh is animated
To avoid mesh-collider, you can build an aproximate shape of your mesh from
several primitive colliders (in parent-, child- or sibling-GameObjects)
If you need a Mesh-collider no matter what, you can try to place additional primitive colliders where they won't be in the way to 'enforce' the collisions
Is the Object a Trigger? If so, select Object
find its Collider-Component (if Terrain, check the last tab, the little cog-wheel)
remove the check of 'IsTrigger'
Is the Collider placed well?
Fiddle with center, size and skin-width (start with 0.1) until the green outline aproximately fits the character
(If you get really strange values, it might be due to scale (e.g. your mesh was way too big so you downsized to .01))
You may try to zero out all positioning (both unity and your modeling-program)
The link goes into even more cases.
Once the player can stay on, the physics engine should handle the momentum transfer to move the player with the platform using friction. Which again, requires RigidBody2D. I'm not sure why you're not using RigidBodys, it kind of feels like you're avoiding the solution.
Doing it this way should avoid the need to parent the player, and have a trigger volume as well, unless you want the player physically stuck and can't move on the platform.
So say I have two sprites. These sprites will not have any BoxCollider2Ds or RigidBody2Ds.
How would I be able to detect if a sprite is touching the other sprite using a script inside of the first sprite.
For example I have a player and a flag. I would want something to happen when the player touches the flag
Sorry if this is poorly written or not informational enough, if you need any more information please just leave a comment and I'll respond.
Do you not want the collider due to the physical repulsion due to the collision? If you are looking to just detect if two objects have entered one another, Triggers might be what you are looking for. You will need to have a collider on both objects, but can mark them isTrigger. To see exactly what you need in order for the OnTriggerEnter2D to go off, view the collision action matrix.
If you really want nothing to do with colliders, you can implement basic collision detection yourself for these two objects. For two simple boxes, you can use AABB collision detection, which is just taking the corners of two boxes and determining if the corners overlap.
if (obj1Pos.x < obj2Pos.x + obj2.width &&
obj1Pos.x + obj1.width > obj2Pos.x &&
obj1Pos.y < obj2Pos.y + obj2.height &&
obj1Pos.y + obj1.height > obj2Pos.y) {
// collision detected!
}
If you want to learn more about self implementation to detect 2D collision, this is a good read. With more complex geometry, collision gets more advanced, but I believe you just want to use triggers. If you have more questions about what triggers are, how they function, etc. I can answer them but I was not exactly sure on how you wanted to approach your problem.
Overview of Game:
The main character of the game is a gum which throws a piece of itself which is called anchor in any direction or at any angle.
There is a thread like connection, which is called connection-thread Object, between the gum and its anchors whose elastic structure is implemented using SpringJoin2D which gives the connection-thread a spring-like behavior.
In order to make the connection-thread more dynamic, a mechanism is added to the game which lets the connection-thread to get segmented by adding a new anchor in the part of the connection-thread that collides with an object in the game environment. In other words whenever an object hits the connection-thread a static anchor is added in the collision point.
The main problem:
The connection-thread Object is scaled and positioned manually in the script, which causes dysfunction in the collision detection system(Box2D) in a way that the collision points cannot be detected correctly.
Used Methods :
boxCollider / EdgeCollider -> dysfunctioned when scaling
RayCast2D / LineCast2D -> middle anchors enter the environment Object in high speed
static boxcollider2D in child object in size of maximum length of connection-thread Object -> [not implemented yet]
Any idea to fix this ?
As far as i know, with kinematic objects you can only have trigger events which only return colliders and not collision information.
I think you may try to check the collision with triggers and then do a circle sweep test in 8 direction for example to search for a collision and receive the information with the raycasthit2D. The sweep test will return you a raycasthit2D wich have a "point" member you can use.
Follow this links -
Unity Manual
http://docs.unity3d.com/ScriptReference/Physics2D.CircleCast.html
How Capsule and Sphere Cast work (same thing as circle cast but in 3D)
https://www.youtube.com/watch?v=D1xqen8uH64
Hope I could explain myself well enough. Good luck
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.
in my XNA game(im fairly new to XNA by the way) i would like to have my player sprite land on top of a platform. i have a player sprite class that inherits from my regular sprite class, and the regular sprite class for
basic non playable sprite stuff such as boxes, background stuff, and platforms. However, i am unsure how to implement a way to make my player sprite land on a platform.
My player Sprite can jump and move around, but i dont know where and how to check to see if it is on top of my platform sprite.
My player sprites jump method is here
private void Jump()
{
if (mCurrentState != State.Jumping)
{
mCurrentState = State.Jumping;
mStartingPosition = Position;
mDirection.Y = MOVE_UP;
mSpeed = new Vector2(jumpSpeed, jumpSpeed);
}
}
mStartingPosition is player sprites starting position of the jump, and Position is the player sprites current position. I would think that my code for checking to see whether my player sprite is on top of my platform sprite. I am unsure how to reference my platform sprite inside of the playersprite class and inside of the jump method.
i think it should be something like this
//platformSprite.CollisonBox would be the rectangle around the platform, but im not
//sure how to check to see if player.Position is touching any point
//on platformSprite.CollisionBox
if(player.Position == platformSprite.CollisionBox)
{
player.mDirection = 0;
}
Again im pretty new to programming and XNA, and some of this logic i dont quite understand so any help on any of it would be greatly appreciated:D
Thanks
If player.Position is a Point and CollisionBox is a Rectangle, you could use
if (platformSprite.CollisionBox.Contains (player.Position))
I was playing around with something like this just a few days ago.
What you're calling a CollisionBox I called a BoundingBox. A BoundingBox is a Rectangle which represents the area occupied by the sprite.
You'll probably find it helpful to define a BoundingBox for your sprites instead of just using their position.
You can easily test for the collision of Rectangles using the following code:
if (player.BoundingBox.Intersects(platform.BoundingBox)
{
// handle collision here...
}
For this to work, make sure that the X and Y coordinates of your BoundingBox are correctly reflecting your sprite's position.
You may want to look at the Platformer Starter Kit. It includes this as well as detecting when the player is touching an enemy, collecting gems, etc.
The answer about BoundingBox is right, but IMHO it is easier to use Rectangle if you are making a 2D game rather than BoundingBox, wich is designed for a 3D one.
Anyway both objects have intersection / collision test methods that takes Vector2 or Point as parameter.
Now you have to have a box for your player, and a box for your platform. If one collide with the other, you have to check from where (the player can hit the platform from up, down, left, right, or maybe up & left on the left up corner).
If the player is on top of the platform, then just stop his fall, probably by setting his Y-speed component to 0.
Maybe you will need the player to go through the platform when jumping (player hit from bottom) but not when falling (player hit from top).