After asking here for a solution, which worked, my sphere "walks" to each point of a list. Now, I'm trying to "force" a rotation around the x axis, to fake the ball is "rolling". I came up with the following code, but it doesn't work (it makes a progressive acceleration of the rotation on the x-axis, which I both dont want and... dont understand).
Create a new project, add a sphere and add this small script. Then add 2 points in the points list. This script does nothing but moving the object (the sphere) each points of the list, and make a simple rotation around the x-axis.
Simple rotation around the x-axis is not realistic (it's working as long as the sphere doesn't change its direction, but in this case, it should rotate on x + on z, but I dont know how to do this too...) so if you have a more realistic idea its even more welcome.
using UnityEngine;
public class MoveAlongPath : MonoBehaviour
{
public Vector3[] points;
public float speed = 1.0f;
public float rotateSpeed = 200.0f;
public float minDistance = Vector3.kEpsilon;
private SphereCollider _c;
[SerializeField] private Vector3 _posStart;
[SerializeField] private Vector3 _posEnd;
[SerializeField] private int _currentIndex;
[SerializeField] private Rigidbody _rigidbody;
[SerializeField] private Vector3 _direction;
[SerializeField] private Vector3 _directionNormalized;
[SerializeField] private float _xRotation;
private void NextPoint()
{
_posStart = points[_currentIndex];
transform.position = _posStart;
_currentIndex = ++_currentIndex % points.Length;
_posEnd = points[_currentIndex];
_direction = _posEnd - _posStart;
_directionNormalized = _direction.normalized;
}
private void Start()
{
_rigidbody = GetComponent<Rigidbody>();
NextPoint();
}
private void FixedUpdate()
{
_xRotation = (_xRotation + -10 * Time.fixedDeltaTime) % 360;
Quaternion q = Quaternion.LookRotation(
_directionNormalized, Vector3.up
);
transform.rotation = Quaternion.RotateTowards(
transform.rotation, q, rotateSpeed * Time.fixedDeltaTime
) * Quaternion.AngleAxis(_xRotation, Vector3.left);
Vector3 step = speed * Time.fixedDeltaTime * _directionNormalized;
if (step.magnitude > _direction.magnitude) {
step = _direction; // don't overshoot
}
Vector3 newPos = transform.position + step;
_rigidbody.MovePosition(newPos);
if (Vector3.Distance(_posEnd, transform.position) < minDistance) {
/* close enough -> next point: */
NextPoint();
}
}
}
Related
I've been working on this Controller, where I walk around a small planet. I'm using Quaternion.Slerp, and when I'm at certain points on the planet, the controller slowly drifts, rotating around the Y axis. My thought is that it is holding a value based on my starting position, and so when I move to different points, the Slerp function is trying to spin me toward that location? I've tried messing around with the script, moving different portions to their own custom methods to pinpoint the issue, but I'm a bit lost at this point. Any help would be appreciated!
I think the issue is going to be somewhere in the bottom two methods NewRotation, or RunWalkStand.
`
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] private LayerMask _groundMask;
[SerializeField] private Transform _groundCheck;
[SerializeField] private Transform cam;
public float jumpCooldown = 1f;
public float groundCheckRadius = 0.3f;
private float speed = 8;
private float runSpeed = 2;
private float turnSpeed = 800f;
private float jumpForce = 500f;
private Rigidbody rb;
private Vector3 direction;
private GravityBody _gravityBody;
private Animator playerAnimator;
private GameObject planetRecall;
void Start()
{
_gravityBody = transform.GetComponent<GravityBody>();
playerAnimator = GetComponent<Animator>();
planetRecall = GameObject.FindGameObjectWithTag("Planet Recall");
}
void Update()
{
bool isCloseToGround = Physics.CheckSphere(_groundCheck.position, groundCheckRadius, _groundMask);
NewRotation();
if (Input.GetKeyDown(KeyCode.Space) && isCloseToGround)
{
Jump();
}
}
void FixedUpdate()
{
RunWalkStand();
}
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject == planetRecall)
{
playerAnimator.SetBool("Grounded", true);
}
}
private void Jump()
{
rb.AddForce(-_gravityBody.GravityDirection * jumpForce, ForceMode.Impulse);
playerAnimator.SetTrigger("Fly_trig");
playerAnimator.SetBool("Grounded", false);
}
private void NewRotation()
{
rb = transform.GetComponent<Rigidbody>();
Vector3 mouseRotationY = new Vector3(0f, Input.GetAxisRaw("Mouse X"), 0f);
Quaternion rightDirection = Quaternion.Euler(0f, mouseRotationY.y * (turnSpeed * Time.deltaTime), 0f).normalized;
Quaternion newRotation = Quaternion.Slerp(rb.rotation, rb.rotation * rightDirection, Time.deltaTime * 1000f);
rb.MoveRotation(newRotation);
//Move Side to Side
Vector3 sideToSide = transform.right * Input.GetAxisRaw("Horizontal");
rb.MovePosition(rb.position + sideToSide * (speed * Time.deltaTime));
}
private void RunWalkStand()
{
direction = new Vector3(0f, 0f, Input.GetAxisRaw("Vertical")).normalized;
Vector3 forwardDirection = transform.forward * direction.z;
bool isRunning = direction.magnitude > 0.1f;
//Walking
if (isRunning && !Input.GetKey(KeyCode.LeftShift))
{
rb.MovePosition(rb.position + forwardDirection * (speed * Time.deltaTime));
playerAnimator.SetFloat("Speed_f", 0.5f);
}
//Running
else if(isRunning && Input.GetKey(KeyCode.LeftShift))
{
rb.MovePosition(rb.position + forwardDirection * (speed * runSpeed * Time.deltaTime));
playerAnimator.SetFloat("Speed_f", 1f);
}
//Standing
else if(isRunning == false)
{
playerAnimator.SetFloat("Speed_f", 0f);
}
}
}
`
it might be because a slerp is not a lineair line so its velocity is less when you are close to the endpoint maybe try Quaternion.RotateTowards.
I "Mostly" solved this issue, and it's an unexpected solution. The drift was solved by freezing rotation on the player in the inspector (Still don't know what was causing the player to spin though) However this caused extra jitter. I found two issues.
If I've selected ANY game object in the hierarchy, and play the game, the game is jittery, but if I click off of it onto nothing, the game runs smoother (Not completely better, but much smoother).
I'm using a Gravity script for the planet that applies gravity to the player's Rigidbody, and I believe with multiple things acting on the RB at the same time, it causes jitter. I'm thinkin of trying to greatly simplify the project, but putting the different methods from the scripts into different Update methods helps a good bit depending on the combination (Not as simple as Physics movement in FixedUpdate and camera in LateUpdate unfortunately).
Create a new project, add a sphere, a rigidbody and add this small script. Then add 2 points in the points list. This script does nothing but moving the object each points of the list.
using System;
using UnityEngine;
public class MoveAlongPath : MonoBehaviour
{
public Vector3[] points;
public float speed = 1.0f;
public float minDistance = Vector3.kEpsilon;
[SerializeField] private Vector3 _posCurrent;
[SerializeField] private Vector3 _posNext;
[SerializeField] private Vector3 _newPos;
[SerializeField] private int _currentIndex;
[SerializeField] private Vector3 _rotate;
[SerializeField] private Vector3 _oldPosition;
[SerializeField] private Rigidbody _rigidbody;
private void NextPoint()
{
_posCurrent = points[_currentIndex];
transform.position = _posCurrent;
_currentIndex++;
if (_currentIndex == points.Length) {
_currentIndex = 0;
}
_posNext = points[_currentIndex];
}
private void Start()
{
_rigidbody = GetComponent<Rigidbody>();
NextPoint();
}
private void FixedUpdate()
{
Transform t = transform;
_oldPosition = t.position;
float singleStep = speed * Time.fixedDeltaTime;
_newPos = new Vector3(
_oldPosition.x + singleStep,
_oldPosition.y + singleStep,
_oldPosition.z + singleStep
);
_rigidbody.MovePosition(_newPos);
if (Vector3.Distance(_newPos, _posNext) <= minDistance) {
/* close enough -> next point: */
NextPoint();
}
}
}
Well... it doesn't work. I've tried many ways:
using Update() instead of FixedUpdate() - but AFAIK Update() should only handle inputs and FixedUpdate() should make actual 3D physics computation
using transform.position = _newPos; doesn't work, and MovePosition() doesn't work too...
I'm stuck!
You're not moving in a meaningful direction, you're just moving +1 unit per second on each axis.
Try
var direction = _posNext - _posCurrent;
This is the direction, but if you moved that vector you'd be there in one tick. What you want is the normalized unit vector, which has a magnitude of 1, then you can multiply your speed by that.
I'm writing this on a cell phone so I may not have the methods exact:
var singleStep = speed * Time.fixedDeltaTime * direction.normalized;
And now you want to take the SMALLER of your direction or singleStep. It's extremely likely that your singleStep would overshoot the target, and you'll know you're going to overshoot because the singleStep is a bigger step than just going to the target.
if(singleStep.magnitude > direction.magnitude)
{
singleStep = direction; // don't overshoot
}
Then you can move and that should get it :)
_newPos = _oldPosition + singleStep;
_rigidbody.MovePosition(_newPos);
Edited by OP: here's the full code of the working solution, thanks again!
using UnityEngine;
public class MoveAlongPath : MonoBehaviour
{
public Vector3[] points;
public float speed = 1.0f;
public float minDistance = Vector3.kEpsilon;
private SphereCollider _c;
[SerializeField] private Vector3 _posStart;
[SerializeField] private Vector3 _posEnd;
[SerializeField] private int _currentIndex;
[SerializeField] private Rigidbody _rigidbody;
[SerializeField] private Vector3 _direction;
private void NextPoint()
{
_posStart = points[_currentIndex];
transform.position = _posStart;
_currentIndex = ++_currentIndex % points.Length;
_posEnd = points[_currentIndex];
_direction = _posEnd - _posStart;
_direction = _direction.normalized;
Debug.Log("Going from "+_posStart+" -> to -> "+_posEnd);
}
private void Start()
{
_rigidbody = GetComponent<Rigidbody>();
NextPoint();
}
private void FixedUpdate()
{
Vector3 step = speed * Time.fixedDeltaTime * _direction.normalized;
if(step.magnitude > _direction.magnitude) {
step = _direction; // don't overshoot
}
Vector3 newPos = transform.position + step;
_rigidbody.MovePosition(newPos);
if (Vector3.Distance(_posEnd, transform.position) < minDistance) {
/* close enough -> next point: */
NextPoint();
}
}
}
I wanted to make an endless runner game where the player can jump and he moves forward automatically, so, for the forward movement i wanted to use the rigidbody velocity function in order to make the movement smoother and for the jump i made a custom function. My problem is, when i use in my update method the velocity function alone, it works, when i use it with my jump function it stops working. What can i do?. I tried changing the material of the floor and of the player's box collider to a really slippery one but nothing
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MovePrima : MonoBehaviour
{
[SerializeField]
private float _moveSpeed = 0f;
[SerializeField]
private float _gravity = 9.81f;
[SerializeField]
private float _jumpspeed = 3.5f;
public float speedAccel = 0.01f;
private CharacterController _controller;
private float _directionY;
public float startSpeed;
public float Yspeed = 10f;
private int i = 0;
public float touchSensibility = 50f;
public int accelTime = 600;
[SerializeField]
Rigidbody rb;
// Start is called before the first frame update
void Start()
{
startSpeed = _moveSpeed;
_controller = GetComponent<CharacterController>();
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
rb.velocity = new Vector3(0f, 0f, 1f) * _moveSpeed;
touchControls();
}
public void touchControls()
{
if (SwipeManager.swipeUp && _controller.isGrounded)
{
_directionY = _jumpspeed;
print("Entrato");
}
_directionY -= _gravity * Time.fixedDeltaTime;
_controller.Move((new Vector3(0, _directionY, 0)) * Time.fixedDeltaTime * Yspeed);
i += 1;
if (i % accelTime == 0)
{
_moveSpeed += startSpeed * speedAccel;
i = 0;
}
}
private void OnControllerColliderHit(ControllerColliderHit hit) //Checks if it collided with obstacles
{
if(hit.transform.tag == "Obstacle")
{
GameOverManager.gameOver = true;
}
}
}
You should either use the CharacterController or the Rigidbody. With the CharacterController you tell Unity where to place the character and you have to check yourself if it is a valid position. With the Rigidbody you push it in the directions you want, but the physics around the character is also interfering.
You can use Rigidbody.AddForce to y when jumping instead of _controller.Move.
I am trying to implement a RADAR sensor in unity. I am currently using sphere cast to identify the object and measure its velocity and distance. At the moment even though there are several objects in within the radius of the sphere cast ,It only identifies the nearest one. How can i make it recognize all the objects within the sphere cast.?
Any help would be appreciated..
This is what i have done now which just identifies identify the closest object.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class spherecast : MonoBehaviour
{
Rigidbody rb;
public GameObject curobject;
public float radius;
public float maxdist;
public LayerMask layermask;
public float velocity;
public Time deltatime;
public Vector3 previous;
private Vector3 origin;
private Vector3 direction;
private float hitdist;
// Use this for initialization
void Start()
{
previous = curobject.transform.position;
}
// Update is called once per frame
void Update()
{
velocity = hitdist/Time.deltaTime;
origin = transform.position;
direction = transform.forward;
RaycastHit hit;
if (Physics.SphereCast(origin, radius, direction, out hit, maxdist, layermask, QueryTriggerInteraction.UseGlobal))
{
curobject = hit.transform.gameObject;
hitdist = hit.distance;
Debug.Log("Distance" + hitdist);
velocity = ((curobject.transform.position - previous).magnitude) / Time.deltaTime;
previous = curobject.transform.position;
Debug.Log("Velocity" + velocity);
// Debug.Log("Velocity = " + velocity);
}
else
{
hitdist = maxdist;
curobject = null;
}
}
private void ondrawgizmosselected()
{
Gizmos.color = Color.red;
Debug.DrawLine(origin, origin + direction * hitdist);
Gizmos.DrawWireSphere(origin + direction * hitdist, radius);
}
}
How can i make it recognize all the objects within the sphere cast.?
I believe you need to use SphereCastAll instead of SphereCast for that.
https://docs.unity3d.com/ScriptReference/Physics.SphereCastAll.html
Answer:
"Like Physics.SphereCast, but this function will return all hits the sphere sweep intersects."
It returns a RaycastHit[].
I'm working on maze game for Android in Unity 5.1.1f1 and I have troubles with controlling my ball with accelerometer. At start I tried:
public class PlayerController : MonoBehaviour {
public GameObject sphere;
public Camera camera;
public float speed=200;
private Rigidbody myRigidBody;
void Start()
{
myRigidBody = gameObject.GetComponent<Rigidbody> ();
}
void FixedUpdate()
{
float moveH = Input.acceleration.x;
float moveV = -Input.acceleration.z;
Vector3 move = new Vector3 (moveH, 0.0f, moveV);
myRigidBody.AddForce (move * speed*Time.deltaTime);
}
}
But the ball is not moving as it should. Then I tried another solution. But my ball still doesn't move right. It seems like it's sometimes hard to move left/right/forward/backward, also it's possible my ball will rotate in the opposite direction.
public class PlayerController : MonoBehaviour {
public float speedAc = 10;
//accelerometer
private Vector3 zeroAc;
private Vector3 curAc;
private float sensH = 10;
private float sensV = 10;
private float smooth = 0.5f;
private float GetAxisH = 0;
private float GetAxisV = 0;
// Use this for initialization
void Start () {
ResetAxes();
}
//accelerometer
void ResetAxes(){
zeroAc = Input.acceleration;
curAc = Vector3.zero;
}
void FixedUpdate () {
curAc = Vector3.Lerp(curAc, Input.acceleration-zeroAc, Time.deltaTime/smooth);
GetAxisH = Mathf.Clamp(curAc.x * sensH, -1, 1);
GetAxisV = Mathf.Clamp(-curAc.z * sensV, -1, 1);
Vector3 movement = new Vector3 (GetAxisH, 0.0f, GetAxisV);
GetComponent<Rigidbody>().AddForce(movement * speedAc);
}
}
Can someone help me, please?
I have answered this one in another SO question: Unity 3D realistic accelerometer control - you've got two variants of control there, just copy the one you find fit.
If you're having more problems, ask.