I have a very simple case: 2 cubes on a plane, the player can move a cube via mouse input.
Each cube has a collider and rigidbody attached, as well as a high friction material with 0 bounce.
What I want to achieve is: if one cube is to come into contact with the other, I want to stop both cubes from pushing each other or going into one another - I want them to act as walls to each other.
What I'm getting is this:
What I have tried is to switch on the IsKinematic option of the cubes while I drag them, but this lead to no result.
Here is my code:
private Vector3 screenPoint;
private Vector3 offset;
private Vector3 oldPosition;
private Rigidbody rBody;
private bool dragging = false;
void Awake()
{
rBody = gameObject.GetComponent<Rigidbody>();
}
void OnMouseUp()
{
dragging = false;
rBody.isKinematic = false;
}
void FixedUpdate()
{
if(dragging)
{
Vector3 cursorPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
Vector3 cursorPosition = Camera.main.ScreenToWorldPoint(cursorPoint) + offset;
cursorPosition.y = oldPosition.y;
rBody.MovePosition(cursorPosition);
}
}
void OnMouseDown()
{
screenPoint = Camera.main.WorldToScreenPoint(rBody.position);
offset = rBody.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z));
oldPosition = rBody.position;
}
void OnMouseDrag()
{
dragging = true;
rBody.isKinematic = true;
}
Any help is appreciated. Thanks.
Did you try to put a physic material and tweek the settings (especially the friction settings)?
If you did, and didn't achieve what you want, you could use MonoBehaviour.OnCollisionExit to detect when your objects aren't in contact anymore and either set the velocity to 0 or lock the position. If you lock the position, you'll probably need a coroutine to unlock it a while later.
This solution may lead to strange bugs later (in case of bumpy move for exemple, where your objects lose contact for one frame only). I would prefer using the physic material if possible.
Might not be the best solution but maybe try destroying the rigid body component after the collision enter, this way the cube will become a static object, thus turning into a wall. You can add the rigidbody again shortly afterwards.
Related
I attach a movement script to the player. with reference to the roller ball tutorial and modified it for jump. the problem is that when may player moves in any direction the player starts to rotate in that direction and even if i am standing still at the one position it starts to rotate and fall down the platform. the player has a rigidbody, boxcollider components.
void Awake ()
{
playerRigidbody = GetComponent<Rigidbody>();
Coll = GetComponent<CapsuleCollider>();
}
/*private void Update()
{
}*/
// Update is called once per frame
void FixedUpdate ()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
bool down = Input.GetKeyDown(KeyCode.Space);
if (down)
{
playerRigidbody.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
Move(h, v);
}
private bool IsGrounded()
{
return Physics.CheckCapsule(Coll.bounds.center, new Vector3(Coll.bounds.center.x,Coll.bounds.min.y,Coll.bounds.center.z),Coll.height * 9f,Ground);
}
void Move (float h, float v)
{
movement.Set(h, 0f, v);
movement = movement.normalized * speed * Time.deltaTime;
playerRigidbody.MovePosition(transform.position + movement);
}
In your code,you use AddForce function,so I confirm your player rigidbody doesn't set Kinematic to true.
When your charactor collision with any other objects ,such as ground,wall, your charactor maybe rotate.
You could freeze rigidbody's rotation via inspector like Bijan says.
I don't recommand your use AddForce and MovePosition together.It may has other problem.
like this
This is due to the nature of the Rigidbody component. This component is very lifelike and when force is added and removed, the object still has inertia and therefore the object is still moving.
When it comes to the rotation it is due to the fact that when a force is applied to an object, the object also acquires rotational motion (also known as angular motion).
You can freeze the rotation using the constraints on the Rigidbody component.
Have a deeper look into this component [here].1
In my opinion, it is one of the core components of Unity and it is definitely worth looking into.
I hope I answered your question!
First of all I'm new to Unity and Game programming. I'm trying to add a cricket bowling like animation. But when the ball touches the ground It doesn't bounce at all and it shows an weird rolling animation.
Here is the GIF,
So I just added the velocity in code,
public class Ball : MonoBehaviour {
public float EndPointX ;
public float EndPointY ;
bool ForceAdded = true;
private Rigidbody2D rigidBody;
void Start () {
rigidBody = GetComponent<Rigidbody2D> ();
}
void Update () {
rigidBody.velocity = new Vector3(EndPointX, EndPointY, 0)*2;
}
}
My Bounce 2d material file,
Ball Properties,
It bounces perfectly without any velocity. I mean when it falls in a straight angle.
Thanx For The Help!!
Since Update() runs every frame, you are continuously setting the velocity, and immediately overwriting the bounce materials attempts to change its direction of movement. If you move the velocity to your Start() method, the velocity will only be set once and the bounciness will be able to influence your object properly.
void Start () {
rigidBody = GetComponent<Rigidbody2D> ();
rigidBody.velocity = new Vector3(EndPointX, EndPointY, 0)*2;
}
I have a blockbreaker type game coded using C# that mainly works fine but upon play testing i have found the ball will slow right down in certain situations! i.e if it wedges between to game object bricks the force of the ball rapidly slows down! 8 times out of ten this will not happen but other times it does and im unsure why! i will post the Ball script i think you will need to help solve this but should you need anymore information then please ask.
public class Ball : MonoBehaviour {
private Paddle paddle;
private bool hasStarted = false;
private Vector3 paddleToBallVector;
void Start () {
paddle = GameObject.FindObjectOfType<Paddle> ();
paddleToBallVector = this.transform.position - paddle.transform.position;
}
// Update is called once per frame
void Update () {
if (!hasStarted) {
//lock ball relative to the paddle
this.transform.position = paddle.transform.position + paddleToBallVector;
//wait for mouse press to start
if (Input.GetMouseButtonDown (0)) {
//if (Input.GetTouch(0).phase == TouchPhase.Ended){
hasStarted = true;
this.GetComponent<Rigidbody2D> ().velocity = new Vector2 (2f, 10f);
}
}
}
void OnCollisionEnter2D(Collision2D collision){
Vector2 tweak = new Vector2 (Random.Range(0f,0.2f),Random.Range(0f,0.2f));
if (hasStarted) {
GetComponent<AudioSource> ().Play ();
GetComponent<Rigidbody2D>().velocity += tweak;
}
}
}
You are adding directly to the velocity of the ball. The velocity variable defines a direction and speed, not just a speed as you are thinking here.
So, when the ball collides with a block it has a velocity of (+x, +y). After the collision and bounce is has a velocity of (+x, -y). So by adding a random positive value to the velocity when it has the -y velocity means that it will slow down the ball.
This does not happen every time because you are moving the ball in your Update() method. Change that to 'FixedUpdated()'. Fixed Update handles all physics calculations.
Also, I would recommend having a RigidBody2D on your Ball object. Moving physics objects should always have RigidBodies. With this, you can then use AddForce in the forward direction of your ball and that should solve your problem.
EDIT: I see you have a RigidBody2D already. I missed that the first time. Instead of having GetComponent<RigidBody2D>().velocity try GetComponent<RigidBody2D>().AddForce( transform.forward * impulseAmount, ForceMode.Impluse );
I am trying to create a drag and drop functionality. I have a game object that lies on a cube (like a table). I want to allow the player to drag and drop this object to multiple "hot spots". At the same time, while this is a 3D game, they should not be allowed to drag the object off of the table, above it, bellow it, etc. Just drag across the top of the table. The way I have this setup is like this:
On the table top (a cube with a rigid body)I have two planes. This plane is just decorative to let the user see where to drop to. Each of these planes has a child plane (a much smaller area, centered in the parent). This child has a Box Collider (isTrigger= true) that extends high up into the air, like a pole sticking out of the ground)
I then have a cube that has a rigid body and a box collider (isTrigger= true). This cube is instantiated onto the "table" top, over one of the hot spots.
HotSpotCode.cs
public class HotSpotCodeScript : MonoBehaviour {
public void OnTriggerEnter(Collider other){
DraggableObject ds = other.GetComponent<DraggableObject>();
ds.startPos = this.gameObject.transform.position;
}
}
DraggableObject.cs
public class DraggableObject : MonoBehaviour {
float lerpTime = 1f;
float currentLerpTime;
float moveDistance = 10f;
Plane plane;
public Vector3 startPos, endPos;
public bool drag;
Vector3 position;
void Start(){
startPos = transform.position;
endPos = transform.position + transform.up * moveDistance;
}
void OnMouseDown(){
plane = new Plane(Vector2.up, transform.position);
drag = true; // start dragging
}
void OnMouseUp(){
lerp();
drag = false;
}
private void lerp() {
currentLerpTime += Time.deltaTime;
if (currentLerpTime > lerpTime)
{
currentLerpTime = lerpTime;
}
float perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(startPos, endPos, perc);
}
public void Update()
{
position = transform.localPosition;
if (drag)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float distance;
if (plane.Raycast(ray, out distance))
{
transform.position = ray.GetPoint(distance);
}
}
}
public void OnTriggerEnter(Collider other){
endPos = other.transform.position;
}
}
This code mostly works somewhat. I am able to drag the object around and it won't leave the plan it it resides on, for short distances. But, the further away I drag the object, then release it, the more likely it is that the object will not snap back to its original start position and will get stuck somewhere along the way. I am not sure if this is an issue with the Lerp() function or maybe how the plane it is dragged across is created. Can anyone help me out? If you need more info, please let me know. I've been working on this for weeks and haven't gotten much further that this.
Another issue that pops up, as well, is that when the object that is being dragged is released, and comes into contact with the hot spot's collider, it stops the object at its exact point. So, if a corner of the cube comes into contact with the collider, the cube will not come to rest centered on the hot spot.
Im currently developing an Air hockey game in Unity3d. The issue I'm having is that when the player attempts to hit the puck too quickly, the player ends up going through the puck and therefore there is no collision. The game works perfectly as expected if the player stays still and the puck hits the player or if the player hits the puck at a slow pace.
The player has a rigidbody using continuous collision detection using a capsule collider. The puck also has rigidbody with continuous dynamic collision detection and a mesh collider with convex.
I tried setting the fixed timestep to 0.01 but that didn't have an effect. Here is the script for the player movement:
void ObjectFollowCursor()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 point = ray.origin + (ray.direction * distance);
Vector3 temp = point;
temp.y = 0.2f; // limits player on y axis
cursorObject.position = temp;
}
and here is the code for the puck when it collides with the player:
// If puck hits player
if(collision.gameObject.tag == "Player")
{
Vector3 forceVec = this.GetComponent<Rigidbody>().velocity.normalized * hitForce;
rb.AddForce(forceVec, ForceMode.Impulse);
Debug.Log ("Player Hit");
}
Any help would be much appreciated. Thanks.
The problem you are having its called "tunneling".
This happens because your object is moving at a high speed and in that specific frame the collision is not detected. In frame n the ball is just in front of the bat, but when frame n+1 is calculated the ball has moved behind the bat, thus "missing" the collision completely.
It is a common problem but there are solutions.
I recommend you study this script and try to implement on your game.
This is not my code:
SOURCE: http://wiki.unity3d.com/index.php?title=DontGoThroughThings
using UnityEngine;
using System.Collections;
public class DontGoThroughThings : MonoBehaviour
{
// Careful when setting this to true - it might cause double
// events to be fired - but it won't pass through the trigger
public bool sendTriggerMessage = false;
public LayerMask layerMask = -1; //make sure we aren't in this layer
public float skinWidth = 0.1f; //probably doesn't need to be changed
private float minimumExtent;
private float partialExtent;
private float sqrMinimumExtent;
private Vector3 previousPosition;
private Rigidbody myRigidbody;
private Collider myCollider;
//initialize values
void Start()
{
myRigidbody = GetComponent<Rigidbody>();
myCollider = GetComponent<Collider>();
previousPosition = myRigidbody.position;
minimumExtent = Mathf.Min(Mathf.Min(myCollider.bounds.extents.x, myCollider.bounds.extents.y), myCollider.bounds.extents.z);
partialExtent = minimumExtent * (1.0f - skinWidth);
sqrMinimumExtent = minimumExtent * minimumExtent;
}
void FixedUpdate()
{
//have we moved more than our minimum extent?
Vector3 movementThisStep = myRigidbody.position - previousPosition;
float movementSqrMagnitude = movementThisStep.sqrMagnitude;
if (movementSqrMagnitude > sqrMinimumExtent)
{
float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);
RaycastHit hitInfo;
//check for obstructions we might have missed
if (Physics.Raycast(previousPosition, movementThisStep, out hitInfo, movementMagnitude, layerMask.value))
{
if (!hitInfo.collider)
return;
if (hitInfo.collider.isTrigger)
hitInfo.collider.SendMessage("OnTriggerEnter", myCollider);
if (!hitInfo.collider.isTrigger)
myRigidbody.position = hitInfo.point - (movementThisStep / movementMagnitude) * partialExtent;
}
}
previousPosition = myRigidbody.position;
}
}
You were right to try continuous collision detection (CCD). There are some constraints (especially in this case where you want to use CCD with two moving objects rather than one moving object and one static object), but it is designed for this kind of scenario. The Rigidbody documentation goes into these constraints:
Set the collision detection mode to Continuous to prevent the
rigidbody from passing through any static (ie, non-rigidbody)
MeshColliders. Set it to Continuous Dynamic to also prevent the
rigidbody from passing through any other supported rigidbodies with
collision detection mode set to Continuous or Continuous Dynamic.
Continuous collision detection is supported for Box-, Sphere- and
CapsuleColliders.
To sum up, both puck and paddle need to be set to Continuous Dynamic, and both need to be Box-, Sphere-, or Capsule Colliders. If you can make these constraints work for your game you should be able to get continuous collision detection without writing it yourself.
A note about Unity's CCD that bears repeating:
Note that continuous collision detection is intended as a safety net
to catch collisions in cases where objects would otherwise pass
through each other, but will not deliver physically accurate collision
results, so you might still consider decreasing the fixed Time step
value in the TimeManager inspector to make the simulation more
precise, if you run into problems with fast moving objects.
But since you are manually specifying the collision reaction, that might not be an issue.