Collision issues

I would like to avoid parenting: "This is a little unsafe, although parent colliders can cause strange results, for example, a random teleport or an object that starts to move and rotate insanely, etc." - Samed Tarık ÇETİN: comment .

+7
game-physics
source share
4 answers

Your basic idea is correct, your code may be slightly modified to support this.

Here's the trick: instead of tying your object to the object it collided with, you create a fictitious game object, call it “glue”, at the point of collision and snap your object to the glue. The glue object is then the parent of the object we encountered.

Since the glue is just a dummy object with only components converted and some script, there are no problems with the parent position.

In addition, note that it doesn’t really matter at which point of contact we create the adhesive if we have several points of contact, and this is also easy to extend to supporting rotations, see below.

Thus, in a collision, the only thing we do now is create glue. Here is the code:

void CreateGlue(Vector3 position, GameObject other) { // Here we create a glue object programatically, but you can make a prefab if you want. // Glue object is a simple transform with Glue.cs script attached. var glue = (new GameObject("glue")).AddComponent<Glue>(); // We set glue position at the contact point glue.transform.position = position; // This also enables us to support object rotation. We initially set glue rotation to the same value // as our game object rotation. If you don't want rotation - simply remove this. glue.transform.rotation = transform.rotation; // We make the object we collided with a parent of glue object glue.transform.SetParent(other.transform); // And now we call glue initialization glue.AttachObject(gameObject); } void OnCollisionEnter(Collision col) { // On collision we simply create a glue object at any contact point. CreateGlue(col.contacts[0].point, col.gameObject); } 

And this is what the Glue.cs script looks like, it will handle LateUpdate and modify the conversion.

 public class Glue : MonoBehaviour { protected Transform stuckTo = null; protected Vector3 offset = Vector3.zero; public void AttachObject(GameObject other) { // Basically - same code as yours with slight modifications // Make rigidbody Kinematic var rb = other.GetComponent<Rigidbody>(); rb.isKinematic = true; // Calculate offset - pay attention the direction of the offset is now reverse // since we attach glue to object and not object to glue. It can be modified to work // the other way, it just seems more reasonable to set all "glueing" functionality // at Glue object offset = transform.position - other.transform.position; stuckTo = other.transform; } public void LateUpdate() { if (stuckTo != null) { // If you don't want to support rotation remove this line stuckTo.rotation = transform.rotation; stuckTo.position = transform.position - transform.rotation * offset; } } // Just visualizing the glue point, remove if not needed void OnDrawGizmos() { Gizmos.color = Color.cyan; Gizmos.DrawSphere(transform.position, 0.2f); } } 

Also, note that simply parent objects, as suggested here, can lead to some additional problems, since scaling the parent also scales the children, so you have to re-scale the child to its original size. The problem is that these scaling operations relate to different anchor points, so you also have to make additional adjustments to the position of the objects. Could be done though.

I also created a small sample project, see here (Unity v5.2.f3): https://www.dropbox.com/s/whr85cmdp1tv7tv/GlueObjects.zip?dl=0

PS I see that you mix transforming and rigid semantics, since this is done on kinematic rigid devices, it doesn’t matter, but just a suggestion: think whether you really need to have rigid bodies on objects that are already “stuck” with others, if not - perhaps just remove or disconnect the rigid body instead of making it kinematic.

+1
source share

What you want to do is scale around the point of collision. You can achieve this by setting the anchor point at the collision point, so when you scale the object, it will scale based on the pivot point. The best way to simulate this is to use sprites in Unity.

Pivot point scaling

In the above image, the top left drawer has a pivot point in the center, so when you scale it along x, it scales around that point, increasing its width on both sides of the pivot point. But when the pivot point is set to the side, as in the lower left image, when you scale it, it can only stretch it to the left (unless you scale negatively, of course).

Now the problem is that you cannot easily change the grid reference point, so there are a number of different tasks for this. One of these approaches is to snap the grid to an empty new gameObject and adjust the local transformation of the grid with respect to the parent element, simulate the movement of the grid pivot point.

What the code does below defines a collision point. Since there can be several collision points, I get the midpoint of the collision of all of them. Then I move the parent of the grid to the collision point and adjust the local position of the grid so that the cube side is at that point, it acts like setting the grid's turning point at the collision point.

Now, when you scale, it will scale around the point of collision, as in the image above.

 public MeshRenderer _meshRenderer; public float _moveXDirection; public Rigidbody _rigidBody; public Transform _meshTransform; public bool _sticksToObjects; public ScalingScript _scalingScript; protected Transform _stuckTo = null; protected Vector3 _offset = Vector3.zero; void LateUpdate() { if (_stuckTo != null) { transform.position = _stuckTo.position - _offset; } } void OnCollisionEnter(Collision collision) { if (!_sticksToObjects) { return; } _rigidBody.isKinematic = true; // Get the approximate collision point and normal, as there // may be multipled collision points Vector3 contactPoint = Vector3.zero; Vector3 contactNormal = Vector3.zero; for (int i = 0; i < collision.contacts.Length; i++) { contactPoint += collision.contacts[i].point; contactNormal += collision.contacts[i].normal; } // Get the final, approximate, point and normal of collision contactPoint /= collision.contacts.Length; contactNormal /= collision.contacts.Length; // Move object to the collision point // This acts as setting the pivot point of the cube mesh to the collision point transform.position = contactPoint; // Adjust the local position of the cube so it is flush with the pivot point Vector3 meshLocalPosition = Vector3.zero; // Move the child so the side is at the collision point. // A x local position of 0 means the child is centered on the parent, // a value of 0.5 means it to the right, and a value of -0.5 means it to the left meshLocalPosition.x = (0.5f * contactNormal.x); _meshTransform.localPosition = meshLocalPosition; if (_stuckTo == null || _stuckTo != collision.gameObject.transform) { _offset = collision.gameObject.transform.position - transform.position; } _stuckTo = collision.gameObject.transform; // Enable the scaling script if (_scalingScript != null) { _scalingScript.enabled = true; } } 

Here is an example project with the above code: https://www.dropbox.com/s/i6pdlw8mjs2sxcf/CubesAttached.zip?dl=0

+1
source share

change global

 protected Collider stuckTo = null; 

///// use Collider instead of transform object. You can get a better solution. Change me if it works or gives any error, since I have not tried, if it works, I would like to know.

 void OnCollisionEnter(Collision col) { rb = GetComponent<Rigidbody>(); rb.isKinematic = true; if(stuckTo == null || stuckTo != col.gameObject.transform) offset = col.collider.bounds.center - transform.position; stuckTo = col.collider; } public void LateUpdate() { if (stuckTo != null) { Vector3 distance=stuckTo.bounds.extents + GetComponent<Collider>().bounds.extents; transform.position = stuckTo.bounds.center + distance; } } 
0
source share

Make sure you scale the stuckTo transform (the one to which the collider is attached), and not one of the parents or this will work.

if stuckTo scale is uniform:

 protected Transform stuckTo = null; protected Vector3 originalPositionOffset = Vector3.zero; protected Vector3 positionOffset = Vector3.zero; protected Vector3 originalScaleOfTheTarget = Vector3.zero; public void LateUpdate() { if (stuckTo != null){ positionOffset *= stuckTo.localScale.x; transform.position = stuckTo.position - positionOffset; } } void OnCollisionEnter(Collision col) { rb = GetComponent<Rigidbody>(); rb.isKinematic = true; if(stuckTo == null || stuckTo != col.gameObject.transform){ originalScaleOfTheTarget = col.gameObject.transform.localScale; originalPositionOffset = col.gameObject.transform.position - transform.position; originalPositionOffset /= originalScaleOfTheTarget.x; } stuckTo = col.gameObject.transform; } 

but if stuckTo scale is uneven:

 protected Transform stuckTo = null; protected Vector3 originalPositionOffset = Vector3.zero; protected Vector3 positionOffset = Vector3.zero; protected Vector3 originalScaleOfTheTarget = Vector3.zero; public void LateUpdate() { if (stuckTo != null){ positionOffset.x = originalPositionOffset.x * stuckTo.localScale.x; positionOffset.y = originalPositionOffset.y * stuckTo.localScale.y; positionOffset.z = originalPositionOffset.z * stuckTo.localScale.z; transform.position = stuckTo.position - positionOffset; } } void OnCollisionEnter(Collision col) { rb = GetComponent<Rigidbody>(); rb.isKinematic = true; if(stuckTo == null || stuckTo != col.gameObject.transform){ originalScaleOfTheTarget = col.gameObject.transform.localScale; originalPositionOffset = col.gameObject.transform.position - transform.position; originalPositionOffset.x /= originalScaleOfTheTarget.x; originalPositionOffset.y /= originalScaleOfTheTarget.y; originalPositionOffset.z /= originalScaleOfTheTarget.z; } stuckTo = col.gameObject.transform; } 

But still, though, why do you follow the advice of ÇETİN? It is absolutely safe for parent colliders and rigid bodies and literally nothing until you know what you are doing. Just parental sticky transformation under the target and bam! if something goes wrong, just remove the rigid body component or disable the collider component.

0
source share

All Articles