I'm trying to find other methods of registering collisions (other than OnCollisionEnter() and OnCollisionExit()). I'm currently using Physics.OverlapBox(), but I need more information about the collision; i.e., normal, point.
I can use Physics.BoxCast(), but the problem is that it moves a box a given distance, and using maxDistance = 0f won't work.
I need a method of checking for collisions similar to Physics.OverlapBox() except in that it would also return information about all collisions in the cast.
Any help is appreciated. Thanks.
Your concern, expressed in the comment to the first answer is valid, but the bad news is that there is no simple trick to go around it. What you should be looking for is called continuous collision detection with a simplified version described in my answer on a somewhat similar matter:
Basically, for each moving object in your scene you have to calculate
moment of next collision within the fraction of the frame 0<t0<1,
then advance positions to this moment t0 within the frame, update
velocities due to collision and proceed further to the next collision
t0<t1<1, until you reach time of tn=1 (end of frame), making sure
you don't get stuck in a the middle of the frame due to rounding of
calculation or "cornered" objects. For spherical colliders, that is
usually done by using capsule vs capsule (for pairs of objects)
intersection and capsule vs box for the boundaries.
In oppose to the simple engine from the answer I'm referring to, Unity has continuous collision detection.
So you can enable continuous collisions and continuous dynamic which computationally is very expensive.
You can also try using RigidBody.SweepTest which returns the closest collision information. Notice that even though there is also RigidBody.SweepTestAll, it doesn't help much. Not only because it returns only first 128 collisions, but also because it doesn't process them as there is no reflection. For physically correct behaviour you have to do what described above - advance time till the first collision and update velocities. Either with the physics engine or by yourself. This is very costly and not many games are doing that even cheating by using simplified objects (spheres are the cheapest ones as two swept spheres are two capsules and their intersection is a relatively cheap calculation), but amount of steps, especially in the "cornered" case when objects have nowhere to go and therefore are constantly colliding could be very large and such cases happen more than one can expect.
For complex objects you unlikely can do better than SweepTest, unless you trigger it based on the simpler primitives, such as Physics.BoxCast or Physics.SphereCast. Again, even though there are Physics.BoxCastAll and Physics.SphereCastAll they are not particularly useful as only first collision is guaranteed to occur. Those xxxCastAll are the functions you wrote you were looking for, so give it a try, they might work well enough for your use case.
You can use OverlapBox and use Collider's ClosestPoint to select a single point of overlap, and use that to make your collision calculations.
Collider[] cols = Physics.OverlapBox(...);
Vector3 myPosition = transform.position; // for example
foreach (Collider col in cols) {
Vector3 closestPoint = col.ClosestPoint(myPosition);
Vector3 positionDifference = (closestPoint-myPosition);
Vector3 overlapDirection = positionDifference.normalized;
}
This overlapDirection will point in the direction away from the the position you use in ClosestPoint to the center of each colliding collider. If you want something based on the surface of your object, what you can do is use that overlap direction to place a raycast aimed at your object, to find the normal that way:
// ...
foreach (Collider col in cols) {
Vector3 closestPoint = col.ClosestPoint(myPosition);
Vector3 positionDifference = (closestPoint-myPosition);
Vector3 overlapDirection = positionDifference.normalized;
RaycastHit hit;
int layerMask = 1; // Set to something that will only hit your object
float raycastDistance = 10.0; // something greater than your object's largest radius,
// so that the ray doesn't start inside of your object
Vector3 rayStart = myPosition + overlapDirection * raycastDistance;
Vector3 rayDirection = -overlapDirection ;
if (Physics.Raycast(rayStart, rayDirection, out hit, Mathf.Infinity, layerMask)) {
Debug.Log(hit.normal);
Debug.Log(hit.position);
} else {
// The ray missed your object, somehow.
// Most likely it started inside your object
// or there is a mistake in the layerMask
}
}
Related
I am making a 2D game and I am using Raycast to detect if the player is on the ground. No matter what Raycast always returns false. I have used Debug.DrawRay which shows that the ray is hitting the ground. I have tried solutions from several different tutorials but nothing worked. I am pretty new to Unity so hopefully someone who is more experienced knows what's wrong.
if (Physics.Raycast(transform.position, transform.TransformDirection(-Vector3.down), DistanceDown, LayerMask)){
Debug.Log("Grounded");
return true;
}
else
{
Debug.Log("Not Grounded");
return false;
}
Revised Code:
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.down), (float)(GetComponent<Collider2D>().bounds.extents.y + Offset)))
A Raycast has a few parameters. I will explain each as there could be a few reason as to why it is not working, so possibly breaking this up can help you realize what's wrong.
Raycast(Vector3 origin, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal);
The origin is the point in space that the ray will start from. Currently, you have it as transform.position, so it will start at the position of whatever the script is on, most likely your player.
The direction is a vector that should be normalized so there is no scale to determine where from the origin the ray should be cast. Right now you are using -Vector3.down, which would result in Vector3.Up as up and down are opposites, so negating one gives the other. For a ground check, I would be using Vector3.down as the ground is generally down or below the origin of your player.
The maxDistance is the length that the ray is cast. Right now you have it as DistanceDown which I am assuming is some float. Generally, for a ground check, I would take the collider of your character, then add some offset to determine when grounded. Something along the lines of GetComponent<Collider>().bounds.extents.y + OFFSET. The bounds.extents.y is half the size of the collider and since your origin is the center of your player, this should land at the feet or base of your collider, which is why you need the offset.
The layermask is used to ignore particular layers when casting. It is rather useful when you only want to cast and hit a single layer, where in your case could just be the ground. I do not see code for this, but I will provide an example in case this is where you are going wrong. LayerMask mask = LayerMask.GetMask("GroundLayer"); and use mask as your mask. Now the ray will only hit objects on the layer GroundLayer.
The last parameter which you are not using is called queryTriggerInteraction. Raycast by default do not hit triggers, but with this parameter, you can override it and tell it to hit triggers.
Read over each of these sections and check to make sure what you are doing matches what I am describing. If you are still unable to figure out your issues based on what I have written, I would need to know a bit more about your situation to help out.
I'm recreating the movement system from the DS game Dragon Quest Heroes Rocket Slime in Unity. Currently I've got pretty desirable results but it's not 100%.
One of the things that are different is that my player bounces back from walls further on long distance slings than he does in the original game. (left is my recreation, right is original game). Obviously, I can't find out how the game was made and the exact maths/values they use for slinging but one of the things I got from the game is that:
The player moves at constant velocity while slinging in a direction (no slow down, they just stop at a certain point)
Which led me to believe the game doesn't use traditional velocity mechanics so I made it so:
While stretching, the players 'pVelocity' (potentialVelocity) goes up. When they let go a point create at curPlayerPosition + pVelocity is created. I then move the player at a constant velocity to that point. My idea to fix my issue of bouncing too far is by getting the distance from the position where I slung from to the end point and making the player bounce less the larger the distance and further the smaller the distance.
Problem is, I'm not really sure how to do that last part.
Here's my code:
Vector2 reflectedVelocity = Vector2.Reflect(pVelNormalized, foundHit.normal);
playerAnimator.SetFloat("VelocityX", reflectedVelocity.x);
playerAnimator.SetFloat("VelocityY", reflectedVelocity.y);
float DistanceFromOrigin = Vector3.Distance(slingOrigin, transform.position);
print(DistanceFromOrigin);
//ToDo: reflectedVelocity is lower the higher DistanceFromOrigin is.
The other solution I thought of is that maybe my idea of how the 'velocity' works in the original game is completely wrong and if so, please tell me how it actually is/looks to be actually done
reflectedVelocity is lower the higher DistanceFromOrigin is.
So reflectedVelocity should have an inverse relationship with DistanceFromOrigin
Vector2 reflectedVelocity = Vector2.Reflect(pVelNormalized, foundHit.normal);
float DistanceFromOrigin = Vector3.Distance(slingOrigin, transform.position);
if(DistanceFromOrigin == 0) DistanceFromOrigin = 0.00001f;//to avoid division by zero
reflectedVelocity *= (SomeConstant / DistanceFromOrigin);
playerAnimator.SetFloat("VelocityX", reflectedVelocity.x);
playerAnimator.SetFloat("VelocityY", reflectedVelocity.y);
You may use a linear relationship instead of non-linear:
reflectedVelocity *= (SomeConstant - DistanceFromOrigin);
I have the object "ball" that can roll on the surface. There is also the object "platform". So, I need to platform up a certain distance, only when the ball will stop on the platform.
Script on C#.
Honsetly - I don't know about Unity API anything :), but I know that it must have available property "position". So one of the ways to solve you problem - check position of ball in every Update(in comparison with previous position), and when it's position become stable(the same) for a while(some time) - move the platform.
something like:
private Vector3 lastPosition;
private stableTime = 0;
public float distThreshold = 0.1;
public float timeThreshold = 0.1;
void FixedUpdate() {
if (lastPoistion == null || Vector3.Distance(ball.position, lastPoistion) > distThreshold) {
lastPosition = ball.position;
stableTime = 0;
}
else
stableTime += Time.deltaTime;
if (stableTime > timeThreshold)
platform.MovePoistion()
}
You need to identify 2 things here.
Is the ball stopped?
is the ball on the platform
To identify if the ball is stopped simply keep track of it's current speed (using a speed property assigned to the ball object). I'm assuming you probably already have this as the player will be controlling the ball? If so, just check that speed == 0.
To check whether the ball is on the platform you will need to use colliders. Take a look under 2d or 3d physics (you haven't specified which you are using) and apply some appropriate colliders to both the platform and the ball. You can now programmatically check for collision between the 2 objects. There are plenty of tutorials on object collision so I won't go into it here but this may be a good place to start.
I have a ball game object that I am making bounce using explicit physics equations. I tried using unity's physics engine, but It was not exact enough. I am setting the balls position like so:
float dTime = Time.time - timeSinceBounce;
ballPosition.x -= meterOffset / sensitivity;
ballPosition.y = ballInitialPosition.y + GameConstants.Instance.GetBallYVel ()
* dTime - (float)0.5 * GameConstants.Instance.GetGravity () * dTime * dTime; //Yi+Vy*t-0.5(G)(time^2)
ballPosition.z = (-dTime * GameConstants.Instance.GetBallZVel ()
+ ballInitialPosition.z) * startConstant; //Zi + t*Vz
ball.transform.position = ballPosition;
This code runs in the Update() method. When a collision is detected the time and initial position as reset, and this works. Currently the ball bounces properly but shakes when it is moving quickly. The FPS is at 80. I don't think my equations are wrong because it bounces properly there is just a shake. What is the best way to remove the shake? Thank!
Edit 1: I decided to write my own physics because Unity's physics engine had a lot of other components like friction and roll that were making the ball's bounces vary. Also the camera's x and y is fixed the only thing that is changing is the camera's z position. It works a lot better in fixed update
As of asker's request I hereby add all the details I can, even though without seeing the actual, full and complete code along with seeing this "shake" effect it's challenging to give an exact and proper answer. I'll try and do my best though.So let's do this.
1. "Shaking" reason: most probably you set up some kind of animation, along with a forced positioning/rotation/etc and these two codes work against each other. Double-check your AnimatorController and Animator as well as your Mecanim if you have set/configure these (also a good read: "Locomotion").You can also keep the Animator Window open while you are testing from Unity UI, to see who is the tricky guy doing the "shaking" effect for you, and get one step closer to why it happens.If these don't help, start from scratch: Minimize the code you wrote to move the ball, remove every effect, etc and add them back one by one. Sooner or later "shakieness" will come back (if it persist with the cleanest possible code, see above, Animator/Locomotion issue)
2. Think again not rewriting Unity Physics... You mention for example "...physics engine had a lot of other components like friction and roll that were making the ball's bounces vary...".Now, this is something you can turn off. Completely if you want. Easiest way is to set everything to zero on Physics Material (i.e. friction and bouncieness) but if you want Unity to ignore all and every "actual physics" and let you do what you want with your GameObject, tick in the isKinematic property of the Rigidbody. The fun part of it, you'll still collide if and where you want, can AddForce, etc yet neither gravity nor friction or "anything physical" affect you.If you are not comfy with Unity (and you are not it seems), I highly recommend learning about it a bit first as it is a very good and very powerful engine - so don't throw it's features away as they don't work as you initially expected they'll do. For example, Sebastian Lague just started a great beginner series a few weeks ago. The guy is a teacher, his videos are both short, fun and full of information so I can just recommend his channel.
3. As of your code I would make these changes:
public static class GameConstants { //no monobehaviour == can be a "proper, global" singleton!
public static float GetGravity {
get { return [whatever you want]; }
//why not Physics.gravity, using locally when and where it's needed?
}
//etc, add your methods, properties and such
//use this approach if and only IF you access these values application wide!
//otherwise, instead of a """singleton""" logic you just implemented,
//consider adding an empty GameObject to the Scene, name it "GM" and
//attach a "GameManager" script to it, having all the methods and
//properties you'll need Scene-wide! You can also consider
//using PlayerPrefs (see help link below) instead of a global static
//class, so you save memory (but will need to "reach out" for the data)
}
private void FixedUpdate() { //FIXED update as you'll work with physics!
//float dTime = Time.time - timeSinceBounce; //not needed
//positioning like this is not recommended, use AddForce or set direction and velocity instead
ballPosition.x -= meterOffset / sensitivity;
ballPosition.y = ballInitialPosition.y + GameConstants.Instance.GetBallYVel ()
* Time.fixedDeltaTime - 0.5f * GameConstants.Instance.GetGravity () * Time.fixedDeltaTime * Time.fixedDeltaTime; //Yi+Vy*t-0.5(G)(time^2)
ballPosition.z = (-dTime * GameConstants.Instance.GetBallZVel ()
+ ballInitialPosition.z) * startConstant; //Zi + t*Vz
ball.transform.position = ballPosition;
}BUT to be honest,
I would just throw that code away, implement the Bouncing Ball example Unity Tutorials offer, alter it (see point #2) and work my way up from there.
Items not linked but I mentioned in code:
PlayerPrefs, Physics.gravity and Rigidbody.velocity.
Note that 'velocity' is ignoring parts of physics (speeding up, being "dragged", etc) i.e. objects suddenly start moving with the speed you set (This is why Unity5 recommends AddForce instead -linked above-)
It might also come handy you can alter the FixedUpdate() frequency via Time Management, but I'm not sure this is yet needed (i.e. just for bouncing a ball)
I hope this helps and that ball is going to bounce and jump and dance and everything you want it to do ;)
Cheers!
My question is about if there is a way to know the coordinates of the corners of a gameobject. What I have is three Vuforia AR targets and a gameobject, a cube in this case.
What I need to achieve, is so that when I move the targets around, the corners of the cube would follow the targets, eg. the cube would be as wide as the space between the targets.
Right now how it does it, is that it checks the distance between the targets and sets the scale of the cube according to it. This results in the cube being expanded always from its set position, which makes the positioning of the targets awkward in real life.
Here's a couple of pictures showing what it does now, taken during execution:
Here is the code attached to the cube-object:
using UnityEngine;
using System.Collections;
public class show : MonoBehaviour {
float distancex;
float distancez;
// Use this for initialization
void Start () {
renderer.enabled = false;
}
// Update is called once per frame
void Update () {
if (GameObject.Find ("target1").renderer.enabled && GameObject.Find ("target2").renderer.enabled &&
GameObject.Find ("target3").renderer.enabled && GameObject.Find ("target4").renderer.enabled)
{
renderer.enabled = true;
}
distancex = Vector3.Distance ((GameObject.Find ("target1").transform.position), (GameObject.Find ("target2").transform.position));
distancez = Vector3.Distance ((GameObject.Find ("target2").transform.position), (GameObject.Find ("target3").transform.position));
Debug.Log (distancex);
Debug.Log (distancez);
transform.localScale = new Vector3((distancex), transform.localScale.y, transform.localScale.z);
transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y, (distancez));
Debug.Log (transform.localScale);
}
}
How do I make the corners of the object follow the targets? I need the width of the side to be the width of the distance between the targets, is there any way to achieve this without using the scale?
I know this is quite some time after you asked the question, but I came across this as I was looking to sort something similar out myself.
What I found I needed (and others may be helped) is to use Renderer.bounds
What it looks like in practice for me:
void Update () {
rend = currentGameObject.GetComponent<Renderer>();
Debug.Log(rend.bounds.max);
Debug.Log(rend.bounds.min);
}
My object in this case was a quad at position 0,0,200 and a scale of 200,200,1. The output of rend.bounds.max is (100.0,100.0,200.0) the min was (-100.0,-100.0,200.0). This gives you the corner position for each corner (granted my example was in 2D space, but this should work for 3d as well).
To get it a little more specific for your example if you wanted the top right corner, you could get the XY of renderer.bounds.max, for the top left you would get the renderer.bounds.max.y and the renderer.bounds.min.x. Hope that helps!
Cheers!
Create 8 empty game objects.
Make them children of the "cube" object to be tracked.
Move them in Editor to be at the 8 corners of your tracked game object. Since they are children of the tracked object, their positions will be {+/- 0.5, +/- 0.5, +/- 0.5}.
Name them memorably (i.e., "top-left-back corner").
These 8 objects will move and scale with the tracked object, staying at its corners. Any time you want to know where a corner is, simply reference one or more of the 8 game objects by name and get the Vector3 for each of their transform.position.
Note: If you're doing step 5 repeatedly, you may want to avoid potentially costly GameObject.Find() calls (or similar) each time. Do them once and save the 8 game objects in a variable.
I worked my way around the problem by determining the position between the targets and counting their mean.
This is not flawless, however, since it is difficult to determine the scale factor with which the models would accurately follow the targets' corners, when bringing external models to use with the system.