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.
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();
}
}
}
Here I share my AiPathfinding script. I want my AI walks to a destination (target) when game starts. however, when AI collide an certain object (finding with tag="bullet") then I want to set new destination. Although code does not give error, when playing AI goes to first destination then stops there even though it collides with certain object on the way.
Can someone have a look please?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class AIPathfinding : MonoBehaviour
{
[SerializeField] public Transform target;
[SerializeField] public Transform target2;
[SerializeField] public float speed = 200f;
[SerializeField] public float nextWayPointDistance = 3f;
[SerializeField] Path path;
[SerializeField] int currentWaypoint = 0;
[SerializeField] bool reachedEndOfPath = false;
[SerializeField] Seeker seeker;
[SerializeField] Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
seeker = GetComponent<Seeker>();
rb = GetComponent<Rigidbody2D>();
InvokeRepeating("UpdatePath", 0f, 0.5f);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "bullet")
{
UpdatePath(target2);
}
}
public void UpdatePath(Transform target2)
{
if (seeker.IsDone())
{
seeker.StartPath(rb.position, target.position, OnPathComplete);
}
}
void OnPathComplete(Path p)
{
if (!p.error)
{
path = p;
currentWaypoint = 0;
}
}
void Update()
{
if (path == null)
{
return;
}
if (currentWaypoint >= path.vectorPath.Count)
{
reachedEndOfPath = true;
return;
}
else
{
reachedEndOfPath = false;
}
Vector2 direction = ((Vector2)path.vectorPath[currentWaypoint] - rb.position).normalized;
Vector2 force = direction * speed * Time.deltaTime;
rb.AddForce(force);
float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]);
if (distance < nextWayPointDistance)
{
currentWaypoint++;
}
}
}
My guess is that you never stopped the Coroutine InvokeRepeating("UpdatePath", 0f, 0.5f). So do call CancelInvoke() when the bullet hits and change the target of the seeker (Start InvokeRepeating(...) again).
I assume NavMeshAgent.SetDestination(Vector3 position) is called in the function seeker.SetTarget(). And somehow Path gets calculated and set.
You could also just move the body of Update Path into the Update function or call it from there. Nevermind the Interval. Or do call it in your custom interval. Just calculate the next time you want to call it so if the nextUpdatePathTime (Time.time plus your cooldown) is smaller than Time.time then call it and calculate the next nextUpdatePathTime.
I just started learning Unity. I tried to make a simple box move by using this script. The premise is, whenever someone presses 'w' the box moves forward.
public class PlayerMover : MonoBehaviour {
public float speed;
private Rigidbody rb;
public void Start () {
rb = GetComponent<Rigidbody>();
}
public void Update () {
bool w = Input.GetButton("w");
if (w) {
Vector3 move = new Vector3(0, 0, 1) * speed;
rb.MovePosition(move);
Debug.Log("Moved using w key");
}
}
}
Whenever I use this, the box doesn't move forward on a 'w' keypress. What is wrong with my code? I thought it might be the way I have my Vector 3 move set up so I tried replacing the z-axis with speed, but that didn't work. Could someone tell me where I am messing up?
You move Rigidbody with Rigidbody.MovePosition and rotate it with Rigidbody.MoveRotation if you want it to properly collide with Objects around it. Rigidbody should not be moved by their position, rotation or the Translate variables/function.
The "w" is not predefined like SherinBinu mentioned but that's not the only problem. If you define it and use KeyCode.W it still won't work. The object will move once and stop.
Change
Vector3 move = new Vector3(0, 0, 1) * speed;
rb.MovePosition(move);
to
tempVect = tempVect.normalized * speed * Time.deltaTime;
rb.MovePosition(transform.position + tempVect);
This should do it:
public float speed;
private Rigidbody rb;
public void Start()
{
rb = GetComponent<Rigidbody>();
}
public void Update()
{
bool w = Input.GetKey(KeyCode.W);
if (w)
{
Vector3 tempVect = new Vector3(0, 0, 1);
tempVect = tempVect.normalized * speed * Time.deltaTime;
rb.MovePosition(transform.position + tempVect);
}
}
Finally, I think you want to move your object with wasd key. If that's the case then use Input.GetAxisRaw or Input.GetAxis.
public void Update()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
Vector3 tempVect = new Vector3(h, 0, v);
tempVect = tempVect.normalized * speed * Time.deltaTime;
rb.MovePosition(transform.position + tempVect);
}
"w" is not predefined unless you explicitly define it. Use KeyCode.W
Try this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMover : MonoBehaviour {
public float speed;
private Rigidbody rb;
void Start () {
rb = GetComponent<Rigidbody>();
}
void Update () {
bool w = Input.GetKey(KeyCode.W);
if (w) {
Vector3 move = new Vector3(0, 0, 1) * speed *Time.deltaTime;
rb.MovePosition(move);
Debug.Log("Moved using w key");
}
}
}
Use Input.GetKey(KeyCode.W) for getting input.
EDIT NOTE:
To move the object relative to its initial position use rb.MovePosition(transform.position+move) rather than rb.MovePosition(move)
Instead of making w a bool, you can use axis, also, in unity editor you should make it so the rigidbody movement is frozen
here is some code
void update()
{
rb.AddForce(Input.GetAxis("Horizontal"));
}
bool w = Input.GetKeyDown(KeyCode.W);
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.