I have a collider prefab (a cube, with a box collider the size of the cube). In one scene, my player collides perfectly with it, but in another scene, the player glitches and phases through one side of the collider but not the other. Rotating it changes the side that it glitches out on, so it always faces the same global direction.
What could be causing this? I've made sure the prefab for both the player and collider object are exactly the same across scenes, and nothing effects the collisions besides the player controller script which moves the player and has a condition for OnCollisionExit that sets the rigidbody's velocity to 0.
Added the code below. The input is with joysticks (think the joysticks in a helicopter). Like I said, the collision works perfectly usually, but for some reason only in this scene it doesn't. I suspect it's some kind of hierarchy or rigidbody problem but I've checked seemingly everything.
https://github.com/ben-humphries/FRC-Driving-Simulation
using UnityEngine;
using UnityEngine.UI;
public class ChassisController : MonoBehaviour {
public UIController uiController;
public Canvas PauseCanvas;
public float speedLinear = 10f;
public float speedAngular = 100f;
public float joyDeadZone = 0.5f;
public float rotationOffset = 3f;
public bool squaredMovement = false;
public DriveModes driveMode = DriveModes.Tank;
[HideInInspector]
public bool paused = false;
private Rigidbody rigidbody;
Vector3 lastLinearPosition;
float lastAngularPosition;
void Start () {
rigidbody = GetComponent<Rigidbody> ();
lastLinearPosition = Vector3.zero;
lastAngularPosition = 0f;
paused = false;
}
void FixedUpdate () {
if (!paused) {
/*
* INPUT
*/
if (driveMode == DriveModes.Tank) {
if (Mathf.Abs (Input.GetAxis ("VerticalLeft")) > joyDeadZone) {
Vector3 rotatePoint = (transform.position) + transform.TransformDirection (Vector3.right) * rotationOffset;
Vector3 rotateAxis = transform.TransformDirection (Vector3.up);
Debug.DrawRay (rotatePoint, rotateAxis * 10f, Color.red);
transform.RotateAround (rotatePoint, rotateAxis, -speedAngular * Input.GetAxis ("VerticalLeft") * Time.fixedDeltaTime * (squaredMovement ? Mathf.Abs (Input.GetAxis ("VerticalLeft")) : 1));
}
if (Mathf.Abs (Input.GetAxis ("VerticalRight")) > joyDeadZone) {
Vector3 rotatePoint = (transform.position) + transform.TransformDirection (Vector3.left) * rotationOffset;
Vector3 rotateAxis = transform.TransformDirection (Vector3.up);
Debug.DrawRay (rotatePoint, rotateAxis * 10f, Color.red);
transform.RotateAround (rotatePoint, rotateAxis, speedAngular * Input.GetAxis ("VerticalRight") * Time.fixedDeltaTime * (squaredMovement ? Mathf.Abs (Input.GetAxis ("VerticalRight")) : 1));
}
} else if (driveMode == DriveModes.Mecanum) {
if (Mathf.Abs (Input.GetAxis ("VerticalRight")) > joyDeadZone) {
transform.Translate (Vector3.forward * -speedLinear * Input.GetAxis ("VerticalRight") * Time.fixedDeltaTime * (squaredMovement ? Mathf.Abs (Input.GetAxis ("VerticalRight")) : 1));
}
if (Mathf.Abs (Input.GetAxis ("HorizontalRight")) > joyDeadZone) {
transform.Translate (Vector3.right * speedLinear / 5f * Input.GetAxis ("HorizontalRight") * Time.fixedDeltaTime * (squaredMovement ? Mathf.Abs (Input.GetAxis ("HorizontalRight")) : 1));
}
if (Mathf.Abs (Input.GetAxis ("TwistRight")) > joyDeadZone) {
transform.Rotate (0, speedAngular * Input.GetAxis ("TwistRight") * Time.fixedDeltaTime * (squaredMovement ? Mathf.Abs (Input.GetAxis ("TwistRight")) : 1), 0);
}
}
float linearVelocity = Mathf.Round (((transform.position - lastLinearPosition) / Time.fixedDeltaTime).magnitude * 100f) / 100f;
lastLinearPosition = transform.position;
float angularVelocity = Mathf.Round ((transform.eulerAngles.y - lastAngularPosition) / Time.fixedDeltaTime * Mathf.Deg2Rad * 100f) / 100f;
lastAngularPosition = transform.eulerAngles.y;
uiController.UpdateVelocities (linearVelocity, angularVelocity);
}
}
void OnCollisionExit(Collision col){
rigidbody.isKinematic = true;
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
rigidbody.isKinematic = false;
}
public enum DriveModes{
Tank,
Mecanum
}
public void setDriveMode(){
int index = PauseCanvas.transform.GetChild (1).GetComponent<Dropdown> ().value;
if (index == 0) {
driveMode = DriveModes.Tank;
} else if (index == 1) {
driveMode = DriveModes.Mecanum;
}
uiController.UpdateDriveMode (index == 0 ? "Tank" : "Mecanum");
}
}
When GameObject has Rigidbody and Collider and you want collision, do not move the Object by transform.Translate or rotate it with transform.Rotate or transform.RotateAround. When you do this there will be no collision.
The right way to move GameObject with a Rigidbody and Collision is with the Rigidbody.MovePosition function.
The correct way to rotate it is to use the Rigidbody.MoveRotation function.
Example of Rigidbody.MovePosition:
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);
Example of Rigidbody.MoveRotation:
Quaternion deltaRotation = Quaternion.Euler(eulerAngleVelocity * Time.deltaTime);
rb.MoveRotation(rb.rotation * deltaRotation);
You need to replace transform.Translate, transform.Rotate and transform.RotateAround with these.
Related
I'm writing some code to create a Minecraft Quake like game but I have an issue with the jump mecanic. When I'm stuck to a wall the player jump to high (see the video).
I use a Rigidbody for the physics and I modify that velocity to move the player. There is a Physic Material on the player's Collider with no consideration for friction or bouncing.
If you have ideas to fix the bug or an alternative to work around the problem, I'm interested.
How it looks like
Here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum PlayerMovementState {
Sneak,
Walk,
Run
}
public class PlayerControls : MonoBehaviour
{
Rigidbody rb;
Vector3 velocity, desiredVelocity;
PlayerMovementState moveState;
float groundDistance;
[SerializeField]
bool forwardAir, backAir, rightAir, leftAir;
[SerializeField]
LayerMask groundLayer;
[SerializeField]
bool onGround;
bool desiredJump;
float jumpHeight = 1.0f;
private void Awake() {
rb = GetComponent<Rigidbody>();
moveState = PlayerMovementState.Walk;
groundDistance = GetComponentInChildren<Collider>().bounds.extents.y;
}
private void Update() {
Vector2 playerInputs = Vector2.ClampMagnitude(
new Vector2(
Input.GetAxis("Horizontal"),
Input.GetAxis("Vertical")
), 1.0f
);
if (Input.GetKey(KeyCode.LeftShift)) moveState = PlayerMovementState.Sneak;
else if (Input.GetKey(KeyCode.LeftControl)) moveState = PlayerMovementState.Run;
else moveState = PlayerMovementState.Walk;
float speed = moveState == PlayerMovementState.Run ? 10f : (
moveState == PlayerMovementState.Sneak ? 2f : 5f
);
RaycastGround();
onGround = !forwardAir && !backAir && !rightAir && !leftAir;
if (Input.GetButtonDown("Jump")) desiredJump = true;
if (moveState == PlayerMovementState.Sneak)
{
if (forwardAir && playerInputs.y > 0) playerInputs.y = 0f;
if (backAir && playerInputs.y < 0) playerInputs.y = 0f;
if (rightAir && playerInputs.x > 0) playerInputs.x = 0f;
if (leftAir && playerInputs.x < 0) playerInputs.x = 0f;
}
desiredVelocity =
(transform.forward * playerInputs.y + transform.right * playerInputs.x) * speed;
}
private void FixedUpdate() {
velocity = rb.velocity;
float acceleration = 10;
velocity.x = Mathf.MoveTowards(velocity.x, desiredVelocity.x, acceleration);
velocity.z = Mathf.MoveTowards(velocity.z, desiredVelocity.z, acceleration);
if (desiredJump && onGround)
{
desiredJump = false;
float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight);
velocity.y += jumpSpeed;
}
rb.velocity = velocity;
desiredJump = false;
}
void RaycastGround()
{
forwardAir = !(Physics.Raycast(
transform.position + Vector3.forward * 0.1f,
-Vector3.up,
groundDistance + 0.1f,
groundLayer
));
backAir = !(Physics.Raycast(
transform.position - Vector3.forward * 0.1f,
-Vector3.up,
groundDistance + 0.1f,
groundLayer
));
rightAir = !(Physics.Raycast(
transform.position + Vector3.right * 0.1f,
-Vector3.up,
groundDistance + 0.1f,
groundLayer
));
leftAir = !(Physics.Raycast(
transform.position - Vector3.right * 0.1f,
-Vector3.up,
groundDistance + 0.1f,
groundLayer
));
}
}
Very likely the problem is that the script thinks it's still grounded while it is jumping upwards along the wall.
Depending on what feeling you want, either fix the raycasts such that they only trigger when you are standing directly on top of an object, or you check if the y part of your velocity is <= 0 for your onGround variable.
I did not find a solution to my problem but I found a workaround anyway.
By detecting the walls around the player, I prevent him from moving in the direction of the wall which prevents him from being stuck on it and having this bug when he jumps.
(It means that my problem is not resolved and that I am still looking for a solution)
Video
...
float wallDistance;
...
[SerializeField]
bool forwardWall, backWall, rightWall, leftWall;
...
SpherecastWall();
...
if (forwardWall && playerInputs.y > 0) playerInputs.y = 0f;
if (backWall && playerInputs.y < 0) playerInputs.y = 0f;
if (rightWall && playerInputs.x > 0) playerInputs.x = 0f;
if (leftWall && playerInputs.x < 0) playerInputs.x = 0f;
void SpherecastWall() {
forwardWall = (Physics.SphereCast(
new Ray(transform.position, Vector3.forward),
wallDistance,
.2f,
groundLayer
));
backWall = (Physics.SphereCast(
new Ray(transform.position, -Vector3.forward),
wallDistance,
.2f,
groundLayer
));
rightWall = (Physics.SphereCast(
new Ray(transform.position, Vector3.right),
wallDistance,
.2f,
groundLayer
));
leftWall = (Physics.SphereCast(
new Ray(transform.position, -Vector3.right),
wallDistance,
.2f,
groundLayer
));
}
I think that's because the spheres center gets over the corner of the wall. So when you apply a force the sphere will be pushed over it.
Maybe you could replace the sphere collider of your player with a capsule or a square collider.
My question might sound confusing but I want to make my code so when my character doesn't move, he looks directly towards the closest enemy. The problem is that when I stop moving my character, it only does the command once and doesn't redirect to the enemy's new position. I tested out many things like making it so when i move, it looks at the enemy and it seems to work but whenever I set it to not move, it doesn't work.
using System.Collections.Generic;
using UnityEngine;
namespace work.working.worked
{
public class Movement : MonoBehaviour
{
public float moveSpeed;
public float rotationSpeed;
public static bool ismoving;
public Rigidbody2D rb;
Vector2 movement;
// Update is called once per frame
void FixedUpdate()
{
rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
}
void Update()
{
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
if (Input.anyKey)
{
ismoving = true;
}
else
{
ismoving = false;
}
if (ismoving == true)
{
Vector2 direction = new Vector2(movement.x, movement.y);
direction.Normalize();
if (direction != Vector2.zero)
{
Quaternion toRotation = Quaternion.LookRotation(Vector3.forward, direction);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
}
}
if (ismoving == false)
{
Vector3 direction = FindClosest.closestEnemy.position - transform.position;
direction.Normalize();
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg - 85f;
rb.rotation = angle;
}
}
}
}
Instead of Input.anyKey you could check that the magnitude of movement vector to see if character is moving.
ismoving = movement.magnitude > 0.0f;
using System.Collections.Generic;
using UnityEngine;
namespace work.working.worked
{
public class Movement : MonoBehaviour
{
public float moveSpeed;
public float rotationSpeed;
public bool ismoving;
public Rigidbody2D rb;
Vector2 movement;
void FixedUpdate()
{
rb.MovePosition(rb.position + movement * moveSpeed * Time.fixedDeltaTime);
}
void Update()
{
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
ismoving = movement.magnitude > 0.0f;
if (ismoving)
{
Vector2 direction = new Vector2(movement.x, movement.y);
direction.Normalize();
if (direction != Vector2.zero)
{
Quaternion toRotation = Quaternion.LookRotation(Vector3.forward, direction);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
}
}
else
{
Vector3 direction = FindClosest.closestEnemy.position - transform.position;
direction.Normalize();
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg - 85f;
rb.rotation = angle;
}
}
}
Also don't make ismoving variable static or it gets set by every active Movement component in loaded scenes which can lead to unexpected behavior down the line. If its public, you can see it's state in Unity inspector by selecting the GameObject with Movement component.
when I jump up to an object that is a cube I reach the step offset and it jitters for a little bit before falling to the ground again. I can remove the step offset altogether but that's not what I want as my game is baste around parkour. when I was making this I was following a Brackeys tutorial on YouTube. Brackeys tutorial. can anyone help me out? ,first person object , the object causing most problems, objects in scene
using System.collections;
using System.Collections.Generic;
using UnityEngine;
public class keymovement : MonoBehaviour
{
public CharacterController controller;
public float speed = 12f;
public float gravity = -10f;
public Transform groundcheck;
public float groundDistance = 0.5f;
public LayerMask groundMask;
public float jumpheight = 3f;
Vector3 velocity;
bool isgrounded;
void Update()
{
isgrounded = Physics.CheckSphere(groundcheck.position, groundDistance, groundMask);
if (isgrounded && velocity.y < 0)
{
velocity.y = -2;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
if (Input.GetButton("jump") && isgrounded)
{
velocity.y = Mathf.Sqrt(jumpheight * -2f * gravity);
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
}
}
Basically, the Character controller is fighting with the velocity of the movement script because of how it checks for slopes and steps.
An easy fix is to set the controller's slopeLimit to 90f while you are jumping:
void Update()
{
isgrounded = Physics.CheckSphere(groundcheck.position, groundDistance, groundMask);
if (isgrounded && velocity.y < 0)
{
velocity.y = -2;
controller.slopeLimit = 45f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
if (Input.GetButton("Jump") && isgrounded)
{
velocity.y = Mathf.Sqrt(jumpheight * -2f * gravity);
controller.slopeLimit = 90f;
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
I have began developing an fps script and followed a tutorial to help with the script. Everything works great now but the only issue is any objects in the view seem to stutter whenever the player is moving and rotating at the same time.
The script for the player's movement and camera rotation is below.
Can anybody point me in the right direction?
Thank you.
{
public Transform cam;
public Rigidbody rb;
public float camRotationSpeed = 5f;
public float camMinimumY = -60f;
public float camMaximumY = 75f;
public float rotationSmoothSpeed = 10f;
public float walkSpeed = 9f;
public float runSpeed = 14f;
public float maxSpeed = 20f;
public float jumpPower = 30f;
public float extraGravity = 45;
float bodyRotationX;
float camRotationY;
Vector3 directionIntentX;
Vector3 directionIntentY;
float speed;
public bool grounded;
void Update()
{
LookRotation();
Movement();
ExtraGravity();
GroundCheck();
if(grounded && Input.GetButtonDown("Jump"))
{
Jump();
}
}
void LookRotation()
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
bodyRotationX += Input.GetAxis("Mouse X") * camRotationSpeed;
camRotationY += Input.GetAxis("Mouse Y") * camRotationSpeed;
camRotationY = Mathf.Clamp(camRotationY, camMinimumY, camMaximumY);
Quaternion camTargetRotation = Quaternion.Euler(-camRotationY, 0, 0);
Quaternion bodyTargetRotation = Quaternion.Euler(0, bodyRotationX, 0);
transform.rotation = Quaternion.Lerp(transform.rotation, bodyTargetRotation, Time.deltaTime * rotationSmoothSpeed);
cam.localRotation = Quaternion.Lerp(cam.localRotation, camTargetRotation, Time.deltaTime * rotationSmoothSpeed);
}
void Movement()
{
directionIntentX = cam.right;
directionIntentX.y = 0;
directionIntentX.Normalize();
directionIntentY = cam.forward;
directionIntentY.y = 0;
directionIntentY.Normalize();
rb.velocity = directionIntentY * Input.GetAxis("Vertical") * speed + directionIntentX * Input.GetAxis("Horizontal") * speed + Vector3.up * rb.velocity.y;
rb.velocity = Vector3.ClampMagnitude(rb.velocity, maxSpeed);
if (Input.GetKey(KeyCode.LeftShift))
{
speed = runSpeed;
}
if (!Input.GetKey(KeyCode.LeftShift))
{
speed = walkSpeed;
}
}
void ExtraGravity()
{
rb.AddForce(Vector3.down * extraGravity);
}
void GroundCheck()
{
RaycastHit groundHit;
grounded = Physics.Raycast(transform.position, -transform.up, out groundHit, 1.25f);
}
void Jump()
{
rb.AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
}
}
This probably happens because the frame needs to update the camera rotation and the player position at the same time. i would suggest doing the rotation of the camera in fixed update. because this will run at the end of each frame.
like this:
void FixedUpdate()
{
LookRotation();
}
another thing i would suggest is to move the player while the groundcheck is being ran, because if you do it seperately i can see a bug coming up when the player moves faster then the code can run or something. so a tip would be or to run it inside the movement method, or call the method while you are calling the movement method.
this:
void Movement()
{
directionIntentX = cam.right;
directionIntentX.y = 0;
directionIntentX.Normalize();
directionIntentY = cam.forward;
directionIntentY.y = 0;
directionIntentY.Normalize();
rb.velocity = directionIntentY * Input.GetAxis("Vertical") * speed + directionIntentX * Input.GetAxis("Horizontal") * speed + Vector3.up * rb.velocity.y;
rb.velocity = Vector3.ClampMagnitude(rb.velocity, maxSpeed);
if (Input.GetKey(KeyCode.LeftShift))
{
speed = runSpeed;
}
if (!Input.GetKey(KeyCode.LeftShift))
{
speed = walkSpeed;
}
GroundCheck();
}
or litteraly copy and paste the code within your movement method.
void Update ()
{
Vector3 input = new Vector3 (0, 0, 1);
Vector3 direction = input.normalized;
Vector3 velocity = direction * speed;
Vector3 moveAmount = velocity * Time.deltaTime;
transform.position += moveAmount;
if(Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
player.AddForce (Vector3.up * jumpForce, ForceMode.Impulse);
}
}
Since Time.deltaTime varies moveAmount also varies for every jump making the distance of the jump vary slightly.
The ball is to jump between blocks separated by a fixed gap and therefore the above behaviour causes problems.
Is there any way I can fix this and make fixed length jumps?
You can use CharacterController for this. Make sure you have a CharacterController and Collider attached to your gameobject. Also if you have a rigibody attached to your game object it may cause it to behave unexpectedly so you might have to contraint it.
public CharacterController controller;
private float verticalVelocity;
private float gravity = 25.0f;
private float jumpForce = 15.0f;
void Awake () {
controller = GetComponent<CharacterController>();
}
void Update () {
if( controller == null )
return;
if( controller.isGrounded)
{
verticalVelocity = -gravity * Time.deltaTime;
if( Input.GetKeyDown(KeyCode.Space) )
{
verticalVelocity = jumpForce;
}
}
else
{
verticalVelocity -= gravity * Time.deltaTime;
}
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
Vector3 moveVector = Vector3.zero;
moveVector.x = moveHorizontal * 5.0f;
moveVector.y = verticalVelocity;
moveVector.z = moveVertical * 5.0f;
controller.Move(moveVector * Time.deltaTime);
}
Check this tutorial for reference: https://www.youtube.com/watch?v=miMCu5796KM