I am trying to move my Player by touch and make it collides so I wrote this script but the problem is that my player is not colliding with the other objects so how to make it collide so how to fix this?
[SerializeField] private Rigidbody rb;
private Vector3 targetPosition;
private void Start()
{
targetPosition = transform.position;
if (!rb)rb = GetComponent<Rigidbody>();
// since this rigibody is going to be moved via code not Physics it should be kinemtic
rb.isKinematic = true;
// in order to smooth the movement
rb.interpolation = RigidbodyInterpolation.Interpolate;
}
void Update()
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Moved)
{
targetPosition += Vector3.right * touch.deltaPosition.x * speedmodifier;
targetPosition += Vector3.forward * touch.deltaPosition.y * speedmodifier;
}
}
}
private void FixedUpdate()
{
rb.MovePosition(targetPosition);
}
You can use MovePosition() with non kinematic rigidbodys, but it will then work like transform.position=newPosition and teleports the object to the new position (rather than performing a smooth transition). Furthermore you can move a kinematic rigidbody object from a script by modifying its Transform Component but it will not respond to collisions and forces like a non-kinematic rigidbody.
So if you want your rigidbody to be correctly affected by Physics you need to use the AddForce() Method and set your rigidBody to Dynamic.
Example:
player.AddForce(new Vector2(speed, 0), ForceMode2D.Impulse);
Related
I am trying ground collision of kinematic bodies using rigidbody2D.cast. Player and ground collider overlap with each other.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ForceMethod : MonoBehaviour
{
[SerializeField] Vector2 worldGravity;
[SerializeField] float downCastDistance = 0.05f;
Vector2 startingVelocity = new Vector2(0, 0);
RaycastHit2D[] hits = new RaycastHit2D[2];
Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
rb.Cast(Vector2.down, hits, downCastDistance);
}
void FixedUpdate()
{
rb.MovePosition(rb.position + startingVelocity * Time.deltaTime);
startingVelocity += worldGravity * Time.deltaTime;
if (hits[0].collider.name == "Ground")
{
rb.MovePosition(rb.position);
}
}
}
Before
After
rb.MovePosition(rb.position);
does nothing, since moves the rb in its current same position.
What you want to do probably is check before moving the rb if the cast hits the ground, but cast in the fixed update as well, and not of a fixed downCastDistance but cast by the same distance the rb will travel downward (which is startingVelocity * Time.deltaTime). If it doesn't hit then go down normally as you did, if it does, move the rb to the highest position between the normal fall update and the position of the point where the raycast hit (which is at ground level):
//FixedUpdate()
float currTravelDistance = startingVelocity.y * Time.deltaTime;
rb.Cast(Vector2.down, hits, Mathf.Abs(currTravelDistance));
if (hits[0].collider.name == "Ground")
{
rb.position.y = Mathf.Max((rb.position + currTravelDistance).y, hits[0].point.y);
}
else {
rb.MovePosition(rb.position + currTravelDistance);
}
also since this moves the rb exactly on the ground, if the rb's center is in the middle of the sprite, move it up by the distance from the sprite's "feet" to its center.
Finally if the player hits the ground you also need to reset to 0 startingVelocity
wrote a script to move the player by dragging it, so at first, I moved the player with transform.position and it worked perfectly so I said its time to move it with rigidbody to make it collides with objects,
so I tried rigidbody.velocity but it not moving smoothly. so how to make this works like transform.position?
this is the script:
void Update()
{
if(Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Moved)
{
transform.position = new Vector3(
transform.position.x + touch.deltaPosition.x * speedmodifier,
transform.position.y,
transform.position.z + touch.deltaPosition.y * speedmodifier);
}
}
}
When using Rigidbody you want to do all physics related stuff in FixedUpdate. then you probably would not use velocity but set fix positions using Rigidbody.MovePosition
You should still get the User input via Update though.
I would separate the logic. Something like maybe
[SerializeField] private Rigidbody _rigidbody;
private Vector3 targetPosition;
private void Start()
{
targetPosition = transform.position;
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
// since this rigibody is going to be moved via code not Physics it should be kinemtic
_rigibody.isKinematic = true;
// in order to smooth the movement
_rigidbody.interpolation = RigidbodyInterpolation.Interpolate;
}
void Update()
{
if(Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Moved)
{
targetPosition += Vector3.right * touch.deltaPosition.x * speedmodifier;
targetPosition += Vector3.forward * touch.deltaPosition.y * speedmodifier;
}
}
}
private void FixedUpdate()
{
_rigidbody.MovePosition(targetPosition);
}
By using method transform.translate(Vector3.left * 5f * Time.Deltatime; does this change the velocity of a gameobject with a rigidbody ,because in my case it doesnt work.Is there a way i can move an object so the velocity changes .If not is there any way to measure velocity of an object without having a Rigidbody attached to it. Thanks.
You can measure the velocity vector like this:
Vector3 pos, velocity;
void Awake()
{
pos = transform.position;
}
void Update()
{
velocity = (transform.position - pos) / Time.deltaTime;
pos = transform.position;
}
You can use Transform.hasChanged
if (!this.transform.hasChanged)
{
print("Player is not moving");
}
transform.hasChanged = false;
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!
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
I'm wanting to have a script that when I click in my scene, my player will rotate and have a force added to it and will travel until it has reached the clicked point in my scene.
Right now I have it working using Vectors and having my player lerp from one point to another. But I want to amend it so I can use physics and get a better sense of my player moving. Like have him speed up to start moving and slowing down as he reaches my target loction
My script now looks like this
public GameObject isActive;
public float speed;
public Ray ray;
public Rigidbody rigidBody;
public Vector3 targetPoint;
// Use this for initialization
void Start ()
{
targetPoint = transform.position;
}
// Update is called once per frame
void Update ()
{
}
void FixedUpdate()
{
if (Input.GetMouseButton(0))
{
targetPoint = Camera.main.ScreenToWorldPoint (Input.mousePosition);
ChangeRotationTarget ();
}
Quaternion targetRotation = Quaternion.LookRotation (targetPoint - transform.position);
transform.rotation = Quaternion.Slerp (transform.rotation, targetRotation, speed * Time.deltaTime);
rigidbody.position = Vector3.Lerp(transform.position, targetPoint, speed * Time.fixedDeltaTime);
}
void ChangeRotationTarget ()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Plane playerPlane = new Plane (Vector3.up, transform.position);
float hitdist = 0.0f;
if (playerPlane.Raycast (ray, out hitdist))
{
targetPoint = ray.GetPoint (hitdist);
}
}
However, when I run this, he just slides from one point to another. Regardless of the drag or mass I put in my rigidbody.
Can someone help me make my changes? Or point me in the right direction
I didn't had time to test this, but it should almost be what you are looking for. Just apply your Quaternion and Rotation codes to it.
void FixedUpdate() {
if (Input.GetMouseButton(0))
{
targetPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (rigidBody.position != targetPoint)
{
reachedTargetPoint = false;
if (reachedTargetPoint == false)
{
GetComponent<Rigidbody>().AddForce(speed);
}
}
//zoneBeforeReachingTP should be how much units before reaching targetPoint you want to start to decrease your rigidbody velocity
if (rigidBody.position == (targetPoint - zoneBeforeReachingTP))
{
//speedReductionRate should be how much of speed you want to take off your rigidBody at the FixedUpdate time rate
rigidBody.velocity = speedReductionRate;
}
if(rigidBody.position == targetPoint)
{
rigidBody.velocity = new Vector3(0, 0, 0);
reachedTargetPoint = true;
}
ChangeRotationTarget();
}
}
That's because you modify the rigidbody.position, and your logic overrides the physics. What you have to do instead is to ApplyForce every physics frame, probably with ForceMode.Force or ForceMode.Acceleration.