I'm developing my first game so am a bit new to Unity. So my problem is am trying to rotate the Fps Controller to face a target by the push of a button. I managed to rotate the camera alone by attaching this code to it but only the camera rotates so it completely messes up the walking
public class PlayerRotate : MonoBehaviour {
public Transform target;
void Update(){
if(Input.GetKeyDown("r")){
print ("Rotate");
transform.LookAt(target.position);
}
}
}
I also tried attaching the same script to the Fps Controller but nothing happens. Anyone's help would be greatly appreciated, Thanks.
FPSCaracterController rotation is driven by the MouseLook.cs script that incorporates its asset.
If you open it you will see the function that controls the rotation:
public void LookRotation(Transform character, Transform camera)
This method modifies the value of the rotation directly with the value of the mouse without any intermediate operation. So if you want to change the rotation of the FPController you need to modify this method (or this class)
For example, I have made this modification makes the FPController rotate 180 degrees every time the user presses the "U" key:
public void LookRotation(Transform character, Transform camera)
{
float yRot = CrossPlatformInputManager.GetAxis("Mouse X") * XSensitivity;
float xRot = CrossPlatformInputManager.GetAxis("Mouse Y") * YSensitivity;
m_CharacterTargetRot *= Quaternion.Euler(0f, yRot, 0f);
m_CameraTargetRot *= Quaternion.Euler(-xRot, 0f, 0f);
if (ORPargaModification)
{
if(Input.GetKeyDown(KeyCode.U))
{
Debug.Log("U pressed");
m_CharacterTargetRot *= Quaternion.Euler(0f, 180, 0f);
}
character.localRotation = m_CharacterTargetRot;
camera.localRotation = m_CameraTargetRot;
}
else
{
if (clampVerticalRotation)
m_CameraTargetRot = ClampRotationAroundXAxis(m_CameraTargetRot);
if (smooth)
{
character.localRotation = Quaternion.Slerp(character.localRotation, m_CharacterTargetRot,
smoothTime * Time.deltaTime);
camera.localRotation = Quaternion.Slerp(camera.localRotation, m_CameraTargetRot,
smoothTime * Time.deltaTime);
}
else
{
character.localRotation = m_CharacterTargetRot;
camera.localRotation = m_CameraTargetRot;
}
}
UpdateCursorLock();
}
Related
Consider this working script, that handles a FPS camera movement:
using UnityEngine;
public class CameraHandler : MonoBehaviour {
public Transform target;
float dragSpeed = 10f;
float lookAtSensitivity = 200f;
float xRot;
Transform parentGO;
private void Start() {
parentGO = transform.parent;
}
void goToPivot(Transform pivot) {
parentGO.position = Vector3.Lerp(transform.position, pivot.position, 0.05f);
transform.rotation = Quaternion.Lerp(transform.rotation, pivot.rotation, 0.05f);
}
void resetCamRot() {
xRot = 0;
float yRot = transform.localEulerAngles.y;
parentGO.transform.eulerAngles += new Vector3(0, yRot, 0);
transform.localEulerAngles -= new Vector3(0, yRot, 0);
}
void LateUpdate() {
if (Input.GetKey(KeyCode.Mouse1)) {
float touchX = Input.GetAxis("Mouse X") * lookAtSensitivity * Time.deltaTime;
float touchY = Input.GetAxis("Mouse Y") * lookAtSensitivity * Time.deltaTime;
xRot -= touchY;
xRot = Mathf.Clamp(xRot, -90f, 90f);
transform.localRotation = Quaternion.Euler(xRot, 0f, 0f);
parentGO.transform.Rotate(Vector3.up * touchX);
}
if (Input.GetKey(KeyCode.Space)) {
goToPivot(target);
}
if (Input.GetKeyUp(KeyCode.Space)) {
resetCamRot();
}
}
}
Check how the rotations take place in different gameobjects in their respective axis, so that each of rotations are kept independent and everything works.
transform.localRotation = Quaternion.Euler(xRot, 0f, 0f); //camera GO only rotates in local x
parentGO.transform.Rotate(Vector3.up * touchX); //parent GO only rotates in global y
Problem comes when I need to "force" the camera look a certain direction without the inputs, to the FPS movement rules break, and for example the camera gameobject rotates also in the Y xis. That is why I need to call the resetCamRot() method, and traspass the local rotation from the camera object to the parent so that the situation meets the the FPS movement requirements (no local Y axis rotation).
Without calling the resetCamRot() method, when the FPS movement starts on right mouse button click, the camera abruptly changes to the direction it was facing before "forcing" it with goToPivot that sets the position and the rotation.(Just commentinf the resetCamRot method out)
Although resetCamRot() does the work it feels a bit hacky, so is there another way to set the camera to a forced rotation maintaining the local rotation of the child object (where the camera is) to 0?
I thought of decomposing the next step rotation given by Quaternion.Lerp(transform.rotation, pivot.rotation, 0.05f); in the goToPivot() method in each of their respective axis and gameObjects as its done when the rotation is set from the input, to have a clean local Y rot in he camera gameobject each step. Seems to be the over-complicated thing in this case, but was not able to figure that out.
I you wish to try the script out for the challenge just need to add a parent gameobject to the camera and the attach the target in the editor.
This will make the camera look in the direction, the parent transform look in the direction, only flattened, and finally update the internal state (xRot) in accordance with the difference between the two:
void LookTowards(Vector3 direction) {
Vector3 flattened = direction;
flattened.y = 0f;
parentGo.rotation = Quaternion.LookRotation(flattened);
transform.rotation = Quaternion.LookRotation(direction);
xRot = Vector3.SignedAngle(flattened, direction, parentGo.right);
}
I am making a racing game. When the car I made is going up on a ramp, it becomes a bit jittery. Don't think it's the camera issue as the game looks smooth on plain grounds and when going down a ramp.
This is the video of the jittery car movement on ramp:
https://youtu.be/6YC6SLsokCw
I followed this tutorial:https://www.youtube.com/watch?v=cqATTzJmFDY&list=PLllUqr_Vxwvto-j8J-Fk2XmjQhn2PcwlZ&index=2
I noticed that in this tutorial, the author's final outcome is also a bit jittery on ramps.
This is the code:
{
sphereRb.transform.parent = null;
}
private void Update()
{
speedInput = 0;
if (Input.GetAxis("Vertical") > 0)
{
GetInputForMoving(forwardAccel);
}
else if (Input.GetAxis("Vertical") < 0)
{
GetInputForMoving(reverseAccel);
}
turnInput = Input.GetAxis("Horizontal");
if (grounded)
{
Turning();
}
SetWheelsRotation();
transform.position = sphereRb.transform.position;
}
void GetInputForMoving(float directionAccel)
{
speedInput = Input.GetAxis("Vertical") * directionAccel * multiplier;
}
void Turning()
{
transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles + new Vector3(0f, turnInput * turnStrength * Time.deltaTime, 0f));
}
void SetWheelsRotation()
{
leftFrontWheel.localRotation = Quaternion.Euler(leftFrontWheel.localRotation.eulerAngles.x, (turnInput * maxWheelTurn) - 180, leftFrontWheel.localRotation.eulerAngles.z);
rightFrontWheel.localRotation = Quaternion.Euler(rightFrontWheel.localRotation.eulerAngles.x, (turnInput * maxWheelTurn), rightFrontWheel.localRotation.eulerAngles.z);
}
private void FixedUpdate()
{
grounded = false;
RaycastHit hit;
//if hits ground
if (Physics.Raycast(groundRayPoint.position, -transform.up, out hit, groundRayLength, whatisGround))
{
grounded = true;
transform.rotation = Quaternion.FromToRotation(transform.up, hit.normal) * transform.rotation;
}
if (grounded)
{
Moving();
}
else
{
Dropping();
}
}
void Moving()
{
sphereRb.drag = dragOnGround;
if (Mathf.Abs(speedInput) > 0)
{
sphereRb.AddForce(transform.forward * speedInput);
}
}
void Dropping()
{
sphereRb.drag = 0.1f;
sphereRb.AddForce(Vector3.up * -gravityForce * 100);
}
Please help I am really struggling. Sorry for my English, thanks in advance!
The car movement actually looks fine and seems to translate smoothly.
The real issue is the car rotation.
If you look at this line :
transform.rotation = Quaternion.FromToRotation(transform.up, hit.normal) * transform.rotation;
You are telling the car to align its y-axis according to the mesh normal.
However, a slope or ramp will never be perfectly curved. Polygons are shaping the slope and the normals between each "part" of the slope change abruptly : this is why the car rotation is so steep along the slope. You can see it in the Scene view by enabling the wireframe shading mode (option available just under the "Scene" tab).
What you can do is interpolate between the current car rotation and the target rotation with something like the code below :
Quaternion toRotation = Quaternion.FromToRotation(transform.up, hit.normal) * transform.rotation;
float angle = Vector3.Angle(transform.up, hit.normal);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, angle * Time.fixedDeltaTime * rotateSpeed);
You'll have to tweak the rotation speed until it matches what you feel is OK.
Quaternion.RotateTowards will smoothly interpolate between the current rotation and the target rotation at the speed of angle x rotateSpeed degrees per second. There are better ways to do all of these but I hope this will unlock you with what you currently have.
[EDIT] Changed title due to additional issues.
Whenever player moves, it draws arc to move. Whenever player jumps, camera controller zooms in first and then zoom out trying to fix its position. I want it to stay zoomed out while jumping (just realized how huge work this question could become...with "unless colliding or interfered its view with other objects"). I also want it to move, well, properly. (Just by movement without additional line of code related to camera script works perfect)
My player controller is default character controller script from Unity Documentation. My camera controller is based on youtube video. I know why it's causing trouble because I used different character controller than the tutorial used but that's because his character controller didn't work with mine. So I'm trying to fix while having default character controller.
[EDIT] After looking into the script for sometime I was able to narrow it down to one of possible culprits.
Vector3 camVel = Vector3.zero;
void MoveToTarget()
{
targetPos = target.position + position.targetPosOffset;
destination = Quaternion.Euler (orbit.xRotation, orbit.yRotation + target.eulerAngles.y, 0) * -Vector3.forward * position.distanceFromTarget;
destination += targetPos;
if (collision.colliding)
{
////from here
adjustedDestination = Quaternion.Euler (orbit.xRotation, orbit.yRotation + target.eulerAngles.y, 0) * Vector3.forward * position.adjustmentDistance;
adjustedDestination += targetPos;
if (position.smoothFollow)
{
//use smooth damp function
////to here makes weird behavior. Found out with debug.log line.
transform.position = Vector3.SmoothDamp(transform.position, adjustedDestination, ref camVel, position.smooth);
}
else
transform.position = adjustedDestination;
}
else
{
if (position.smoothFollow)
{
//use smooth damp function
transform.position = Vector3.SmoothDamp(transform.position, destination, ref camVel, position.smooth);
}
else
transform.position = destination;
}
}
target is getting reference from TPController, which is character controller below. Though I still don't understand why this is happening (It didn't happen in tutorial video). Unless...my character controller is somehow interfering with camera controller.
public Quaternion TargetRotation
{
get { return targetRotation; }
}
void Start() {
targetRotation = transform.rotation;
}
void Update() {
Movement ();
Turn ();
}
void Movement()
{
CharacterController controller = GetComponent<CharacterController>();
if (controller.isGrounded) {
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
if (Input.GetButton("Jump"))
moveDirection.y = jumpSpeed;
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
void Turn()
{
if (Mathf.Abs (Input.GetAxis("Horizontal")) > inputDelay)
{
targetRotation *= Quaternion.AngleAxis (rotateVel * Input.GetAxis("Horizontal") * Time.deltaTime, Vector3.up);
}
transform.rotation = targetRotation;
}
disabling Turn() does allow character to move without drawing arc. But then camera doesn't turn around as character turns. ///this arc movement part is solved - leaving it here for future reference in case any one tries similar method: in the section of
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
put 0 instead of Input.GetAxis("Horizontal"). Should be able to move without drawing arc.
Thanks for help in advance.
I'm working through a book and have come to an issue. it's a top down shooting game that has the player rotate with the mouse and move with the keyboard. problem is when testing if either the mouse or the keyboard set off movement the image vibrates. If i push the arrow keys it moves in a circle the longer I hold the key the wider the circle. Below is the script I'm working with.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PlayerBehaviour : MonoBehaviour
{
//movement modifier applied to directional movement
public float playerSpeed = 2.0f;
//current player speed
private float currentSpeed = 0.0f;
/*
* Allows us to have multiple inputs and supports keyboard,
* joystick, etc.
*/
public List<KeyCode> upButton;
public List<KeyCode> downButton;
public List<KeyCode> leftButton;
public List<KeyCode> rightButton;
//last movement made
private Vector3 lastMovement = new Vector3();
// Update is called once per frame
void Update()
{
//rotates ship to face mouse
Rotation();
//moves ship
Movement();
}
void Rotation()
{
//finds mouse in relation to player location
Vector3 worldPos = Input.mousePosition;
worldPos = Camera.main.ScreenToWorldPoint(worldPos);
/*
get x and y screen positions
*/
float dx = this.transform.position.x - worldPos.x;
float dy = this.transform.position.y - worldPos.y;
//find the angle between objects
float angle = Mathf.Atan2(dy, dx) * Mathf.Rad2Deg;
/*
* The transform's rotation property uses a Quaternion,
* so we need to convert the angle in a Vector
* (The Z axis is for rotation for 2D).
*/
Quaternion rot = Quaternion.Euler(new Vector3(0, 0, angle + 90));
// Assign the ship's rotation
this.transform.rotation = rot;
}
// Will move the player based off of keys pressed
void Movement()
{
// The movement that needs to occur this frame
Vector3 movement = new Vector3();
// Check for input
movement += MoveIfPressed(upButton, Vector3.up);
movement += MoveIfPressed(downButton, Vector3.down);
movement += MoveIfPressed(leftButton, Vector3.left);
movement += MoveIfPressed(rightButton, Vector3.right);
/*
* If we pressed multiple buttons, make sure we're only
* moving the same length.
*/
movement.Normalize();
// Check if we pressed anything
if (movement.magnitude > 0)
{
// If we did, move in that direction
currentSpeed = playerSpeed;
this.transform.Translate(movement * Time.deltaTime * playerSpeed, Space.World);
lastMovement = movement;
}
else
{
// Otherwise, move in the direction we were going
this.transform.Translate(lastMovement * Time.deltaTime * currentSpeed, Space.World);
// Slow down over time
currentSpeed *= .9f;
}
}
/*
* Will return the movement if any of the keys are pressed,
* otherwise it will return (0,0,0)
*/
Vector3 MoveIfPressed(List<KeyCode> keyList, Vector3 Movement)
{
// Check each key in our list
foreach (KeyCode element in keyList)
{
if (Input.GetKey(element))
{
/*
* It was pressed so we leave the function
* with the movement applied.
*/
return Movement;
}
}
// None of the keys were pressed, so don't need to move
return Vector3.zero;
}
}
I studied your code for a while and could not find anything wrong with it. So I tested it myself and it works perfectly.
So I suppose there's something wrong with your scene. You might for example have your player object be a child of some object that rotates your axes: this would cause problems I suppose.
Have a new, empty scene. Add a new GameObject (a 3D cube for example, or a 2D sprite) and assign PlayerBehaviour to it. Now test: it should work perfectly.
I am able to jump or move left/right any point in time. But unable to jump and at the same time move left/right simultaneously. Am I missing anything? My codes as follows. Thanks for advice.
public int rotationSpeed = 100;
public float jumpHeight = 8;
private bool isFalling = false;
void Update () {
// handle ball rotation
float rotation = Input.GetAxis("Horizontal") * rotationSpeed;
rotation *= Time.deltaTime;
rigidbody.AddRelativeTorque(Vector3.back * rotation);
//check input
if (Input.GetKeyDown(KeyCode.W))
{
rigidbody.velocity = new Vector3(0, jumpHeight, 0);
}
}
As stated in docs : here
In most cases you should not modify the velocity directly
you should use AddForce instead or AddForceAtPosition
EDIT :
Just to clarify why :
Velocity is a calculated result of the different forces applied to your object, you CAN assign this value to force the calculus to not be used BUT you SHOULD NOT because adding forces together with the builtin AddForce is way more stable and clean,
in short if you assign to velocity you bypass every calculus you've done before if you use AddForce, as name states you add a new force to your forces sum
EDIT 2 :
void Update () {
// handle ball rotation
float rotation = Input.GetAxis("Horizontal") * rotationSpeed;
rotation *= Time.deltaTime;
rigidbody.AddRelativeTorque(Vector3.back * rotation);
//check input
if (Input.GetKeyDown(KeyCode.W))
{
rigidbody.AddForce(new Vector3(0, jumpHeight, 0) * Time.deltaTime);
}
}