I'm in a situation where I need a 2d sensor that will not collide but will also give me contact points for a collision.
Triggers don't give me contact points and colliders give me contact points but cause a collision.
I've tried disabling collisions when using colliders in the hopes of getting a collision enter callback but not having the collision actually occur, but no luck.
So how do I get contact points from a trigger? Or how do I get a collision callback with rigidbodies without causing a collision?
Basically I have a circle that I want to act as radar, but I'd like it to be fairly accurate with contact points too.
Posting due to this being top Google result.
I think there's a simpler (depends on how you look at it of course) and more precise way to do this than raycasting.
private void OnTriggerEnter(Collider collider)
{
var collisionPoint = collider.ClosestPoint(transform.position);
}
collider is a collider that entered trigger in question, transform is a triger's transform. As the function name suggests this fill find a point on collider closest to said trigger.
Of course this is not super precise especially for weirdly shaped colliders, but most likely it will be good enough for most of the cases, definitely good enough for a projectile, or say, blade, hitting a rigidbody character.
As a bonus, here's a simple vector math to find collision normal:
var collisionNormal = transform.position - collisionPoint;
Again, not super precise and will not be an actual proper normal (i.e perpendicular) to the impact point, but as with a previous one - most likely will be good enough.
Have fun!
You can get point of contact using OnTriggerEnter function
OnTriggerEnter(Collider other)
{
RaycastHit hit;
if (Physics.Raycast(transform.position, transform.forward, out hit))
{
Debug.Log("Point of contact: "+hit.point);
}
}
You should use OnCollisionEnter but add a rigidbody to it and set isTrigger to false and set rigidbody isKinematic to true then it will act like a trigger and you can get the contact points like this
void OnCollisionEnter(Collision collision) {
foreach (ContactPoint contact in collision.contacts) {
Debug.DrawRay(contact.point, contact.normal, Color.white);
}
Related
My problem looks like this:
I have one stationary object wall and second object that is moving projectile. I want projectile to stop when it collides with wall. But ewery my attempt ended with projectile passing through wall.
Only script that affects movement of projectile:
public Transform trans;
void Update()
{
trans.Translate(0, 0, speed * Time.deltaTime, Space.Self);
}
All compomponents of projectile:
All components of wall:
Note: I have correctly set up layers in project settings.
I have tried many combinations of rigit boddies, colliders, layers and so on, but projectile always passed through wall.
In general, your presented setup is right. The contacting object have a Rigidbody component, and the objects you can collide have at least a collider (not a trigger).
First possible problem: your Rigidbody is set to Kinematic. Did you enable all collision modes in Project Settings > Physics > Contact Pairs Mode?
Second problem is the use of transform.Translate in the Update. Translate will ignore physic and just move the object when you tell it to do, and Update may be out of sync with the physic loop, where it would be recommended to use FixedUpdate. Even using FixedUpdate, Translate will not do the right job. It will force the object to move and the physic can complain or not about that (it can move the object back, glitching, or it can just ignore and let the object pass).
The right way to move a Kinematic rigidbody is through rigidbody.MovePosition, as stated in the Unity docs. For non-kinematic objects, you can use AddForce. Some tutorials also teach to set velocity of Rigidbody, which is not recommended because it can have undesired effects. Do it if you know the side effects and really want them.
I had a similar issue before. May I suggest destroying the projectile instead? You will then have to use OnCollisionEnter().
First, create a script and attach it to your projectile game object. After that, give your wall a tag, like 'wall'. Then, do something like this:
void OnCollisionEnter(Collision collision)
{
// Gets information about the game object the projectile collided with
GameObject objectCollided = collision.gameObject;
if (objectCollided.tag == "wall'){
// Do something with either the wall/enemy
}
// Destroy the projectile
Destroy(gameObject);
}
Note: If the intent was not to destroy the projectile but rather sticking to the wall, I suggest you to use detect collision using Raycast. Raycast has a hitInfo parameter and stores information about a gameObject that it hit, as well as the where did the Ray hit the gameObject.
Thus you can use this to move your projectile accordingly. In my previous projects, I used the Rigidbody component of my projectile and used .AddForce() to shoot it out, then detect collision with OnCollisionEnter().
Maybe you could do the same, but have a separate function to remove that force on entering a collision. I never tried it so I do not know if it will work.
Transform.Translate overrides the physics engine of unity. If you want to take advantage of the rigidbody and collider components of your gameObjects, you need to use rigidbody.Velocity or (as unity recommends in the docs) rigidbody.AddForce, and make sure you set IsKinematic to False on both rigidbodies, so that they can interact with one another physically.
Thank you in advance for any input.
What is essentially happening is this: When the player shoots, the scene resets
What I interpret as happening:
Player shoots a laser beam
Laserbeam prefab collides immediately with player (so far as I can tell)
SceneManager resets scene
I suspect two possible causes:
Either the origin of my laserbeam is causing the created projectilePrefab to trigger the player's collision in this piece of code:
private void OnCollisionEnter2D(Collision2D collision)
{
SceneManager.LoadScene(0);
//if collider is projectile.Prefab, don't run this piece of code
}
More generally that my laserbeam collision doesn't exclude the player gameobject:
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
var projectile = Instantiate(
projectilePrefab,
transform.position,
projectilePrefab.transform.rotation);
}
}
The typical solutions then would likely look like this:
Ensure the laserbeam spawns away from the player, so they do not collide.
or, ideally,
The collider on my player would be able to simply exclude contact with a laserbeam as cause for SceneManager to reset the scene.
Where I get stuck when it comes to testing these solutions is the exact syntax. I was hoping someone might be willing to give some insight that is adept at conditional statements and the like. I'm still super new, so while I can largely comprehend what I'm reading and being shown, I'm still stuck before that hump of really being able to dissect it all myself.
Essentially what my ideal might look like (so far as I can tell) is: "If collider is projectilePrefab, do not run SceneManager.LoadScene"
Cheers and thank you in advance!
As the title suggests, I've literally just started learning Unity recently and I'm practising by making a side scrolling shooter. I've been following a Udemy course about it and (to my knowledge) I've been following the tutor's instructions to the letter, but at the point where he tests his and it works fine, by projectiles go straight through my enemies.
I'm a bit stumped at this point and so I thought I'd post here to see what you guys thought. I bet it's something really simple I've not done.
Please see my projectile code below:
using UnityEngine; using System.Collections;
public class DestroyEnemyAndProjectile : MonoBehaviour {
public GameObject WhiteExplosion;
public GameObject OrangeExplosion;
void Start()
{
}
void Update()
{
}
void OnCollisionEnter2D (Collision2D tempCollision)
{
if (tempCollision.gameObject.tag == "Collision")
{
spawnParticles(tempCollision.transform.position);
Destroy(tempCollision.gameObject);
}
}
void spawnParticles(Vector2 tempPosition)
{
Instantiate(WhiteExplosion, tempPosition, Quaternion.identity);
Instantiate(OrangeExplosion, tempPosition, Quaternion.identity);
}
}
Thanks for your help!
I did post a question to them which got a response, they suggested that perhaps the projectile is going too fast and check there was a Rigidbody 2D attached - both of which were already collect
make sure that the object you're colliding with has the tag "Collision", with the same capitalisation.
If it's not, you can do this by:
1. Selecting the GameObject to be collided with
2. In the top-right, select the Tag property
3. Add tag, click the plus and type in "Collision"
4. Select the GameObject again, and select the "Collision" tag from the Tag property dropdown
Otherwise, if that's not the issue. Make sure the projectile has a type of Collider2D component, and that the projectile, or the object being collided with, has a Rigidbody2D.
First I would like to know what the behaviour of this script curently is. Is the collision method being called, and the projectile goes through the enemy anyway? Or the collision method is not being called at all?
That being said, these are things that you should check in order for collision to work:
Make sure that the projectile and the enemy have Collider2D components attached to them.
Make sure that the projectile and/or the enemy (at least one of them must) have a Rigidbody2D attached to it.
Make sure that the layer of the projectiles and the layer of the enemies are set to collide in the collision matrix (You can find the collision matrix in Edit->ProjectSettings->Physics)
Make sure that the enemy GameObject has its tag set to "Collision".
P.S: Welcome to Unity where issues like this are, in fact, usually caused by something super simple that you probably missed.
I have an issue that I've not been able to solve for a few days now and without it working I can't move on with my project, so your help would be greatly appreciated!
What I'm trying to do is 'find' the velocity of an object that is directly below my 2D sprite (that also contains a Rigidbody and 2D box collider) and then add that velocity (in the same direction) to the object that is 'looking' for it.
I feel like ray-casting might be part of the answer but I'm not sure how to use that, especially in this context.
The idea behind this is I have a platform that can carry objects stacked on top of each other, so you move the mouse, it manipulates the platforms velocity, but this causes the objects on-top to fly backwards, no mater how high the friction is.
Here is the platform code:
void Update()
{
float distance_to_screen = Camera.main.WorldToScreenPoint(gameObject.transform.position).z;
Vector3 pos_move = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance_to_screen));
mouseDelta = pos_move - lastMousePosition;
acceleration = mouseDelta.x * 0.4f;
platformVelocity += acceleration;
platform.velocity = new Vector2(platformVelocity, 0) * speed;
lastMousePosition = pos_move;
}
Thank you for taking the time to read this, I would very much appreciate any help you can give!
Is it necessary to 'find' that object? 'Finding' may be difficult & costing.
Can you hold the 'below-thing' reference and pass it to 'above-thing'?
I'm assuming you have some method on objects that checks whether it's on a moving platform. One option is to add an IsOnMovingPlatform flag on those objects. Then use a similar method to check if any object is on top of the "IsOnMovingPlatform == true" objects. Keep doing passes on that method until you don't come up with any more flags set to true.
So, i'm trying to understand your question and I think I might know what you're talking about.
Are you saying that you want your game object (2d sprite) to stay on the platform as its moving? I have had the same problem, where a platform is moving and my player (game object) slides off- and this is how I fixed it.
Add a box collider to the platform (trigger). When the player enters the platform, make the player's transform the platform. That way, when the platform moves- the player moves with it no matter what the speed is.
For example:
void OnTriggerEnter2D(Collider2d collider){
if(collider.tag.equals("Player")){ // you only want the player to work
// attach script to platform for "this.transform" to work
collider.transform.SetParent(this.transform);
}
}
then, to unparent the transform "otherwise, even when the player is off the platform, it will move with the platform.
void OnTriggerExit(Collider2d collider){
// assuming your player isn't supposed to have a parent!
collider.transform.parent = null;
}
Sorry if I misunderstood your question! If this is what you were going for, finding the velocity of the platform is not necessary. Also, this is uncompiled code- if you have issues let me know.
EDIT:
To add a bit of a give, for the entire duration that the player is in contact with the platform, we can add a little bit of a negative force to shove the player a little bit!
We do this by using OnTriggerStay2D (assuming your game is 2d, otherwise use OnTriggerStay).
For example:
void OnTriggerStay2D(Collider2d collider){
if(collider.tag.equals("Player")){
collider.getComponent<Rigidbody2D>().addForce(new vector2(forceToAdd, 0));
}
}
now, that "forceToAdd" variable should be based on the speed and direction that your platform is going. Does your platform have a rigid body on it? if so you can get your force variable by retrieving the rigid body from the platform (getcomponent()) and get the X velocity from it. With this velocity, experiment with dividing it by numbers until you find a good fit, multiply it by -1 (because you want to push the player in the opposite direction of the platform travel), then this will be your forceToAdd!
However, if your platform is not using a Rigidbody, then a way you can do this is record the x positions in your script (just two positions, and subtract these to find the distance in an update method), multiply by -1, try dividing by a number (experiment with this) and that can be your force to add variable.
Also, my code for the OnTriggerStay2d is rough, the spelling could be a bit wrong- but this will give you a rough idea for a solution! ALSO.. I don't think getting the component of a rigid body should be done in every frame, a way around this would to have your rigid body component as a global variable in the script, then set your rigid body on OnTriggerEnter2d, this way it will only retrieve it once (instead of every frame) which I assume would be more efficient!
Good luck! and if you have any questions il try to help!
This is a raycast2d version, I tested it and it works much better than the trigger collider version :).
using UnityEngine;
using System.Collections;
public class RaycastFeetDeleteLater : MonoBehaviour {
[SerializeField] private float raycastDist = 0.1f;
[SerializeField] Transform feet;
[SerializeField] private float xForceDampener = 0.1f;
private Rigidbody2D rb2d;
// Use this for initialization
void Start () {
rb2d = GetComponent<Rigidbody2D> ();
}
// Update is called once per frame
void FixedUpdate () {
RaycastDown ();
}
private void RaycastDown(){
RaycastHit2D hit = Physics2D.Raycast (feet.position, Vector2.down, raycastDist);
if (hit.collider != null) {
print (hit.transform.tag);
if (hit.collider.tag.Equals ("Platform")) {
transform.SetParent (hit.transform);
Vector2 force = new Vector2 ((hit.transform.gameObject.GetComponent<Rigidbody2D> ().velocity.x * xForceDampener) * -1, 0);
print (force);
rb2d.AddForce (force);
} else if (transform.parent != null) {
print ("UNPARENT");
transform.parent = null;
}
} else {
if (transform.parent != null) {
transform.parent = null;
}
}
Debug.DrawRay (feet.position, Vector3.down, Color.green, raycastDist);
}
}
This script is attached to the player! No need to have scripts on the platforms, which was the main reason my previous method was a pain :P
Basically this script does the following:
- Gets rigidbody2d of the player on start (we will be adding forces to the player with it soon, so might as well store it in a variable!
On FixedUpdate (Different than Update, FixedUpdate is called on Physics update, where as Update is called every frame) I have read that raycast should use FixedUpdate, I suppose because it is a Physics method!
RaycastDown shoots a 2D raycast directly down from the feet position (to get the feet, create a child object of the player, put the position of the feet a tiny bit below the player, NOT ON THE PLAYER- I will explain why in a second why that will mess up everything! Then drag the feet GameObject on to the "feet" slot of the script.)
Raycast shoots from feet directly down at a distance that is changeable in the editor (raycastDist) I found a distance of 0.1f was perfect for my needs, but play around with it for yours! You can actually see a drawing of your Ray by using Debug.DrawRay as seen in the code, the ray is only visible in the editor- not game view! But this will help, especially with positioning the feet.
Checks if it hit an object with a collider, if so check if the objects tag is a Platform (set the platforms to a new tag and call it Platform).
If it hits a platform set the parent of the player to the platform (like the last script) and start applying a little force to the player in the negative direction that the platform is traveling (we get the rigid body of the platform to find its velocity).
If the raycast does not hit a platform, unparent it so the player can move freely.
Thats basically it, however...
Please note:
This script needs to be on the player, not any of the players child objects, since this script will unparent the player from any platforms- if you have the script on a child object of the player it will unparent from the player- which is horrible. So The script MUST be on the root game object of the player. There are ways around this if for some reason you can't.
As mentioned before, the "feet" child object must be a little bit under the player- this is because when the Raycast2d "fires" it will find the FIRST object with a collider it hits- so if you have the start of the raycast hit a collider on the player then it will always show the raycast hitting the player, not a platform. So to get around this have the raycast fire from the feet- and have the feet position a tiny bit below the player. To help with this I added a print statement that will print to the console which object the raycast is hitting, if it doesn't say "Platform" then you need to lower the feet! :)
Furthermore, I'm not sure how you are moving your platforms- but if the Velocity of the platform always prints "0,0" then its because you are moving the platforms position directly, instead of moving it by forces (this happened to me when I was testing this script). I got around this by moving the platform by "AddForce". Also depending on the "Mass" of your platforms, you may need to drastically increase the "xForceDampener" which I should of actually labeled "xForceMultiplier" as I found while testing I needed to set it much higher- like at 60, instead of 0.25 which I previously thought would be okay..
Hope this helped :D
EDIT: Linking the unity docs for 2D Raycasts in case you need
http://docs.unity3d.com/ScriptReference/Physics2D.Raycast.html
I am programming a top-down hack-and-slash game for a class. We want a mechanic where if you get hit by an enemy, or an enemy hits you, you get 'knocked back'. Unfortunately, no matter what I have tried, I cannot get either the enemy or the player to react to the force.
Here is a list of things I have checked that have been suggested in other questions like this:
Player/Enemy is NOT Kinematic
Tried with both Gravity on and off
No positions/rotations are frozen
Player/Enemy have Rigidbodies attached
Player/Enemy have colliders attached, one with and one without the 'isTrigger' function checked.
Tried both OnCollisionEnter and OnTriggerEnter
The force value is high, the mass, drag, and angular drag are low
I have run out of ideas. Any and all support you can give is greatly appreciated.
Here is a snippet of the code from a script from the player Object:
public void OnTriggerEnter(Collider col)
{
if (col.gameObject.tag == "EnemyHit" && !invincible)
{
Debug.Log("The player has been hit!");
//sets player as invincible
invincible = true;
// Set the damaged flag so the screen will flash.
hit = true;
timeOfHit = Time.time;
// Reduce the current health by the damage amount.
currentHealth -= 1;
GetComponent<Rigidbody>().AddForce(transform.forward * recoilThrust, ForceMode.Force);
Debug.Log("This is the force that is being added to the player when it is hit. : " + -transform.forward * recoilThrust);
//...
}
}
I can prove (using the Debug.Log) function, that the code reaches there, and the force is calculated.
To summarise the comments:
Rigidbody.AddForce doesn't work when the object has a CharacterController besides the rigidbody. In this case the effect has to be "faked". Possible ways can be found here:
The last couple of posts
Another solution, C# and Javascript
Basically you need to apply the force using CharacterController.Move.
Is Kinematic enabled? Doing so will make the object ignore physic forces.