[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.
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);
}
Whenever I'm running against a wall, like if there is a wall to my left and I hold 'a' against the wall it kinda spazes out. It looks like the character is going in and out of the wall. Hopefully that made sense and you know what I'm talking about. So my question is how could I fix this so that when I am actively running into a wall it doesnt do that and instead the character is just there against the wall and appearing to move at all.
code for the movement:
void Update()
{
var movement = Input.GetAxis("Horizontal");
transform.position += new Vector3(movement, 0, 0) * Time.deltaTime * MovementSpeed;
if (Input.GetButtonDown("Jump") && Mathf.Abs(_rigidbody.velocity.y) < .001f)
{
_rigidbody.AddForce(new Vector2(0, JumpForce), ForceMode2D.Impulse);
}
}
Setting the transform.position literally teleports the player, so it sometimes teleports them into the wall then they get pushed back.
To prevent that i suggest using the rigidbody's movePosition function. This takes into account physics while moving, so it interacts with the other objects that are there.
To change your current code to that it would be something like this:
void Update()
{
var movement = Input.GetAxis("Horizontal");
//This moves the GameObject to the currentPosition + The move direction. Which means to move it in the direction that you intended to move in.
_rigidbody.MovePosition(transform.position + new Vector3(movement, 0, 0) * Time.deltaTime * movementSpeed);
if (Input.GetButtonDown("Jump") && Mathf.Abs(_rigidbody.velocity.y) < .001f)
{
_rigidbody.AddForce(new Vector2(0, JumpForce), ForceMode2D.Impulse);
}
}
However i suggest splitting it into this:
float movement = 0f; //Setting a default value.
void Update()
{
movement = Input.GetAxis("Horizontal"); //Getting movement input from player
if (Input.GetButtonDown("Jump") && Mathf.Abs(_rigidbody.velocity.y) < .001f)
{
_rigidbody.AddForce(new Vector2(0, JumpForce), ForceMode2D.Impulse);
}
}
private void FixedUpdate()
{ //Moving in Physics update
_rigidbody.MovePosition(transform.position + new Vector3(movement, 0, 0) * Time.deltaTime * movementSpeed);
}
Because Update checks every frame, so its good for checking input, however you should apply physics in FixedUpdate(), because that is applied whenever there is a physics update (Or physics frame). (For even more smoothness!)
CharacterController _charController;
// ...
moveInputX = Input.GetAxis("Horizontal") * speed;
moveInputZ = Input.GetAxis("Vertical") * speed;
Vector3 movement = new Vector3(moveInputX, 0, moveInputZ);
movement = Vector3.ClampMagnitude(movement, speed);
movement.y = -9.81f;
movement *= Time.deltaTime;
movement = transform.TransformDirection(movement);
_charController.Move(movement);
I have a player that rotates and I want to make him move to his local direction but i don't understand when I remove movement = transform.TransformDirection(movement); from the code it moves to global direction which doesn't make any sense because this line of code converts the direction from local to global space.
It's because CharacterController.Move expects a vector in world space. Sadly, the documentation on that has never been clear about that.
When you calculate movement here:
Vector3 movement = new Vector3(moveInputX, 0, moveInputZ);
You can see that it doesn't take the rotation of the character's transform into account. In order for it to do that, you want to find what this vector looks like in world space when interpreted as being in the character's local space. That's exactly what this line does:
movement = transform.TransformDirection(movement);
It converts movement from local space to world space.
The 2018.1 CharacterController.Move documentation actually uses transform.TransformDirection in its example:
using UnityEngine;
using System.Collections;
// The GameObject is made to bounce using the space key.
// Also the GameOject can be moved forward/backward and left/right.
// Add a Quad to the scene so this GameObject can collider with a floor.
public class ExampleScript : MonoBehaviour
{
public float speed = 6.0f;
public float jumpSpeed = 8.0f;
public float gravity = 20.0f;
private Vector3 moveDirection = Vector3.zero;
private CharacterController controller;
void Start()
{
controller = GetComponent<CharacterController>();
// let the gameObject fall down
gameObject.transform.position = new Vector3(0, 5, 0);
}
void Update()
{
if (controller.isGrounded)
{
// We are grounded, so recalculate
// move direction directly from axes
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0.0f, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection = moveDirection * speed;
if (Input.GetButton("Jump"))
{
moveDirection.y = jumpSpeed;
}
}
// Apply gravity
moveDirection.y = moveDirection.y - (gravity * Time.deltaTime);
// Move the controller
controller.Move(moveDirection * Time.deltaTime);
}
}
Your movement vector is in local space
You want the player to move forward locally and move horizontally locally and so you construct a forward/rightward movement vector based on the player input. This vector is in local coordinates and you TransformDirection back to world space before applying the result to world position with _charController.Move (presumably, you didn't include that method's code).
I'm trying to add jumping to the controls of our control script but it just doesn't work yet and I'm getting kind of frustrated because I tried a lot to make it work. I don't use rigidbodies and rather want to use script based physics and - but later - raycasts (to detect ground). It's 3D but with a fixed perspective because the characters are sprites (we are thinking about trying a style similar to Don't Starve, Paper Mario etc.). And the thing I want to put in for now is to jump upwards when the character stands still and later on jumps that also let the character travel a bit of distance when the character moves. Well, you get the picture. For that I first need to get jumping working at all - and also gravity to put the character back down while also considering that the character could land on a ground that has a different height than the starting ground.
What happens now is that the character jumps just a tiny little bit, like not even a hop, and then falls down endlessly - or goes up endlessly when jump is pressed again. So, how do I fix that?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Controls3D : MonoBehaviour
{
public float player3DSpeed = 5f;
private bool IsGrounded = true;
private const float groundY = (float) 0.4;
private Vector3 Velocity = Vector3.zero;
public Vector3 gravity = new Vector3(0, -10, 0);
void Start()
{
}
void Update()
{
if (IsGrounded)
{
if (Input.GetButtonDown("Jump"))
{
IsGrounded = false;
Velocity = new Vector3(Input.GetAxis("Horizontal"), 20, Input.GetAxis("Vertical"));
}
Velocity.x = Input.GetAxis("Horizontal");
Velocity.z = Input.GetAxis("Vertical");
if (Velocity.sqrMagnitude > 1)
Velocity.Normalize();
transform.position += Velocity * player3DSpeed * Time.deltaTime;
}
else
{
Velocity += gravity * Time.deltaTime;
Vector3 position = transform.position + Velocity * Time.deltaTime;
if (position.y < groundY)
{
position.y = groundY;
IsGrounded = true;
}
transform.position = position;
}
}
}
If i were you, i think i wouldve changed the whole thing into a character controller, as this simplifies the process, a ####ton :P
If you figure out you do want to use the CC. This is the solution i usually use:
CharacterController controller = GetComponent<CharacterController>();
// is the controller on the ground?
if (controller.isGrounded)
{
//Feed moveDirection with input.
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
//Multiply it by speed.
moveDirection *= speed;
//Jumping
if (Input.GetButton("Jump"))
moveDirection.y = jumpSpeed;
}
//Applying gravity to the controller
moveDirection.y -= gravity * Time.deltaTime;
//Making the character move
controller.Move(moveDirection * Time.deltaTime);
moveDirection is a Vector3 Type variable, and speed and jumpSpeed are public float values used to modify the speed.
Please NOTE: Character Controllers, let you program your own Physics.
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();
}