Reputation: 85
So I am a bit of a noob when it comes to scripting/programming and I am in need of some assistance. Currently I have the script below attached to an empty game object that is in the middle of a sphere. The empty object has a child cube that is on the outside of the sphere so when the empty object rotates around, the cube moves along the outside of the sphere.
My problem comes when that cube comes in contact with any other immovable object on the outside of the sphere, it moves and rotates around that object which is what I dont want. What I want is for the cube to stop moving in the direction the immovable object is but be able to move left/right/back. I have written code that when the cube comes in contact with the immovable object it stops the script that rotates the empty game object, but then I cant move the empty game object ever again...its just frozen there. Here is my script.
This is the script attached to the empty game object
public class SphereMotor : MonoBehaviour {
public Vector3 eulerAngleVelocity;
public Rigidbody rb;
private float speed = 45.0f;
public bool collided = false;
public float turnSpeed = 45.0f;
void Start()
{
rb = GetComponent<Rigidbody>();
}
public void Collided()
{
collided = true;
}
public void NotCollided()
{
collided = false;
}
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow) && !collided) {
transform.Rotate (0.0f, turnSpeed * Time.deltaTime, 0.0f);
}
if (Input.GetKey(KeyCode.RightArrow) && !collided) {
transform.Rotate (0.0f, -turnSpeed * Time.deltaTime, 0.0f);
}
if (Input.GetKey(KeyCode.UpArrow) && !collided) {
transform.Rotate (turnSpeed * Time.deltaTime, 0.0f, 0.0f);
}
if (Input.GetKey(KeyCode.DownArrow) && !collided) {
transform.Rotate (-turnSpeed * Time.deltaTime, 0.0f, 0.0f);
}
}
This is the script for collision
herebool isColliding = false;
public SphereMotor _SphereMotor;
void Start()
{
_SphereMotor = GameObject.FindObjectOfType<SphereMotor>();
}
void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.tag == "Player")
{
//isColliding = true;
StartCoroutine(ObjectColliding());
Debug.Log("wall hit");
}
}
void OnCollisionExit(Collision collision)
{
if(collision.gameObject.tag == "Player")
{
isColliding = false;
}
}
public IEnumerator ObjectColliding()
{
isColliding = true;
yield return new WaitForSeconds(2f);
isColliding = false;
}
void Update()
{
if(isColliding == true)
_SphereMotor.Collided();
if(isColliding == false)
_SphereMotor.NotCollided();
}
Here is a video showcasing my problem in action: https://www.youtube.com/watch?v=tkbbiTwkTqA
I tried using a Coroutine to try and fix the problem but it doesnt really work. When I hold one of the movement buttons down the cube still rotates around the immovable object. I dont really know how to approach this problem so any advice would be really appreciated!
Upvotes: 1
Views: 1394
Reputation: 2943
One way would be to use collision.relativeVelocity or collision.contacts to detect from which direction the collision came, then to save some flags like collidedLeft, collidedRight, and so something similar to what you do now - on update do not rotate in some direction if one of the flags is true. But then, what it comes in some angle? You basically need to implement the whole physics system.
I would suggest a little different configuration to achieve this, that uses built in physics engine.
First, it is generally a bad practice to use transform.rotation on objects that affect rigidbodies, as it requires extra computation. It is better to work with rigidbody directly in FixedUpdate.
Now, both the inner sphere and the cube are rigidbodies. We do not need gravity or angular drag. Instead of parenting them, a better way is to connect them using fixed joint.
Next, in inner spere's rigidbody properties, make its position fixed on X, Y and Z axis (we want it to rotate only, not to move).
The cube needs a collider. Inner shpere doesnt. And now you only need the SphereMotor, no need for collision handling at all, all is done by rigidbodies. The code is very similar to yours, just manipulates rigidbody speed instead of doing it manually:
using UnityEngine;
public class SphereMotor : MonoBehaviour {
public Rigidbody rb;
public float turnSpeed = 45.0f;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
rb.freezeRotation = false;
if (Input.GetKey(KeyCode.LeftArrow)) {
// Unity measures angular velocity in radians
rb.angularVelocity = new Vector3(0.0f, turnSpeed*Mathf.Deg2Rad, 0.0f);
} else if (Input.GetKey(KeyCode.RightArrow)) {
rb.angularVelocity = new Vector3 (0.0f, -turnSpeed*Mathf.Deg2Rad, 0.0f);
} else if (Input.GetKey(KeyCode.UpArrow)) {
rb.angularVelocity = new Vector3 (turnSpeed*Mathf.Deg2Rad, 0.0f, 0.0f);
} else if (Input.GetKey(KeyCode.DownArrow)) {
rb.angularVelocity = new Vector3 (-turnSpeed*Mathf.Deg2Rad, 0.0f, 0.0f);
} else {
rb.freezeRotation = true; // No key pressed - stop
}
}
}
I create a demo project, you can get it from my dropbox here: https://www.dropbox.com/s/b1kjrax0repku48/SphereSampleProject.zip?dl=0
Hope that helps
Upvotes: 0