using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.Characters.ThirdPerson;
public class DistanceCheck : MonoBehaviour
{
public Transform target;
public float lerpDuration;
public float rotationSpeed;
public GameObject descriptionTextImage;
public TextMeshProUGUI text;
public ThirdPersonUserControl thirdPersonusercontrol;
private Animator anim;
private float timeElapsed = 0;
private float startValue = 1;
private float endValue = 0;
private float valueToLerp = 0;
private bool startRot = false;
// Start is called before the first frame update
void Start()
{
anim = transform.GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
if(startRot)
{
transform.rotation = Quaternion.RotateTowards(transform.rotation,
target.rotation, rotationSpeed * Time.deltaTime);
}
}
private void OnTriggerExit(Collider other)
{
if (other.name == "Colliding Area")
{
StartCoroutine(SlowDown());
}
}
IEnumerator SlowDown()
{
while (timeElapsed < lerpDuration)
{
valueToLerp = Mathf.Lerp(startValue, endValue, timeElapsed / lerpDuration);
anim.SetFloat("Forward", valueToLerp);
timeElapsed += Time.deltaTime;
yield return null;
}
thirdPersonusercontrol.enabled = false;
descriptionTextImage.SetActive(true);
yield return new WaitForSeconds(3f);
startRot = true;
}
}
I used a breakpoint and when startRot is true it does getting to the Quaternion.RotateTowards line in the Update and the rotationSpeed is 5 but the transform is not rotating at all.
I want that the transform will rotate by 180 degrees or what even angle the transform is looking at forward it should rotate back towards the target.
The transform and the target both facing same direction and still the transform is not rotating at all towards the target.
If i'm changing on my own the transform rotation at runtime it will rotate but when it's getting to the Update it's not rotating at all.
Quaternion.RotateTowards will change one rotation towards another one, if two objects are facing same direction, they have same rotation, so nothing will be changed.
If you want the object to face the target, you need use a rotation created by Quaternion.LookRotation.
transform.rotation = Quaternion.RotateTowards(transform.rotation,
Quaternion.LookRotation(target.position - transform.position),
rotationSpeed * Time.deltaTime);
Related
I have a player in unity with the movement controlled by a rigidbody. The movement on the Z axis is kept contstant by the game to keep the player moving forward. However, this means that the rigidbody keeps speeding up so its speed at the start of the game is much slower than the speed at the end of the game. Here is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
public class Controller : MonoBehaviour
{
[Tooltip("Rigidbody component attached to the player")]
public Rigidbody rb;
public float forwardMax;
public float slowBy;
private float movementX;
private float movementY;
private float gravity = -9.81f;
private bool isJumping = false;
private bool isSlowing = false;
private bool isSpeeding = false;
private float speedX = 100;
private float speedY = 150000;
private float speedZ = 60;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
// if(!controller.isGrounded)
// {
// Vector3 gravityVector = new Vector3(0.0f, gravity, 0.0f);
// rb.AddForce(gravityVector * Time.deltaTime);
// }
}
void OnCollisionEnter(Collision collision)
{
// SceneManager.LoadScene(1);
}
// Update is called once per frame
void OnMove(InputValue movementValue)
{
Vector2 movementVector = movementValue.Get<Vector2>();
movementX = movementVector.x;
movementY = movementVector.y;
}
void OnJump()
{
isJumping = true;
}
void CalculateMovement()
{
if(rb.velocity.z > 20)
{
rb.drag = 20;
}
else
{
rb.drag = 0;
}
Vector3 movement = new Vector3(movementX * speedX, 0.0f, speedZ);
if(isJumping)
{
movement.y += Mathf.Sqrt(speedY * -3.0f * gravity);
isJumping = false;
}
rb.AddForce(movement);
Debug.Log("Speed is " + rb.velocity.z);
}
void FixedUpdate()
{
CalculateMovement();
}
}
Is there a way to keep the forward velocity constant? The problem is worse when the player jumps.
First I tried clamping the forward (z-axis) vector but that had no effect. Then, I tried adding a backward vector onto the total when the forward velocity was above a certain number but this led to it speeding up and slowing down all the time. Then I tried the same thing with the drag on the rigidbody but that had the same effect.
You can use rigidbody.velocity and set it to constant value or whatever you want instead of adding force. By adding force, your character's speed increases.
Also you can use AddForce but you have to tune the force value dynamically according to the current velocity.
rigidbody velocity
How about directly setting the z value you want at the end of FixedUpdate() ?
......
void FixedUpdate()
{
CalculateMovementWithoutZMovement();
rb.velocity = new Vector3 (rb.velocity.x, rb.velocity.y, ConstantZValue);
}
we are working on a student project and we chose to do a Mario Galaxy style platformer with planetoids and gravity (kind of a big mistake for my first coding project but I cannot back out of it now) but I am having a hard time to get the character to face it's movement direction without absolutely spazzing out.
I have only been coding for around 2 months so please excuse me being useless at trying to figure this out.
This is the code I use for movement for the character
using System.Collections.Generic;
using UnityEngine;
public class SC_RigidbodyWalker : MonoBehaviour
{
public float speed = 5.0f;
public bool canJump = true;
public float jumpHeight = 2.0f;
public Camera playerCamera;
public float lookSpeed = 2.0f;
public float lookXLimit = 60.0f;
bool grounded = false;
Rigidbody r;
Vector2 rotation = Vector2.zero;
float maxVelocityChange = 10.0f;
void Awake()
{
r = GetComponent<Rigidbody>();
r.freezeRotation = true;
r.useGravity = false;
r.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
rotation.y = transform.eulerAngles.y;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void Update()
{
}
void FixedUpdate()
{
if (grounded)
{
// Calculate how fast we should be moving
Vector3 forwardDir = Vector3.Cross(transform.up, -playerCamera.transform.right).normalized;
Vector3 rightDir = Vector3.Cross(transform.up, playerCamera.transform.forward).normalized;
Vector3 targetVelocity = (forwardDir * Input.GetAxis("Vertical") + rightDir * Input.GetAxis("Horizontal")) * speed;
Vector3 velocity = transform.InverseTransformDirection(r.velocity);
velocity.y = 0;
velocity = transform.TransformDirection(velocity);
Vector3 velocityChange = transform.InverseTransformDirection(targetVelocity - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0;
velocityChange = transform.TransformDirection(velocityChange);
r.AddForce(velocityChange, ForceMode.VelocityChange);
if (Input.GetButton("Jump") && canJump)
{
r.AddForce(transform.up * jumpHeight, ForceMode.VelocityChange);
}
}
grounded = false;
}
void OnCollisionStay()
{
grounded = true;
}
}
And here are the code for the gravity functions
using System.Collections.Generic;
using UnityEngine;
public class SC_PlanetGravity : MonoBehaviour
{
public Transform planet;
public bool alignToPlanet = true;
float gravityConstant = 9.8f;
Rigidbody r;
void Start()
{
r = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
Vector3 toCenter = planet.position - transform.position;
toCenter.Normalize();
r.AddForce(toCenter * gravityConstant, ForceMode.Acceleration);
if (alignToPlanet)
{
Quaternion q = Quaternion.FromToRotation(transform.up, -toCenter);
q = q * transform.rotation;
transform.rotation = Quaternion.Slerp(transform.rotation, q, 1);
}
}
}
I can propose an alternative approach which I believe will simplify the problem, should you choose. If the root of your character is positioned at the center of the planetoid, then all movement can be handled as a rotation on root and you won't be fighting the inertia of the character or needing to orient it to the planetoid. Jumping could be handled by adding another child below the root that can slide up and down. Hope this helps!
I managed to get it working by having a new script added to the player avatar.
Here is the code, it's basically a copy paste of a part of the RigidBodyWalker script with some more added parts. It's not perfect but it works like I wanted it to.
using System.Collections.Generic;
using UnityEngine;
public class InputRotation : MonoBehaviour
{
public Camera playerCamera;
public float speed;
public float rotationSpeed;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//float horizontalInput = Input.GetAxis("Horizontal");
//float verticalInput = Input.GetAxis("Vertical");
}
private void FixedUpdate()
{
Vector3 forwardDir = Vector3.Cross(transform.up, -playerCamera.transform.right).normalized;
Vector3 rightDir = Vector3.Cross(transform.up, playerCamera.transform.forward).normalized;
Vector3 targetVelocity = (forwardDir * Input.GetAxis("Vertical") + rightDir * Input.GetAxis("Horizontal")) * speed;
if(targetVelocity != Vector3.zero)
{
Quaternion toRotation = Quaternion.LookRotation(targetVelocity, transform.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
}
}
}
I have a Player (made out of an cylinder, some FirstCam, SecondCam and ThirdCam, for switching views when pressing a certain key - doesn't matter), which is located on a cube (wide enough to take a walk on it). There's nothing else below the cube.
This is how it looks like:
This is the Player's structure:
This is the inspector for Player:
I have the following script ResetToInitial.cs, attached to my Player:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ResetToInitial : MonoBehaviour {
Vector3 pos;
void Start() {
pos = transform.position;
}
void Update() {
if (transform.position.y < -10) {
transform.position = pos;
}
}
}
and also some movement script PlayerMovement.cs that allows him to move up, down, left, right, jump and sprint.
This is PlayerMovement.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class PlayerMovement : MonoBehaviour {
public CharacterController controller;
Vector3 velocity;
bool isGrounded;
public Transform groundCheck;
public LayerMask groundMask;
public TextMeshPro State;
public TextMeshPro State2;
public TextMeshPro State3;
// Start is called before the first frame update
void Start() {
}
// Update is called once per frame
void Update() {
float groundDistance = 0.4f;
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0) {
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
float g = -20f;
float jumpHeight = 1f;
float speed = 6f;
if (Input.GetKey(KeyCode.LeftShift)) {
speed = 8f;
State.text = "running mode";
State2.text = "running mode";
State3.text = "running mode";
} else {
speed = 5f;
State.text = "walking mode";
State2.text = "walking mode";
State3.text = "walking mode";
}
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
if (Input.GetButton("Jump") && isGrounded) {
velocity.y = Mathf.Sqrt(jumpHeight * -2f * g);
}
velocity.y += g * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
ResetToInitial.cs is intended to teleport the Player to the initial position (back on the cube, the position recorded when the scene loads), after he falls of the cube. The problem is transform.position won't move the player anywhere. It won't do anything. I tried to output something in the last if to see if it works, and the output was printed, but still no teleportation happened.
What could be the problem?
I am trying to have my object smoothly rotate on the Z axis from 0 to 180, back and forth on every button press.
The Quaternion.Slerp setup i'm using does not smoothly rotate to the target rotation, rather it instantly jumps to a number close to it. After many button presses that call the Quaternion.Slerp, it it finally halfs works in that it goes from 0 to 180, but still instantly and not smoothly.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float moveSpeed;
public bool movePlayer;
private float maxMoveSpeed;
public float waitToMove;
Rigidbody2D thisRigidbody;
//SCOOP variables
private GameObject scoop;
private GameObject scoopRotation;
Quaternion currentScoopRotation;
public Quaternion targetScoopRotation;
Quaternion lastScoopRotation;
public float scoopRotateTime;
// Use this for initialization
void Start ()
{
thisRigidbody = gameObject.GetComponent<Rigidbody2D> ();
maxMoveSpeed = moveSpeed;
//SCOOP finders
scoop = GameObject.FindGameObjectWithTag ("Scoop");
currentScoopRotation = scoop.transform.rotation;
}
// Update is called once per frame
void Update ()
{
if(movePlayer)
thisRigidbody.velocity = new Vector2 (moveSpeed, thisRigidbody.velocity.y);
//Rotates SCOOP using Quaternions + Spacebar
if (Input.GetKeyDown(KeyCode.Space)) {
lastScoopRotation = currentScoopRotation;
scoop.transform.transform.rotation = Quaternion.Lerp (currentScoopRotation, targetScoopRotation, scoopRotateTime * Time.time);
currentScoopRotation = targetScoopRotation;
targetScoopRotation = lastScoopRotation;
changeMoveDirection ();
}
}
void changeMoveDirection()
{
moveSpeed *= -1;
}
IEnumerator changeDirectionCO()
{
//thisRigidbody.velocity = new Vector2 (0f,0f);
//moveSpeed = 0;
yield return new WaitForSeconds (waitToMove);
moveSpeed *= -1;
}
}
Got stuck with camera movement in my Unity3D C# project.
What I have:
some objects on the scene
a camera, which would fly from any object's poition or from it's own current position
What I need:
smooth rotare camera to one of the object's origin
fly to the spot near the object (there is an empty, so I fly to empty's coords)
Algorithm: rotate to the object's origin, when rotation finishes, start to fly to the empty's position. while flying, look at object's origin.
The problem is that it's not smooth, the camera "jumps" at the end of the movement.
My C# code (attached to the camera):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class testMove : MonoBehaviour {
public GameObject startObj;
public GameObject endObj;
public float speed = 1.0F;
private float startTime;
private float journeyLength;
private string endObjName;
private GameObject endObjLookAt;
void Start () {
startTime = Time.time;
if (startObj) {
} else {
startObj = this.gameObject;
}
journeyLength = Vector3.Distance(startObj.transform.position, endObj.transform.position);
endObjName = endObj.name;
endObjLookAt = GameObject.Find(endObjName + "LookAt");
}
void Update () {
if (endObj) {
float distCovered = (Time.time - startTime) * speed;
float fracJourney = distCovered / journeyLength;
tweenLook(endObjLookAt, fracJourney);
float angle = Quaternion.Angle(transform.rotation, Quaternion.LookRotation(endObjLookAt.transform.position - transform.position));
if (angle <= 0.0001) {
Debug.Log("rotation finished");
tweenPos(startObj, endObj, fracJourney);
transform.LookAt(endObjLookAt.transform.position);
}
}
}
private void tweenPos(GameObject startObj, GameObject endObj, float fracJourney) {
Vector3 newposition = Vector3.Lerp(startObj.transform.position, endObj.transform.position, fracJourney);
transform.position = newposition;
}
private void tweenLook(GameObject endObjLookAt, float fracJourney) {
Quaternion newrotation = Quaternion.LookRotation(endObjLookAt.transform.position - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, newrotation, fracJourney);
}
}
Since what you're trying to achieve implies doing actions one after another, I'd recommend using a Coroutine:
public class testMove : MonoBehaviour
{
public Transform startObj;
public Transform endObj;
private Transform endObjLookAt;
public float rotationDuration;
public AnimationCurve rotationCurve;
public float movementDuration;
public AnimationCurve movementCurve;
private IEnumerator moveAndRotateCameraIEnumerator;
void Start()
{
// If you want to do it on start just call MoveAndRotateCamera() here, else call if from anywhere you want (a script, a game button, ...)
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
MoveAndRotateCamera();
}
}
public void MoveAndRotateCamera(Transform startTransform = null, Transform endTransform = null)
{
if(startTransform)
{
startObj = startTransform;
}
else
{
startObj = this.transform;
}
if(endTransform)
{
endObj = endTransform;
}
endObjLookAt = GameObject.Find(endObj.name + "LookAt").transform;
if(moveAndRotateCameraIEnumerator != null)
{
StopCoroutine(moveAndRotateCameraIEnumerator);
}
moveAndRotateCameraIEnumerator = MoveAndRotateCameraCoroutine();
StartCoroutine(moveAndRotateCameraIEnumerator);
}
private IEnumerator MoveAndRotateCameraCoroutine()
{
//ROTATION
Vector3 startEulerAngles = transform.eulerAngles;
transform.LookAt(endObjLookAt);
Vector3 deltaEulerAngles = new Vector3(Mathf.DeltaAngle(startEulerAngles.x, transform.eulerAngles.x), Mathf.DeltaAngle(startEulerAngles.y, transform.eulerAngles.y), Mathf.DeltaAngle(startEulerAngles.z, transform.eulerAngles.z));
Debug.Log("Starting rotation...");
float timer = 0.0f;
while(timer < rotationDuration)
{
timer += Time.deltaTime;
transform.eulerAngles = startEulerAngles + deltaEulerAngles * rotationCurve.Evaluate(timer / rotationDuration);
yield return new WaitForEndOfFrame();
}
transform.eulerAngles = startEulerAngles + deltaEulerAngles;
Debug.Log("Rotation done!");
//----
//MOVEMENT
Vector3 startPosition = transform.position;
Debug.Log("Starting movement...");
timer = 0.0f;
while(timer < movementDuration)
{
timer += Time.deltaTime;
transform.position = Vector3.Lerp(startPosition, endObj.position, movementCurve.Evaluate(timer / movementDuration));
transform.LookAt(endObjLookAt);
yield return new WaitForEndOfFrame();
}
transform.position = endObj.position;
transform.LookAt(endObjLookAt);
Debug.Log("Movement done!");
//----
}
}
Please note a few things here:
Changed your GameObject variables to Transform ones because you always used them to get to the Transform component so you can use it directly instead
Added a time notion instead of a speed notion for rotation and movement (you can also use speed instead: simply multiply the Time.deltaTime by your speed factor)
Using AnimationCurve allows you to adjust the way the rotation/movement will occur: simply set the curve in Inspector (curve must start at (0, 0) and end at (1, 1))
Hope this helps,