I'm new to programming in C# in Unity and there is some problems when moving in the z-axis. The problem is that I continue to move when I let go of the up button. However, when I move in the x-axis it is fine, as letting go of the button will stop the player. The code is below:
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour {
public Vector3 motion = new Vector3();
private CharacterController controller;
private bool onGround;
private float xRot, yRot;
public static float X_ROTATION = 0;
public static float Y_ROTATION = 0;
private const float lookSpeed = 2.0f;
public void Start() {
controller = GetComponent<CharacterController>();
}
public void FixedUpdate() {
if(Screen.lockCursor) {
Vector3 impulse = new Vector3();
if(Input.GetKey(KeyCode.W)) impulse.z+=1;
if(Input.GetKey(KeyCode.A)) impulse.x-=1;
if(Input.GetKey(KeyCode.S)) impulse.z-=1;
if(Input.GetKey(KeyCode.D)) impulse.x+=1;
if(impulse.sqrMagnitude > 0) {
motion += Quaternion.Euler(new Vector3(0, xRot, 0)) * impulse.normalized * 0.05f;
}
if(onGround) {
if(Input.GetKey(KeyCode.Space)) {
motion.y += 0.2f;
}
}
motion.y -= 0.015f;
Vector3 oldMotion = motion;
Vector3 oldPos = controller.transform.localPosition;
controller.Move(motion);
Vector3 newPos = controller.transform.localPosition;
motion = newPos - oldPos;
onGround = oldMotion.y < -0.0001f && motion.y >= -0.0001f;
motion.x *= 0.8f;
motion.y *= 0.8f;
}
}
public void Update() {
if(Screen.lockCursor && Input.GetKeyDown(KeyCode.Escape)) {
Screen.lockCursor = false;
}
if(!Screen.lockCursor && Input.GetMouseButtonDown(0)) {
Screen.lockCursor = true;
}
if(Screen.lockCursor) {
xRot += Input.GetAxis("Mouse X") * lookSpeed;
yRot -= Input.GetAxis("Mouse Y") * lookSpeed;
if(yRot < -90) yRot = -90;
if(yRot > 90) yRot = 90;
if(xRot < -180) xRot += 360;
if(xRot >= 180) xRot -= 360;
controller.transform.localRotation = Quaternion.Euler(new Vector3(yRot, xRot, 0));
}
Player.X_ROTATION = xRot;
}
}
The last few statments in FixedUpdate() where you have coded-
motion.x *= 0.8f;
motion.y *= 0.8f;
should also contain
motion.z *= 0.8f;
and may be u dont need-
motion.y *= 0.8f;
As Gkills said, you should fix the "depletion" of motion.y for motion.z at the end.
Additionally, you should probably want to swap
motion += Quaternion.Euler(new Vector3(0, xRot, 0)) * impulse.normalized * 0.05f;
for
motion += transform.rotation * impulse.normalized * 0.05f;
Then zero out the impulse.y component to prevent the player flying.
Finally you might want to use Time.deltaTime in your Update (so mouse look is frame rate independent)
Related
Did someone know how to make the player not turning when i press left or right button ? I'm using unity script refrence for moving.
this video showing my problem: https://drive.google.com/file/d/1m-_Q3j0kt5bMfxiiqqjGAp0wtW7x8gno/view?usp=sharing
public class Move : MonoBehaviour
{
private CharacterController controller;
private Vector3 playerVelocity;
private bool groundedPlayer;
private float playerSpeed = 2.0f;
private float jumpHeight = 1.0f;
private float gravityValue = -9.81f;
private void Start()
{
controller = gameObject.AddComponent<CharacterController>();
}
void Update()
{
groundedPlayer = controller.isGrounded;
if (groundedPlayer && playerVelocity.y < 0)
{
playerVelocity.y = 0f;
}
Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
controller.Move(move * Time.deltaTime * playerSpeed);
if (move != Vector3.zero)
{
gameObject.transform.forward = move;
}
// Changes the height position of the player..
if (Input.GetButtonDown("Jump") && groundedPlayer)
{
playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
}
playerVelocity.y += gravityValue * Time.deltaTime;
controller.Move(playerVelocity * Time.deltaTime);
}
}
Remove this line:
if (move != Vector3.zero)
{
gameObject.transform.forward = move;
}
I'll start off by saying I'm very new to Unity and C# and I'm attempting my first project after following a series of youtube tutorials. I manually made physics for my player (for acceleration, airborne velocity and drag, gravity, etc), but when I jump moving forward (or any other direction for that matter), when I rotate my character (through my mouse movement) my airborne movement changes relative to my rotation rather than remaining constant (I guess since it's moving in local space?). I'd like it so my airborne velocity gets influenced by my grounded velocity (that which is influenced by WASD), but doesn't move relative to my rotation, and i keep my movement direction no matter where I look.
I've been pouring overt my code for a while but I can't figure it out.
My player is called PlayerColliderParent, it has a Character Controller attached to it, along with my script PlayerColliderParentScript.cs (which handles all physics and movement for the player).
TLDR or if that was too confusing: When my player is airborne and moving, the direction of movement changes along with the player's rotation (where I look). I'd rather the direction remained constant regardless of the rotation.
Any help is appreciated as I really want to fix this.
Here is a video of the problem occuring: https://www.youtube.com/watch?v=wpEMv059cZs
And here is PlayerColliderParentScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerColliderParentScript : MonoBehaviour
{
public Transform cameraRotation;
public Vector3 directionalInput;
public float accelerationAmount = 0.5f;
public float maxSpeed = 6f;
public float maxMoveSpeed =6f;
public Vector3 directionalSpeed;
public Vector3 airDirectionalSpeed;
public float terminalVelocity = 30f;
public float gravityStrength = 1f;
public float currentYvel;
public float airDrag;
public float maxAirSpeed;
public float jumpStrength = 1f;
Vector3 gravityController;
//Rigidbody playerBody = new Rigidbody();
CharacterController controller = new CharacterController();
//Rigidbody playerBody = new Rigidbody();
// Start is called before the first frame update
void Start()
{
//playerBody = GetComponent<Rigidbody>();
controller = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update()
{
//set acceleration factor
float accelerationFactor = (accelerationAmount * 200) * Time.deltaTime;
//rotate horizontally with camera
transform.localEulerAngles = new Vector3(0, cameraRotation.localEulerAngles.y, 0);
//JUMP or press against ground
if (controller.isGrounded == true)
{
if (Input.GetKey(KeyCode.Space))
{
currentYvel = jumpStrength;
}
else
{
currentYvel = -2f /** Time.deltaTime * 500*/;
}
}
//accelerate while movement key is held
//z axis
if (Input.GetKey(KeyCode.W))
{
directionalInput.z = 1;
if (directionalSpeed.z < maxSpeed)
{
directionalSpeed.z += accelerationFactor;
}
}
else if (Input.GetKey(KeyCode.S))
{
directionalInput.z = -1;
if (directionalSpeed.z < maxSpeed)
{
directionalSpeed.z += accelerationFactor;
}
}
else if (directionalSpeed.z > 0)
{
if (controller.isGrounded == true)
{
directionalInput.z = 0;
directionalSpeed.z -= accelerationFactor;
}
else
{
directionalSpeed.z -= airDrag * Time.deltaTime * 100;
}
}
else if (directionalSpeed.z < 0)
{
if (controller.isGrounded == true)
{
directionalInput.z = 0;
directionalSpeed.z += accelerationFactor;
}
else
{
directionalSpeed.z += airDrag * Time.deltaTime * 100;
}
}
else
{
directionalInput.z = 0;
}
//x axis
if (Input.GetKey(KeyCode.D))
{
directionalInput.x = 1;
if (directionalSpeed.x < maxSpeed)
{
directionalSpeed.x += accelerationFactor;
}
}
else if (Input.GetKey(KeyCode.A))
{
directionalInput.x = -1;
if (directionalSpeed.x < maxSpeed)
{
directionalSpeed.x += accelerationFactor;
}
}
else if (directionalSpeed.x > 0)
{
if (controller.isGrounded == true)
{
directionalInput.x = 0;
directionalSpeed.x -= accelerationFactor;
}
else
{
directionalSpeed.x -= airDrag * Time.deltaTime * 100;
}
}
else if (directionalSpeed.x < 0)
{
if (controller.isGrounded == true)
{
directionalInput.x = 0;
directionalSpeed.x += accelerationFactor;
}
else
{
directionalSpeed.x += airDrag * Time.deltaTime * 100;
}
}
else
{
directionalInput.x = 0;
}
//accelerate downwards if not grounded
if (controller.isGrounded == false)
{
if (currentYvel > -terminalVelocity)
{
currentYvel -= gravityStrength * Time.deltaTime;
}
}
//Reset gravity vecctor
gravityController = Vector3.zero;
//calculate movement velocity
Vector3 direction = directionalInput.normalized;
float velocityX = direction.x * Mathf.Round(directionalSpeed.x);
float velocityY = (currentYvel / 2) * Time.deltaTime;
float velocityZ = direction.z * Mathf.Round(directionalSpeed.z);
Vector3 velocity = new Vector3(velocityX * Time.deltaTime, velocityY, velocityZ * Time.deltaTime);
//check for sprint key
if (Input.GetKey(KeyCode.LeftShift))
{
velocity *= 1.5f;
}
//move player according to velocity
controller.Move(transform.TransformDirection(velocity));
//set airspeed the same as character velocity
airDirectionalSpeed.x = velocityX * Time.deltaTime;
airDirectionalSpeed.y = 0;
airDirectionalSpeed.z = velocityZ * Time.deltaTime;
if (airDirectionalSpeed.x > 0)
{
airDirectionalSpeed.x -= airDrag * Time.deltaTime * 100;
}
if (airDirectionalSpeed.z > 0)
{
airDirectionalSpeed.z -= airDrag * Time.deltaTime * 100;
}
airDirectionalSpeed = transform.TransformDirection(airDirectionalSpeed);
Vector3 airVelocity = new Vector3(airDirectionalSpeed.x, 0, airDirectionalSpeed.z);
Vector3 airMoveAmountX = new Vector3((airVelocity.x / 3), 0, 0);
Vector3 airMoveAmountZ = new Vector3(0, 0, (airVelocity.z / 3));
//move player according to airspeed
if (controller.isGrounded == false)
{
if (airDirectionalSpeed.x < maxAirSpeed | airDirectionalSpeed.x > -maxAirSpeed)
{
controller.Move(airMoveAmountX);
}
if (airDirectionalSpeed.z < maxAirSpeed | airDirectionalSpeed.z > -maxAirSpeed)
{
controller.Move(airMoveAmountZ);
}
}
}
}
This part smells:
void Update()
{
// (...)
if (Input.GetKey(KeyCode.D))
{
directionalInput.x = 1;
if (directionalSpeed.x < maxSpeed)
{
directionalSpeed.x += accelerationFactor;
}
}
//(...)
}
If your are not grounded, you shouldn't be responding to directional inputs and accelerating in horizontal direction. Same applies for the corresponding logic for the other 3 directional keys.
You can try to add a condition to only execute this logic if the player is grounded.
As seen in the video here: https://i.gyazo.com/ad45ef9e231fd2f9ec6d4cf76889aece.mp4
My code:
MouseLook.cs:
using UnityEngine;
using System.Collections;
public class MouseLook : MonoBehaviour
{
public enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 }
public RotationAxes axes = RotationAxes.MouseXAndY;
public float sensitivityX = 3F;
public float sensitivityY = 3F;
public Camera playerCamera;
public float minimumX = -360F;
public float maximumX = 360F;
public float minimumY = -60F;
public float maximumY = 60F;
private float rotationX = 0F;
private float rotationY = 0F;
private Quaternion originalRotation;
void Update()
{
if (axes == RotationAxes.MouseXAndY)
{
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationX = ClampAngle(rotationX, minimumX, maximumX);
rotationY = ClampAngle(rotationY, minimumY, maximumY);
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, -Vector3.right);
transform.localRotation = originalRotation * xQuaternion * yQuaternion;
}
if (axes == RotationAxes.MouseX)
{
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationX = ClampAngle(rotationX, minimumX, maximumX);
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
transform.localRotation = originalRotation * xQuaternion;
}
if (axes == RotationAxes.MouseY || playerCamera != null)
{
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationY = ClampAngle(rotationY, minimumY, maximumY);
Quaternion yQuaternion = Quaternion.AngleAxis(-rotationY, Vector3.right);
if (playerCamera != null)
{
playerCamera.transform.localRotation = originalRotation * yQuaternion;
}
else
{
transform.localRotation = originalRotation * yQuaternion;
}
}
}
void Start()
{
/*
if (gameObject.GetComponent<Rigidbody>())
{
gameObject.GetComponent<Rigidbody>().freezeRotation = true;
}
*/
originalRotation = transform.localRotation;
}
public static float ClampAngle(float angle, float min, float max)
{
if (angle < -360F)
{
angle += 360F;
}
if (angle > 360F)
{
angle -= 360F;
}
return Mathf.Clamp(angle, min, max);
}
}
FirstPersonController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Cursor = UnityEngine.Cursor;
public class FirstPersonController : MonoBehaviour
{
private float speed = 5;
private float jumpPower = 4;
Rigidbody rb;
CapsuleCollider col;
public GameObject crossHair;
bool isActive;
float HorizontalInput;
float VerticalInput;
void Start()
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
rb = GetComponent<Rigidbody>();
col = GetComponent<CapsuleCollider>();
crossHair = GameObject.FindWithTag("CrossHair");
}
void Update()
{
HorizontalInput = Input.GetAxisRaw("Horizontal");
VerticalInput = Input.GetAxisRaw("Vertical");
if (Input.GetKeyDown("escape"))
{
Cursor.lockState = CursorLockMode.None;
}
if (Input.GetButtonDown("Sprint"))
{
speed = 15;
}
if (Input.GetButtonUp("Sprint"))
{
speed = 5;
}
if (Input.GetKeyDown(KeyCode.H))
{
isActive = !isActive;
}
if (isActive)
{
crossHair.SetActive(true);
}
else
{
crossHair.SetActive(false);
}
}
void FixedUpdate()
{
Vector3 xMovement = transform.right * speed * HorizontalInput * Time.deltaTime;
Vector3 zMovement = transform.forward * speed * VerticalInput * Time.deltaTime;
rb.velocity = new Vector3(HorizontalInput, 0, VerticalInput) * speed;
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
}
}
private bool isGrounded()
{
return Physics.Raycast(transform.position, Vector3.down, col.bounds.extents.y + 0.1f);
}
}
Is there anything wrong I am doing in this code, if so how do I fix it?
Entire project can be downloaded here: https://github.com/Some-T/FirstPersonController-CSharp
Project has relevant colliders and rigidbodies set up!
Someone has advised me to use shapecast, but I believe that may incorrect? I can't see how that would work as my player does not have character controller component added to it?
Overall how do I stop my first person character controller going through the wall like in the initial video specified above?
Upon further research I have discovered the following:
The answer is to use:
https://docs.unity3d.com/ScriptReference/Rigidbody.AddForce.html as a quick fix.
But definitively for flawlessness use:
https://docs.unity3d.com/ScriptReference/Rigidbody-velocity.html
As to how in C# I am not so sure, but here is a screen shot I did in bolt asset.
Currently I have movmement working with velocity but it does not work properly, not sure as to why? So overall my question now is how do I get movement working using velocity? I added a line in FirstPersonController.cs that moves the character using velocity of rb.velocity = new Vector3(HorizontalInput, 0, VerticalInput) * speed; so my only question and issue now is my player does not move in the direction the camera on my player is facing so I am not sure how to fix this specific thing overall?
In your project code is different from one that you provided here.
Try to enable rigidbody`s rotation constraint - freeze X and Z rotation, leave only Y. When you rotate your capsule collider (as it works in your project), it can "climb" on a wall.
Do the isGrounded check when you move, and lock movement, if not grounded.
Try to increase Collider.contactOffset
If above does not helps, try to use Rigidbody.velocity instead of Rigidbody.MovePosition.
You can also reduce Rigidbody.drag
In general, pretty good technique is to use NavMesh for movement - in this way, you able to explicitly lock player from movement outside of navmesh surface. But, of course, it doesn`t fit to many gameplays.
Hope, it helps.
upd
You move your player like this:
void FixedUpdate()
{
Vector3 xMovement = transform.right * speed * HorizontalInput * Time.deltaTime;
Vector3 zMovement = transform.forward * speed * VerticalInput * Time.deltaTime;
}
Note, that you not even apply it to rigidbody;
But you get your input this way:
float HorizontalInput = Input.GetAxis("Horizontal");
float VerticalInput = Input.GetAxis("Vertical");
in Update, so input just stays inside Update, and does not applied at all. You declared your variables twice, once on class top, another in Update().
I wrote the code below to change an object's scale (X axis) with mouse swipe, it works but it's not smooth, how can I smooth it?
Script:
Vector3 newScale;
private float _previousSwipePosition;
private float newPosition;
if (Input.GetMouseButton(0))
{
_previousSwipePosition = Input.mousePosition.x;
if (newPosition != _previousSwipePosition)
{
if (newPosition - _previousSwipePosition < -2)
{
if (transform.localScale.x <= 1.4f)
{
newScale = transform.localScale;
newScale.x += 0.06f;
transform.localScale = newScale;
}
}
else if (newPosition - _previousSwipePosition > 2)
{
if (transform.localScale.x >= 0.2f)
{
newScale = transform.localScale;
newScale.x -= 0.06f;
transform.localScale = newScale;
}
}
}
newPosition = Input.mousePosition.x;
}
You can use Input.GetAxis("Mouse X") to get smoothed scale of how much the mouse has moved in the last frame. Multiply that by a speed parameter.
Get the power of 2 by that product to get how much to change the current scale. Then, change the scale and clamp it:
public float scaleSpeed = 1f;
// ...
// ignore first frame mouse is pressed
if (Input.GetMouseButton(0) && !Input.GetMouseButtonDown(0))
{
float scaleFactor = Mathf.Pow(2f, Input.GetAxis("Mouse X")
* scaleSpeed);
float newX = Mathf.Clamp(transform.localScale.x * scaleFactor, 0.2f, 1.4f);
transform.localScale = new Vector3(
newX,
transform.localScale.y,
transform.localScale.z);
}
Use Time.deltaTime to smooth
Vector3 newScale;
private float _previousSwipePosition;
private float newPosition;
private float speed = 6f;
private void Update()
{
if (Input.GetKey(KeyCode.A))
{
if (transform.localScale.x <= 1.4f)
{
newScale = transform.localScale;
newScale.x += speed * Time.deltaTime;
transform.localScale = newScale;
}
}
if (Input.GetKey(KeyCode.B))
{
if (transform.localScale.x >= 0.2f)
{
newScale = transform.localScale;
newScale.x -= speed * Time.deltaTime;
transform.localScale = newScale;
}
}
}
I've got a camera script that I'm working on and I just cannot get the camera pan to work at all. It's recognising the input, but it's not moving the camera at all.
It was working at one point but it reset the camera upon zooming or rotating, now the zooming and rotating work perfectly. This issue was due to the function PositionRotation which you can find at the bottom.
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
[AddComponentMenu("Camera-Control/Mouse drag Orbit with zoom")]
public class DragMouseOrbit : MonoBehaviour
{
public Transform target;
public float distance = 5.0f;
public float xSpeed = 120.0f;
public float ySpeed = 120.0f;
public float yMinLimit = -20f;
public float yMaxLimit = 80f;
public float distanceMin = .5f;
public float distanceMax = 15f;
public float smoothTime = 2f;
public float zoomFactor = 5f;
public float panFactor = 10f;
private Transform m_Transform;
float rotationYAxis = 0.0f;
float rotationXAxis = 0.0f;
float velocityX = 0.0f;
float velocityY = 0.0f;
// Use this for initialization
void Start()
{
Vector3 angles = transform.eulerAngles;
rotationYAxis = angles.y;
rotationXAxis = angles.x;
// Make the rigid body not change rotation
if (GetComponent<Rigidbody>())
{
GetComponent<Rigidbody>().freezeRotation = true;
}
}
void Update()
{
if (!EventSystem.current.IsPointerOverGameObject())
{
if(Input.GetMouseButton(0))
{
CameraRotate();
}
if(Input.GetMouseButton(1))
{
CameraPan();
}
if(Input.GetAxis("Mouse ScrollWheel") <= 0)
{
CameraZoom();
}
if (Input.GetAxis("Mouse ScrollWheel") >= 0)
{
CameraZoom();
}
}
}
public static float ClampAngle(float angle, float min, float max)
{
if (angle < -360F)
angle += 360F;
if (angle > 360F)
angle -= 360F;
return Mathf.Clamp(angle, min, max);
}
public void CameraRotate()
{
if (target)
{
velocityX += xSpeed * Input.GetAxis("Mouse X") * distance * 0.02f;
velocityY += ySpeed * Input.GetAxis("Mouse Y") * 0.02f;
}
rotationYAxis += velocityX;
rotationXAxis -= velocityY;
rotationXAxis = ClampAngle(rotationXAxis, yMinLimit, yMaxLimit);
PositionRotation();
}
public void CameraPan()
{
if (!EventSystem.current.IsPointerOverGameObject())
{
Debug.Log("right click has been pressed dumbfuck");
//transform.rotation = transform.rotation;
transform.Translate(Vector3.right * -Input.GetAxis("Mouse X") * panFactor);
transform.Translate(Vector3.forward * -Input.GetAxis("Mouse Y") * panFactor);
}
}
public void CameraZoom()
{
if (!EventSystem.current.IsPointerOverGameObject())
{
//transform.Translate(Vector3.forward * +Input.GetAxis("Mouse ScrollWheel") * zoomFactor);
PositionRotation();
distance = Mathf.Clamp(distance - Input.GetAxis("Mouse ScrollWheel") * zoomFactor, distanceMin, distanceMax);
RaycastHit hit;
if (Physics.Linecast(target.position, transform.position, out hit))
{
distance -= hit.distance;
}
}
}
void PositionRotation()
{
Quaternion fromRotation = Quaternion.Euler(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, 0);
Quaternion toRotation = Quaternion.Euler(rotationXAxis, rotationYAxis, 0);
Quaternion rotation = toRotation;
Vector3 negDistance = new Vector3(0.0f, 0.0f, -distance);
Vector3 position = rotation * negDistance + target.position;
transform.rotation = rotation;
transform.position = position;
velocityX = Mathf.Lerp(velocityX, 0, Time.deltaTime * smoothTime);
velocityY = Mathf.Lerp(velocityY, 0, Time.deltaTime * smoothTime);
}
}
The problem is within your Update function. It calls CameraZoom all the time because you set
Input.GetAxis("Mouse ScrollWheel") <= 0
And CameraZoom calls PositionRotation, where you reset the positon of the camera to the original position.
Try setting it to
if (Input.GetAxis("Mouse ScrollWheel") <= -0.1)
{
CameraZoom();
}
if (Input.GetAxis("Mouse ScrollWheel") >= 0.1)
{
CameraZoom();
}
That should do the trick.
Cheers
Tobi