Unity - Make character look at mouse with New input system/Character Controller - c#

Been following this guide https://youtu.be/bXNFxQpp2qk?t=1280 for Character Controller.
This got me getting basic movement working and at time 21:20 he creates a way to rotate the player.
https://youtu.be/b0AQg5ZTpac and this video for the player facing the mouse position.
At the 7:15 mark she explains how to get the mousePosition in which I stored it in positionToLookAt
I want my character's rotation to be done with the mouse and have a field of view like here: https://youtu.be/rQG9aUWarwE
Been trying to to get it so that the player is facing the direction of the mouse position, but haven't been getting the result. With here showing https://imgur.com/gallery/mPPWogi the video results and my PlayerInputs.
Some peers mentioned replacing
Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt);
by
Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt - transform.position);
But it doesn't work and produced the same results in the imgur.
Can anyone help me out here? I'm stuck and I have no idea how to make this character look at the mouse instead.
here's the snippet of the method
void handleRotation()
{
Vector3 positionToLookAt;
// get mouse position
Vector2 mousePosition = playerInput.CharacterControls.MousePosition.ReadValue<Vector2>();
mousePosition = Camera.main.ScreenToWorldPoint(mousePosition);
// insert mouse position to looking position
positionToLookAt.x = currentMovement.x;
//positionToLookAt.x = mousePosition.x;
positionToLookAt.y = 0.0f;
positionToLookAt.z = currentMovement.z;
//positionToLookAt.z = mousePosition.y;
Quaternion currentRotation = transform.rotation;
if (isMovementPressed)
{
Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt);
transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, rotationFactorPerFrame * Time.deltaTime);
}
}
And if anyone wants my full code, here.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class AnimationAndMovementController : MonoBehaviour
{
PlayerInput playerInput;
UnityEngine.CharacterController characterController;
Animator animator;
int isWalkingHash;
int isRunningHash;
Vector2 currentMovementInput;
Vector3 currentMovement;
Vector3 currentRunMovement;
bool isMovementPressed;
bool isRunPressed;
float rotationFactorPerFrame = 15.0f;
float runMultiplier = 3.0f;
//Debug.Log(context.ReadValue<Vector2>());
void Start()
{
}
void Awake()
{
playerInput = new PlayerInput();
characterController = GetComponent<UnityEngine.CharacterController>();
animator = GetComponent<Animator>();
isWalkingHash = Animator.StringToHash("isWalking");
isRunningHash = Animator.StringToHash("isRunning");
playerInput.CharacterControls.Move.started += onMovementInput;
playerInput.CharacterControls.Move.canceled += onMovementInput;
playerInput.CharacterControls.Move.performed += onMovementInput;
playerInput.CharacterControls.Run.started += onRun;
playerInput.CharacterControls.Run.canceled += onRun;
}
void onRun(InputAction.CallbackContext context)
{
isRunPressed = context.ReadValueAsButton();
}
void onMovementInput (InputAction.CallbackContext context)
{
currentMovementInput = context.ReadValue<Vector2>();
currentMovement.x = currentMovementInput.x;
currentMovement.z = currentMovementInput.y;
currentRunMovement.x = currentMovementInput.x * runMultiplier;
currentRunMovement.z = currentMovementInput.y * runMultiplier;
isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0;
}
void handleRotation()
{
Vector3 positionToLookAt;
Vector2 mousePosition = playerInput.CharacterControls.MousePosition.ReadValue<Vector2>();
mousePosition = Camera.main.ScreenToWorldPoint(mousePosition);
positionToLookAt.x = currentMovement.x;
//positionToLookAt.x = mousePosition.x;
positionToLookAt.y = 0.0f;
positionToLookAt.z = currentMovement.z;
//positionToLookAt.z = mousePosition.y;
Quaternion currentRotation = transform.rotation;
if (isMovementPressed)
{
Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt);
transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, rotationFactorPerFrame * Time.deltaTime);
}
}
void handleAnimation()
{
bool isWalking = animator.GetBool(isWalkingHash);
bool isRunning = animator.GetBool(isRunningHash);
if (isMovementPressed && !isWalking) {
animator.SetBool(isWalkingHash, true);
}
else if (!isMovementPressed && isWalking){
animator.SetBool(isWalkingHash, false);
}
if ((isMovementPressed && isRunPressed) && !isRunning)
{
animator.SetBool(isRunningHash, true);
}
else if ((!isMovementPressed && !isRunPressed) && isRunning)
{
animator.SetBool(isRunningHash, false);
}
else if ((isMovementPressed && !isRunPressed) && isRunning)
{
animator.SetBool(isRunningHash, false);
}
}
void handleGravity()
{
if (characterController.isGrounded) {
float groundedGravity = -0.05f;
currentMovement.y = groundedGravity;
currentRunMovement.y = groundedGravity;
} else {
float gravity = -9.8f;
currentMovement.y = gravity;
currentRunMovement.y = gravity;
}
}
// Update is called once per frame
void Update()
{
handleRotation();
handleAnimation();
handleGravity();
if (isRunPressed) {
characterController.Move(currentRunMovement * Time.deltaTime);
}
else {
characterController.Move(currentMovement * Time.deltaTime);
}
}
void OnEnable()
{
playerInput.CharacterControls.Enable();
}
void OnDisable()
{
playerInput.CharacterControls.Disable();
}
}
EDIT
Updated full code and handleRotation() is where Rotation is being handled:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class AnimationAndMovementController : MonoBehaviour
{
PlayerInput playerInput;
CharacterController characterController;
Animator animator;
int isWalkingHash;
int isRunningHash;
Camera _camera;
Vector2 currentMovementInput;
Vector3 currentMovement;
Vector3 currentRunMovement;
bool isMovementPressed;
bool isRunPressed;
//float rotationFactorPerFrame = 15.0f;
float runMultiplier = 3.0f;
// Start is called before the first frame update
void Start()
{
_camera = Camera.main;
}
void Awake()
{
playerInput = new PlayerInput();
characterController = GetComponent<CharacterController>();
animator = GetComponent<Animator>();
isWalkingHash = Animator.StringToHash("isWalking");
isRunningHash = Animator.StringToHash("isRunning");
playerInput.CharacterControls.Move.started += onMovementInput;
playerInput.CharacterControls.Move.canceled += onMovementInput;
playerInput.CharacterControls.Move.performed += onMovementInput;
playerInput.CharacterControls.Run.started += onRun;
playerInput.CharacterControls.Run.canceled += onRun;
}
void onRun(InputAction.CallbackContext context)
{
isRunPressed = context.ReadValueAsButton();
}
void onMovementInput(InputAction.CallbackContext context)
{
currentMovementInput = context.ReadValue<Vector2>();
currentMovement.x = currentMovementInput.x;
currentMovement.z = currentMovementInput.y;
currentRunMovement.x = currentMovementInput.x * runMultiplier;
currentRunMovement.z = currentMovementInput.y * runMultiplier;
isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0;
}
void handleAnimation()
{
bool isWalking = animator.GetBool(isWalkingHash);
bool isRunning = animator.GetBool(isRunningHash);
if (isMovementPressed && !isWalking)
{
animator.SetBool(isWalkingHash, true);
}
else if (!isMovementPressed && isWalking)
{
animator.SetBool(isWalkingHash, false);
}
if ((isMovementPressed && isRunPressed) && !isRunning)
{
animator.SetBool(isRunningHash, true);
}
else if ((!isMovementPressed && !isRunPressed) && isRunning)
{
animator.SetBool(isRunningHash, false);
}
else if ((isMovementPressed && !isRunPressed) && isRunning)
{
animator.SetBool(isRunningHash, false);
}
}
void handleGravity()
{
if (characterController.isGrounded)
{
float groundedGravity = -0.05f;
currentMovement.y = groundedGravity;
currentRunMovement.y = groundedGravity;
}
else
{
float gravity = -9.8f;
currentMovement.y = gravity;
currentRunMovement.y = gravity;
}
}
void handleRotation()
{
// We're getting a Vector2, whereas we will need a Vector3
// Get a z value based on camera, and include it in a Vector3
var mousePosition = playerInput.CharacterControls.MousePosition.ReadValue<Vector2>();
var mousePositionZ = _camera.farClipPlane * .5f;
var mouseViewportPosition = _camera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, mousePositionZ));
// Do the same with the object's position
var positionOnViewport = Camera.main.WorldToViewportPoint(transform.position);
// Get the angle between the points
var angle = AngleBetweenTwoPoints(positionOnViewport, mouseViewportPosition);
// Apply the angle as the rotation of the object
transform.rotation = Quaternion.Euler(new Vector3(0f, -angle, 0f));
}
float AngleBetweenTwoPoints(Vector3 a, Vector3 b)
{
return Mathf.Atan2(b.y - a.y, b.x - a.x) * Mathf.Rad2Deg;
}
// Update is called once per frame
void Update()
{
handleRotation();
handleAnimation();
handleGravity();
if (isRunPressed)
{
characterController.Move(currentRunMovement * Time.deltaTime);
}
else
{
characterController.Move(currentMovement * Time.deltaTime);
}
}
void OnEnable()
{
playerInput.CharacterControls.Enable();
}
void OnDisable()
{
playerInput.CharacterControls.Disable();
}
}
EDIT 2: with current iteration
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class AnimationAndMovementController : MonoBehaviour
{
PlayerInput playerInput;
CharacterController characterController;
Animator animator;
int isWalkingHash;
int isRunningHash;
Camera _camera;
Vector2 currentMovementInput;
Vector3 currentMovement;
Vector3 currentRunMovement;
bool isMovementPressed;
bool isRunPressed;
float rotationFactorPerFrame = 15.0f;
float runMultiplier = 3.0f;
// Start is called before the first frame update
void Start()
{
_camera = Camera.main;
}
void Awake()
{
playerInput = new PlayerInput();
characterController = GetComponent<CharacterController>();
animator = GetComponent<Animator>();
isWalkingHash = Animator.StringToHash("isWalking");
isRunningHash = Animator.StringToHash("isRunning");
playerInput.CharacterControls.Move.started += onMovementInput;
playerInput.CharacterControls.Move.canceled += onMovementInput;
playerInput.CharacterControls.Move.performed += onMovementInput;
playerInput.CharacterControls.Run.started += onRun;
playerInput.CharacterControls.Run.canceled += onRun;
}
void onRun(InputAction.CallbackContext context)
{
isRunPressed = context.ReadValueAsButton();
}
void onMovementInput(InputAction.CallbackContext context)
{
currentMovementInput = context.ReadValue<Vector2>();
currentMovement.x = currentMovementInput.x;
currentMovement.z = currentMovementInput.y;
currentRunMovement.x = currentMovementInput.x * runMultiplier;
currentRunMovement.z = currentMovementInput.y * runMultiplier;
isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0;
}
void handleAnimation()
{
bool isWalking = animator.GetBool(isWalkingHash);
bool isRunning = animator.GetBool(isRunningHash);
if (isMovementPressed && !isWalking)
{
animator.SetBool(isWalkingHash, true);
}
else if (!isMovementPressed && isWalking)
{
animator.SetBool(isWalkingHash, false);
}
if ((isMovementPressed && isRunPressed) && !isRunning)
{
animator.SetBool(isRunningHash, true);
}
else if ((!isMovementPressed && !isRunPressed) && isRunning)
{
animator.SetBool(isRunningHash, false);
}
else if ((isMovementPressed && !isRunPressed) && isRunning)
{
animator.SetBool(isRunningHash, false);
}
}
void handleGravity()
{
if (characterController.isGrounded)
{
float groundedGravity = -0.05f;
currentMovement.y = groundedGravity;
currentRunMovement.y = groundedGravity;
}
else
{
float gravity = -9.8f;
currentMovement.y = gravity;
currentRunMovement.y = gravity;
}
}
void handle_isRunPressed()
{
if (isRunPressed)
{
characterController.Move(currentRunMovement * Time.deltaTime);
}
else
{
characterController.Move(currentMovement * Time.deltaTime);
}
}
void handleRotation()
{
var mousePosition = playerInput.CharacterControls.MousePosition.ReadValue<Vector2>();
var mousePositionZ = _camera.farClipPlane * .5f;
var mouseWorldPosition = _camera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, mousePositionZ)); // _camera.ScreenToViewportPoint(mousePosition);
// Get the angle between the points
// Use the x and z from the object/mouse, since we're looking along the y axis
var angle = AngleBetweenTwoPoints(new Vector2(transform.position.x, transform.position.z), new Vector2(mouseWorldPosition.x, mouseWorldPosition.z));
transform.rotation = Quaternion.Euler(new Vector3(0f, -angle, 0f));
}
float AngleBetweenTwoPoints(Vector3 a, Vector3 b)
{
return Mathf.Atan2(b.y - a.y, b.x - a.x) * Mathf.Rad2Deg;
}
// Update is called once per frame
void Update()
{
handleRotation();
handleAnimation();
handleGravity();
handle_isRunPressed();
}
void OnEnable()
{
playerInput.CharacterControls.Enable();
}
void OnDisable()
{
playerInput.CharacterControls.Disable();
}
}
Rotation still not working as intended with my code unforunately within handleRotation()
EDIT 3:
void handleRotation()
{
// We're getting a Vector2, whereas we will need a Vector3
// Get a z value based on camera, and include it in a Vector3
Vector2 mousePosition = playerInput.CharacterControls.MousePosition.ReadValue<Vector2>();
var mousePositionZ = _camera.farClipPlane * .5f;
Vector3 mouseViewportPosition = _camera.ViewportToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, _camera.transform.position.y));
Debug.Log("MousePos: " + mouseViewportPosition);
Vector3 positionToLookAt;
positionToLookAt.x = mouseViewportPosition.x;
positionToLookAt.y = 0.0f;
//positionToLookAt.z = currentMovement.z;
positionToLookAt.z = mouseViewportPosition.z;
Quaternion currentRotation = transform.rotation;
Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt - transform.position);
transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, rotationFactorPerFrame * Time.deltaTime);
}
IT'S ROTATING. But not correctly.

UPDATE Take 2
Here is the full code I have in my Update():
var mousePosition = _playerInput.Player.MousePosition.ReadValue<Vector2>();
var mousePositionZ = _camera.farClipPlane * .5f;
var mouseWorldPosition = _camera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, mousePositionZ)); // _camera.ScreenToViewportPoint(mousePosition);
// Get the angle between the points
// Use the x and z from the object/mouse, since we're looking along the y axis
var angle = AngleBetweenTwoPoints(new Vector2(transform.position.x, transform.position.z), new Vector2(mouseWorldPosition.x, mouseWorldPosition.z));
transform.rotation = Quaternion.Euler(new Vector3(0f, -angle, 0f));
and the helper function is the same:
float AngleBetweenTwoPoints(Vector2 a, Vector2 b)
{
return Mathf.Atan2(b.y - a.y, b.x - a.x) * Mathf.Rad2Deg;
}
UPDATE
I figured out how to do this in the way that Joseph was originally trying.
ScreenToWorldPoint does indeed work correctly with the new input system. The issue is the way we're trying to use it. It needs a Vector3!
You CAN'T do this:
// Don't do this, it wont work!
var mousePosition = _playerInput.Player.MousePosition.ReadValue<Vector2>();
var mouseViewportPosition = _camera.ScreenToWorldPoint(mousePosition);
You need to do this:
// We're getting a Vector2, whereas we will need a Vector3
// Get a z value based on camera, and include it in a Vector3
var mousePosition = _playerInput.Player.MousePosition.ReadValue<Vector2>();
var mousePositionZ = _camera.farClipPlane * .5f;
var mouseViewportPosition = _camera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, mousePositionZ));
Original answer:
Your example had some issues... I think you were in the middle of editing it to make the rotation based on the movement direction? There may be an issue with the mouse position stuff right now in the engine... I noticed some other threads complaining that the input values weren't the same, but I'm not sure to what extent. Maybe the conversion to world coordinates isn't working?
For some reason, this does not work for me, it always spits out (0.0, 10.0, 0.0):
var mouseWorldPosition = Camera.main.ScreenToWorldPoint(mousePosition);
This is what I was able to get working:
// Get the mouse position from the NEW input system
var mousePosition = _playerInput.Player.MousePosition.ReadValue<Vector2>();
// Convert the mousePosition to the VIEWPORT
var mouseViewportPosition = Camera.main.ScreenToViewportPoint(mousePosition);
// Do the same with the object's position
var positionOnViewport = Camera.main.WorldToViewportPoint(transform.position);
// Get the angle between the points
var angle = AngleBetweenTwoPoints(positionOnViewport, mouseViewportPosition);
// Apply the angle as the rotation of the object
transform.rotation = Quaternion.Euler(new Vector3(0f, -angle, 0f));
The general idea was from here, including this function (https://answers.unity.com/questions/855976/make-a-player-model-rotate-towards-mouse-location.html):
float AngleBetweenTwoPoints(Vector3 a, Vector3 b)
{
return Mathf.Atan2(b.y - a.y, b.x - a.x) * Mathf.Rad2Deg;
}
Note that this isn't perfect... the angle is slightly off... not sure why that is. You can see it exaggerated a little here:
I think the moral of the story here is to check the input at each stage to see what it looks like and how it gets/got transformed. Now that we know some of the new input values may be off, you can either use the old system's mouse position, or combine my example with some of the other methods you mentioned.

Adding a new answer because this will just use Quaternions like you're trying to do.
The first step is to convert the mouse from screen coordinates, to world coordinates:
// Read the mouse position from the new input system
var mousePosition = _playerInput.Player.MousePosition.ReadValue<Vector2>();
// Ensure that there is a "valid" z value so that the conversion works properly
var mousePositionZ = _camera.farClipPlane * .5f;
// Convert!
var mouseWorldPosition = _camera.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, mousePositionZ));
Now that we have the mouse position in world coordinates, we can compare it to the object's (player's) position, and rotate based on that difference:
// Calculate the difference between the positions
var positionVector = mouseWorldPosition - transform.position;
// Match the new y value to the object's Y value.
// This ensures that the rotation is calculated only with the X and Z
// I would love to know why this is happening... but I didn't find anything in my initial research
positionVector.y = transform.position.y;
// Now we calculate the rotation
var targetRotation = Quaternion.LookRotation(positionVector);
// FYI, if your object's final rotation is off by 90 degrees, you can do the following
// I think it has to do with what the system thinks "forward" is, and which way your model is facing by default.
// So you can either fix it in your model, or add/subtract 90 degrees
// Note that **multiplying** Quaternions together effectively **combines** them
// var targetRotation = Quaternion.LookRotation(positionVector) * Quaternion.Euler(0, -90, 0);
// And smoothly transition to the new angle using Slerp
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, rotationFactorPerFrame * Time.deltaTime);

I was having the same issue and finally i come up with smoother look at mouse solution for 3D Top-Down games. This is what my whole code looks like. I hope it helps.
[SerializeField] float moveSpeed = 5f;
[SerializeField] float turnSpeed = 2f;
Vector2 _moveInput;
Vector2 _mousePos;
void Update()
{
HandleMovement();
HandleRotation();
}
void OnMove(InputValue moveInput)
{
_moveInput = moveInput.Get<Vector2>();
}
void HandleMovement()
{
Vector3 deltaPos = new Vector3(_moveInput.x, 0f, _moveInput.y) * moveSpeed * Time.deltaTime;
transform.position += deltaPos;
}
void OnAim(InputValue currentMousePos)
{
_mousePos = currentMousePos.Get<Vector2>();
}
void HandleRotation()
{
Ray ray = Camera.main.ScreenPointToRay(_mousePos);
Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
float rayDistance;
if (groundPlane.Raycast(ray, out rayDistance))
{
Vector3 point = ray.GetPoint(rayDistance);
LookAt(point);
}
}
void LookAt(Vector3 lookPoint)
{
Vector3 direction = (lookPoint - transform.position).normalized;
Quaternion lookRotation = Quaternion.LookRotation(new Vector3(direction.x, 0f, direction.z));
transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * turnSpeed);
}
And this is PlayerInput setup
Also you can check this tutorial video for more detail

Related

Why the object keep moving up nonstop and how to move it forward fast when it finished moving up?

I want to move the object up smooth slowly from it's current position on y 50.01 to new position 51.255
But the object keep moving up nonstop.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class RoboSphereWindowBreakInteraction : MonoBehaviour
{
public Transform target;
public AudioClip audioClip;
public float speed;
private bool hasStarted = false;
private Animator anim;
void Update()
{
if ((Input.GetKeyDown(KeyCode.B) || (hasStarted == true)))
{
float step = speed * Time.deltaTime; // calculate distance to move
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
hasStarted = true;
}
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "Square 1")
{
GetComponent<Rigidbody>().isKinematic = false;
hasStarted = false;
Destroy(GameObject.Find("Wall_Window_Long_03"));
}
}
public void ActivateRoboSphere()
{
foreach(Transform child in transform)
{
if(child.name == "Camera")
{
RepositionCamera(child);
}
}
anim = GetComponent<Animator>();
anim.enabled = true;
GetComponent<FPEInteractableActivateScript>().interactionString = "";
FPEInteractionManagerScript.Instance.BeginCutscene();
StartCoroutine(PlayAudio());
}
private void RepositionCamera(Transform camera)
{
var Eyes = GameObject.Find("eyeDome");
camera.position = Eyes.transform.position + Eyes.transform.forward;
camera.LookAt(Eyes.transform);
camera.GetComponent<Camera>().enabled = true;
}
IEnumerator PlayAudio()
{
AudioSource audio = GetComponent<AudioSource>();
audio.clip = audioClip;
audio.Play();
yield return new WaitForSeconds(audio.clip.length);
var rotation = Quaternion.LookRotation(target.position - transform.position);
StartCoroutine(Spin(3f, rotation, () =>
{
anim.SetBool("Roll_Anim", true);
}));
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
transform.position.y + 51.255f, transform.position.z), 3f));
}
IEnumerator Spin(float lerpTime, Quaternion rotation, Action whenDone)
{
float elapsedTime = 0f;
while (elapsedTime <= lerpTime)
{
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, elapsedTime / lerpTime);
elapsedTime += Time.deltaTime;
yield return null;
}
whenDone?.Invoke();
}
// 51.255
IEnumerator MoveFromTo(Transform objectToMove, Vector3 a, Vector3 b, float speed)
{
float step = (speed / (a - b).magnitude) * Time.fixedDeltaTime;
float t = 0;
while (t <= 1.0f)
{
t += step; // Goes from 0 to 1, incrementing by step each time
objectToMove.position = Vector3.Lerp(a, b, t); // Move objectToMove closer to b
yield return new WaitForFixedUpdate(); // Leave the routine and return here in the next frame
}
objectToMove.position = b;
}
}
I did :
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
transform.position.y + 51.255f, transform.position.z), 3f));
But the object keep moving up nonstop. Or at least very high and not like I wanted from 50.01 to 51.255
And when the object reaching the height of 51.255 then I want it to move fast smooth forward to target.
In your code replace this part:
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
transform.position.y + 51.255f, transform.position.z), 3f));
with this:
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
51.255f, transform.position.z), 3f));

Rotation smooth in editor but isn't in device

I have a 3D object that I want to rotate with mouse/finger swipe, so I made the script below.
The object's rotation looks smooth on editor, but when playing the game on real device (android), the rotation didn't follow the finger movement immediately, it takes some milliseconds to follow finger, it isn't smooth and the controls become hard and frustrating!
float sensitivity = 0.8f;
Vector2 firstPressPos;
Vector2 secondPressPos;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
//save began touch 2d point
firstPressPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y);
}
if (Input.GetMouseButton(0))
{
//save ended touch 2d point
secondPressPos = new Vector3(Input.mousePosition.x, Input.mousePosition.y);
if (firstPressPos != secondPressPos)
{
float RotX = Input.GetAxis("Mouse X") * sensitivity * Time.deltaTime;
float RotY = Input.GetAxis("Mouse Y") * sensitivity * Time.deltaTime;
transform.RotateAround(Vector3.up, RotX);
transform.RotateAround(Vector3.right, -RotY);
}
}
}
try this code
// Screen Touches
Vector2?[] oldTouchPositions = {
null,
null
};
// Rotation Speed
public float rotSpeed = 0.5f;
public void Update()
{
if (Input.touchCount == 0)
{
oldTouchPositions[0] = null;
oldTouchPositions[1] = null;
}
else if (Input.touchCount == 1)
{
if (oldTouchPositions[0] == null || oldTouchPositions[1] != null)
{
oldTouchPositions[0] = Input.GetTouch(0).position;
oldTouchPositions[1] = null;
}
else
{
Vector2 newTouchPosition = Input.GetTouch(0).position;
float distanceX = (oldTouchPositions[0] - newTouchPosition).Value.x;
float distanceY = (oldTouchPositions[0] - newTouchPosition).Value.y;
float rotX = distanceX * rotSpeed * Mathf.Deg2Rad;
float rotY = distanceY * rotSpeed * Mathf.Deg2Rad;
transform.Rotate(Vector3.up, rotX * 5, Space.Self);
transform.Rotate(Vector3.left, rotY * 5, Space.Self);
oldTouchPositions[0] = newTouchPosition;
}
}
else
{
if (oldTouchPositions[1] == null)
{
oldTouchPositions[0] = Input.GetTouch(0).position;
oldTouchPositions[1] = Input.GetTouch(1).position;
}
else
{
}
}
}
Here's a script I found a few years back that manipulates X axis spin with Mouse or Touches. It seems to work well with touches, but not as good with Mouse. Try it and let me know if it behaves a bit better. This one should adjust the spinning speed dynamically:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpinDrag : MonoBehaviour {
float f_lastX = 0.0f;
float f_difX = 0.5f;
float f_steps = 0.0f;
int i_direction = 1;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
f_difX = 0.0f;
}
else if (Input.GetMouseButton(0))
{
f_difX = Mathf.Abs(f_lastX - Input.GetAxis("Mouse X"));
if (f_lastX < Input.GetAxis("Mouse X"))
{
i_direction = -1;
transform.Rotate(Vector3.up, -f_difX);
}
if (f_lastX > Input.GetAxis("Mouse X"))
{
i_direction = 1;
transform.Rotate(Vector3.up, f_difX);
}
f_lastX = -Input.GetAxis("Mouse X");
}
else
{
if (f_difX > 0.5f) f_difX -= 0.05f;
if (f_difX < 0.5f) f_difX += 0.05f;
transform.Rotate(Vector3.up, f_difX * i_direction);
}
}
}

How do I change the colour of the whole object once its trigger has been hit?

I am making a chemistry molecule game. So far I have a molecule with a trigger on a receptor site and when I drag the correct atom close to this site, the atom changes colour and attaches. How do I change the colour of the whole molecule (not just the trigger/receptor site) once this atom is attached?
Code used for this part of my game is from: https://gamedev.stackexchange.com/questions/123929/snap-an-object-to-another-object
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Snap : MonoBehaviour
{
public string partnerTag;
public float closeVPDist = 0.05f;
public float farVPDist = 1;
public float moveSpeed = 40.0f;
public float rotateSpeed = 90.0f;
private Vector3 screenPoint;
private Vector3 offset;
private bool isSnaped;
Color color = new Color(2, 1, 3);
float dist = Mathf.Infinity;
Color normalColor;
GameObject partnerGO;
// Use this for initialization
void Start()
{
normalColor = GetComponent<Renderer>().material.color;
partnerGO = GameObject.FindGameObjectWithTag(partnerTag);
}
void OnMouseDown()
{
screenPoint = Camera.main.WorldToScreenPoint(transform.position);
offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z));
Cursor.visible = false;
}
void OnMouseDrag()
{
//transform.SetParent(null);
Vector3 curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint) + offset;
transform.position = curPosition;
Vector3 partnerPos = Camera.main.WorldToViewportPoint(partnerGO.transform.position);
Vector3 myPos = Camera.main.WorldToViewportPoint(transform.position);
dist = Vector2.Distance(partnerPos, myPos);
GetComponent<Renderer>().material.color = (dist < closeVPDist) ? color : normalColor;
}
void OnMouseUp()
{
Cursor.visible = true;
if (dist < closeVPDist)
{
transform.SetParent(partnerGO.transform);
StartCoroutine(InstallPart());
isSnaped = true;
}
if (dist > farVPDist)
{
// transform.SetParent(null);
}
}
IEnumerator InstallPart()
{
while (transform.localPosition != Vector3.right || transform.localRotation != Quaternion.identity)
{
transform.localPosition = Vector3.MoveTowards(transform.localPosition, Vector3.right, Time.deltaTime * moveSpeed);
transform.localRotation = Quaternion.RotateTowards(transform.localRotation, Quaternion.identity, Time.deltaTime * rotateSpeed);
yield return new WaitForEndOfFrame();
}
}
}
In OnMouseUp, add a line to change the material/colour of your molecule. Code:
public Color newColor;
public GameObject objectToChangeColour;
void OnMouseUp()
{
Cursor.visible = true;
if (dist < closeVPDist)
{
transform.SetParent(partnerGO.transform);
StartCoroutine(InstallPart());
isSnaped = true;
objectToChangeColour.GetComponent<Renderer>().material.color = newColour; // Change the color of object to the newColour
}
if (dist > farVPDist)
{
// transform.SetParent(null);
}
}
And simple change objectToChangeColour to the object you want to change its color (parentGO/molecule/etc.), and newColour to the new colour.

Unity, First time using a script in c# it's working fine then after few times getting null exception why?

I have a ThirdPersonController in the Hierarchy.
Then i'm adding to it a Nav Mesh Agent.
The dragging to it the scrip in c#.
Then in the Inspector i'm adding in the script part the agent.
Then baking.
And it was working more or less with some stuttering but was working.
The character ThirdPersonController walked automatic to specific point in this case to a cylinder i have on my Terrain.
I can't figure out why first time/s it's working fine but then now i have this error.
This is the script i'm using for the automatic walk:
using System.Collections;
using UnityStandardAssets.Characters.ThirdPerson;
public class BasicAi : MonoBehaviour {
public NavMeshAgent agent;
public ThirdPersonCharacter character;
public enum State {
PATROL,
CHASE
}
public State state;
private bool alive;
// Variables for patrolling
public GameObject[] waypoints;
private int waypointInd = 0;
public float patrolSpeed = 0.5f;
// Variable for chasing
public float chaseSpeed = 1f;
public GameObject target;
// Use this for initialization
void Start () {
agent = GetComponent<NavMeshAgent> ();
character = GetComponent<ThirdPersonCharacter>();
agent.updatePosition = true;
agent.updateRotation = false;
state = BasicAi.State.PATROL;
alive = true;
StartCoroutine ("FSM");
}
IEnumerator FSM()
{
while (alive)
{
switch (state)
{
case State.PATROL:
Patrol ();
break;
case State.CHASE:
Chase ();
break;
}
yield return null;
}
}
void Patrol()
{
agent.speed = patrolSpeed;
if (Vector3.Distance (this.transform.position, waypoints [waypointInd].transform.position) >= 2) {
agent.SetDestination (waypoints [waypointInd].transform.position);
character.Move (agent.desiredVelocity, false, false);
} else if (Vector3.Distance (this.transform.position, waypoints [waypointInd].transform.position) <= 2) {
waypointInd += 1;
if (waypointInd > waypoints.Length) {
waypointInd = 0;
}
}
else
{
character.Move (Vector3.zero, false, false);
}
}
void Chase()
{
agent.speed = chaseSpeed;
agent.SetDestination (target.transform.position);
character.Move (agent.desiredVelocity, false, false);
}
void OnTriggerEnter(Collider coll)
{
if (coll.tag == "Player")
{
state = BasicAi.State.CHASE;
target = coll.gameObject;
}
}
// Update is called once per frame
void Update () {
}
And the exception error message is:
NullReferenceException: Object reference not set to an instance of an object
UnityStandardAssets.Characters.ThirdPerson.ThirdPersonCharacter.CheckGroundStatus () (at Assets/Standard Assets/Characters/ThirdPersonCharacter/Scripts/ThirdPersonCharacter.cs:215)
UnityStandardAssets.Characters.ThirdPerson.ThirdPersonCharacter.Move (Vector3 move, Boolean crouch, Boolean jump) (at Assets/Standard Assets/Characters/ThirdPersonCharacter/Scripts/ThirdPersonCharacter.cs:54)
BasicAi.Patrol () (at Assets/My Scripts/BasicAi.cs:72)
BasicAi+<FSM>c__Iterator0.MoveNext () (at Assets/My Scripts/BasicAi.cs:55)
UnityEngine.MonoBehaviour:StartCoroutine(String)
BasicAi:Start() (at Assets/My Scripts/BasicAi.cs:43)
When i click on the error message in the Console window it's going to the ThirdPersonCharacter script. The script is from in Unity.
The error is on the line:
m_Animator.applyRootMotion = true;
And this is the ThirdPersonCharacter script where the error is:
using UnityEngine;
namespace UnityStandardAssets.Characters.ThirdPerson
{
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(Animator))]
public class ThirdPersonCharacter : MonoBehaviour
{
[SerializeField] float m_MovingTurnSpeed = 360;
[SerializeField] float m_StationaryTurnSpeed = 180;
[SerializeField] float m_JumpPower = 12f;
[Range(1f, 4f)][SerializeField] float m_GravityMultiplier = 2f;
[SerializeField] float m_RunCycleLegOffset = 0.2f; //specific to the character in sample assets, will need to be modified to work with others
[SerializeField] float m_MoveSpeedMultiplier = 1f;
[SerializeField] float m_AnimSpeedMultiplier = 1f;
[SerializeField] float m_GroundCheckDistance = 0.1f;
Rigidbody m_Rigidbody;
Animator m_Animator;
bool m_IsGrounded;
float m_OrigGroundCheckDistance;
const float k_Half = 0.5f;
float m_TurnAmount;
float m_ForwardAmount;
Vector3 m_GroundNormal;
float m_CapsuleHeight;
Vector3 m_CapsuleCenter;
CapsuleCollider m_Capsule;
bool m_Crouching;
void Start()
{
m_Animator = GetComponent<Animator>();
m_Rigidbody = GetComponent<Rigidbody>();
m_Capsule = GetComponent<CapsuleCollider>();
m_CapsuleHeight = m_Capsule.height;
m_CapsuleCenter = m_Capsule.center;
m_Rigidbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY | RigidbodyConstraints.FreezeRotationZ;
m_OrigGroundCheckDistance = m_GroundCheckDistance;
}
public void Move(Vector3 move, bool crouch, bool jump)
{
// convert the world relative moveInput vector into a local-relative
// turn amount and forward amount required to head in the desired
// direction.
if (move.magnitude > 1f) move.Normalize();
move = transform.InverseTransformDirection(move);
CheckGroundStatus();
move = Vector3.ProjectOnPlane(move, m_GroundNormal);
m_TurnAmount = Mathf.Atan2(move.x, move.z);
m_ForwardAmount = move.z;
ApplyExtraTurnRotation();
// control and velocity handling is different when grounded and airborne:
if (m_IsGrounded)
{
HandleGroundedMovement(crouch, jump);
}
else
{
HandleAirborneMovement();
}
ScaleCapsuleForCrouching(crouch);
PreventStandingInLowHeadroom();
// send input and other state parameters to the animator
UpdateAnimator(move);
}
void ScaleCapsuleForCrouching(bool crouch)
{
if (m_IsGrounded && crouch)
{
if (m_Crouching) return;
m_Capsule.height = m_Capsule.height / 2f;
m_Capsule.center = m_Capsule.center / 2f;
m_Crouching = true;
}
else
{
Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up);
float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half;
if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength, Physics.AllLayers, QueryTriggerInteraction.Ignore))
{
m_Crouching = true;
return;
}
m_Capsule.height = m_CapsuleHeight;
m_Capsule.center = m_CapsuleCenter;
m_Crouching = false;
}
}
void PreventStandingInLowHeadroom()
{
// prevent standing up in crouch-only zones
if (!m_Crouching)
{
Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up);
float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half;
if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength, Physics.AllLayers, QueryTriggerInteraction.Ignore))
{
m_Crouching = true;
}
}
}
void UpdateAnimator(Vector3 move)
{
// update the animator parameters
m_Animator.SetFloat("Forward", m_ForwardAmount, 0.1f, Time.deltaTime);
m_Animator.SetFloat("Turn", m_TurnAmount, 0.1f, Time.deltaTime);
m_Animator.SetBool("Crouch", m_Crouching);
m_Animator.SetBool("OnGround", m_IsGrounded);
if (!m_IsGrounded)
{
m_Animator.SetFloat("Jump", m_Rigidbody.velocity.y);
}
// calculate which leg is behind, so as to leave that leg trailing in the jump animation
// (This code is reliant on the specific run cycle offset in our animations,
// and assumes one leg passes the other at the normalized clip times of 0.0 and 0.5)
float runCycle =
Mathf.Repeat(
m_Animator.GetCurrentAnimatorStateInfo(0).normalizedTime + m_RunCycleLegOffset, 1);
float jumpLeg = (runCycle < k_Half ? 1 : -1) * m_ForwardAmount;
if (m_IsGrounded)
{
m_Animator.SetFloat("JumpLeg", jumpLeg);
}
// the anim speed multiplier allows the overall speed of walking/running to be tweaked in the inspector,
// which affects the movement speed because of the root motion.
if (m_IsGrounded && move.magnitude > 0)
{
m_Animator.speed = m_AnimSpeedMultiplier;
}
else
{
// don't use that while airborne
m_Animator.speed = 1;
}
}
void HandleAirborneMovement()
{
// apply extra gravity from multiplier:
Vector3 extraGravityForce = (Physics.gravity * m_GravityMultiplier) - Physics.gravity;
m_Rigidbody.AddForce(extraGravityForce);
m_GroundCheckDistance = m_Rigidbody.velocity.y < 0 ? m_OrigGroundCheckDistance : 0.01f;
}
void HandleGroundedMovement(bool crouch, bool jump)
{
// check whether conditions are right to allow a jump:
if (jump && !crouch && m_Animator.GetCurrentAnimatorStateInfo(0).IsName("Grounded"))
{
// jump!
m_Rigidbody.velocity = new Vector3(m_Rigidbody.velocity.x, m_JumpPower, m_Rigidbody.velocity.z);
m_IsGrounded = false;
m_Animator.applyRootMotion = false;
m_GroundCheckDistance = 0.1f;
}
}
void ApplyExtraTurnRotation()
{
// help the character turn faster (this is in addition to root rotation in the animation)
float turnSpeed = Mathf.Lerp(m_StationaryTurnSpeed, m_MovingTurnSpeed, m_ForwardAmount);
transform.Rotate(0, m_TurnAmount * turnSpeed * Time.deltaTime, 0);
}
public void OnAnimatorMove()
{
// we implement this function to override the default root motion.
// this allows us to modify the positional speed before it's applied.
if (m_IsGrounded && Time.deltaTime > 0)
{
Vector3 v = (m_Animator.deltaPosition * m_MoveSpeedMultiplier) / Time.deltaTime;
// we preserve the existing y part of the current velocity.
v.y = m_Rigidbody.velocity.y;
m_Rigidbody.velocity = v;
}
}
void CheckGroundStatus()
{
RaycastHit hitInfo;
#if UNITY_EDITOR
// helper to visualise the ground check ray in the scene view
Debug.DrawLine(transform.position + (Vector3.up * 0.1f), transform.position + (Vector3.up * 0.1f) + (Vector3.down * m_GroundCheckDistance));
#endif
// 0.1f is a small offset to start the ray from inside the character
// it is also good to note that the transform position in the sample assets is at the base of the character
if (Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out hitInfo, m_GroundCheckDistance))
{
m_GroundNormal = hitInfo.normal;
m_IsGrounded = true;
m_Animator.applyRootMotion = true;
}
else
{
m_IsGrounded = false;
m_GroundNormal = Vector3.up;
m_Animator.applyRootMotion = false;
}
}
}
}

Jumping at an angle with Unity and C#

I'm working on a project where I'm trying to make my character move by jumping at an angle. Right now during the frame updates, the character will pivot back and forth and when the right key is pressed, they will jump. This code causes them to jump at an angle, but they always return to their original position.
Additionally, I have two characters who start on opposite sides of the stage, but when I start the game they teleport to the same position. I've spent a lot of time reviewing my code but I can't seem to get this to work. Any help you can provide?
using UnityEngine;
using System.Collections;
public class Freg : MonoBehaviour {
public GameObject Tounge;
public float gravity;
public float tempScale = 1;
public float MaxJump = 8f;
public float MinJump = 0.1f;
static float yVector = 0;
static float xVector = 0;
static bool grounded = true;
bool isleft = false;
Vector3 farthestleft;
Vector3 farthestright;
// Use this for initialization
void Start () {
farthestleft = new Vector3 (-33.7f, 50.2f, 24.8f);
farthestright = new Vector3 (22.56f, 54.83f, -15.12f);
}
void OnTriggerEnter (Collider other) {
if (other.GetComponent<Collider> ().tag == "Ground") {
grounded = true;
yVector = 0;
//xVector = 0;
Vector3 onGround = new Vector3 (transform.position.x, -4.86f, transform.position.z);
transform.position = onGround;
} else
grounded = false;
}
// Update is called once per frame
void Update () {
/*if (Input.GetKey (KeyCode.UpArrow) == true) {
Tounge.transform.localScale.Set (1, 0.5f, 1);
} else {
Tounge.transform.localScale.Set (1, 1, 1);
}*/
if (grounded == false) {
yVector -= gravity;
}
if (Input.GetKeyDown (KeyCode.UpArrow) == true && grounded == true) {
MinJump += 0.5f;
} else if (MinJump > 0.1f){
yVector += MinJump;
xVector += MinJump;
MinJump = 0.1f;
grounded = false;
}
Vector3 stuff = new Vector3 (transform.localPosition.y + xVector, transform.position.y + yVector, transform.position.z);
transform.position = stuff;
float t = Mathf.PingPong (Time.time * 0.5f * 2.0f, 1.0f);
transform.eulerAngles = Vector3.Lerp (farthestright, farthestleft, t);
}
}
it looks like you should update the current position during the if statements, rather than after that way on each update, the actual position is moving based on the decision rather than just the end of the loop.

Categories

Resources