I have an object in my scene that move forward and rotate with input.GetAxis, and I want to limit its X rotation between -45 and 45 degree. So I tried the Clamp method but the object can't rotate anymore! is there something wrong in my code?
float Speed = 10f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
transform.Rotate(-Input.GetAxis("Vertical") * 2f, 0, -Input.GetAxis("Horizontal"));
float rotationX = Mathf.Clamp(transform.rotation.x, -45.0f, 45.0f);
transform.localEulerAngles = new Vector3(rotationX, transform.localEulerAngles.y, transform.localEulerAngles.z);
}
private void FixedUpdate()
{
transform.position += transform.forward * Speed * Time.fixedDeltaTime;
}
Personally I wouldn't try setting localEulerAngles directly, I'd reset the rotation to Quaternion.Identity (no rotation) or to another reference transform's rotation and then use Transform.rotate rotationX degrees around the desired axis whenever the rotation changes
The main issue your having atm is transform.rotation is a Quaternion and not a vector3. Since Quaternions have a min/max value of -1/1 the vector3 X rotation can't ever be outside that range.
You probably wanted to do: float rotationX = Mathf.Clamp(transform.localEulerAngles.x, -45.0f, 45.0f);
Related
How to limit my ai movement to move only on the z axis. Ive already tried freezing the rotation and position on it's rigidbody but instead when i jump my ai also goes up with me on it's position and slightly rotates towards me.
private void Update()
{
StopFollowing();
Vector3 relativePos = target.position - transform.position;
Quaternion rotation = Quaternion.LookRotation(relativePos);
Quaternion current = transform.localRotation;
transform.localRotation = Quaternion.Slerp(current, rotation, Time.deltaTime
* LookSpeed);
}
// Update is called once per frame
void followPlayer()
{
Vector3 pos = Vector3.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
rig.MovePosition(pos);
;
}
Freezing the rigidbody will only affect physics (using AddForce or rb.velocity)
You are using MoveTowards (basically teleporting it ignoring physics)
But you can limit it to Z-Axis manually:
void followPlayer()
{
Vector3 targetPos = target.position; // copy target Position
targetPos.x = transform.position.x; // keep x
targetPos.y = transform.position.y; // keep y
Vector3 pos = Vector3.MoveTowards(transform.position, targetPos, speed * Time.deltaTime); // now only considers z-difference
rig.MovePosition(pos);
}
You basically modify the target Position to be equal on the x and y axis, so that the MoveTowards only uses Z effectively.
I'm making top down shooting game. I wrote the code where enemies spawn randomly on map and they're trying to catch you. I made them do that and also I wrote a code to make them look at you. Basically rotate towards you only on Z axis. But problem is that when they are spawned on players' right, enemy is moving away from player. but if I rotate and start to move they are trying to fix themselves. Here's my script:
void FixedUpdate () {
Vector3 difference = player.position - transform.position;
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0.0f, 0.0f, rotationZ);
Vector2 toTarget = player.transform.position - transform.position;
float speed = 1.5f;
transform.Translate(toTarget * speed * Time.deltaTime);
}
Consider that Translate is a relative modifier. For this reason, when you specify the direction in the Translate itself, the movement becomes confused. Use Vector3.MoveTowards to solve the problem. If your game is 2D, you can also use Vector2 like below:
Vector2.MoveTowards(currentPosition, targetPosition, step);
Preferably you can fix this code like this and set the return value of MoveTowards equal to transform.Position.
void FixedUpdate () {
Vector3 difference = player.position - transform.position;
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0.0f, 0.0f, rotationZ);
float speed = 1.5f;
// replace Translate to MoveTowards
transform.position = Vector3.MoveTowards(transform.position, player.position, Time.deltaTime * speed);
}
private float rotationZ;
Vector3 difference = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
difference.Normalize();
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rotationZ);
So far my code will get the cursor position and instantly perform a transform. However, if I move the cursor near the center of the pivot, the rotation will be instantaneous and choppy. How can I add a slight smooth to this, to make it travel from point A to point B slower?
You would store the actual target value in a field and then move towards it
private Quaternion targetRotation;
private Camera camera;
private void Awake ()
{
// Camera.main is quite expensive so rather store the reference
camera = Camera.main;
// Initially set your current rotation as target
// especially important if you don't always update the target rotation but e.g.
// only if a certain button is pressed
targetRotation = transform.rotation;
}
private void Update ()
{
UpdateTargetRotation();
RotateTowardsTarget();
}
private void UpdateTargetRotation ()
{
var difference = (camera.ScreenToWorldPoint(Input.mousePosition) - transform.position).normalized;
var targetRotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
targetRotation = Quaternion.Euler(0, 0, targetRotationZ);
}
Now for the RotateTowardsTarget you have many options.
One of them would indeed be using Quaternion.Lerp with a certain interpolation factor. Here you interpolate smoothly towards the target value but very fast at the beginning and get slower and slower the closer you get to the target value regardless of how much you rotate in total. This also might never really reach the exact target value.
// Adjust in the Inspector
public float interpolationFactor = 5f;
private void RotateTowardsTarget ()
{
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, interpolationFactor * Time.deltaTime);
}
Or another option would be to rather use Quaternion.RotateTowards which rotates with a fixed speed
// Adjust in the Inspector
public float anglePerSecond = 45;
private void RotateTowardsTarget ()
{
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, anglePerSecond * Time.deltaTime);
}
You're looking for Mathf.Lerp
Example of how you could use it:
float rotationZ;
//Changes how quickly will rotate
const float rotationSpeed = 0.58f;
Vector3 difference = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
difference.Normalize();
rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
float lerpRotation = Mathf.Lerp(transform.rotation.z, rotationZ, Time.deltaTime * rotationSpeed);
transform.rotation = Quaternion.Euler(0f, 0f, lerpRotation);
You should use Quaternion.Slerp() in a coroutine.
You check the cursor position every FixedUpdate then start a coroutine that interpolates and sets the rotation every frame until the next FixedUpdate comes. The created lag is basicly unnoticable and it looks silky smooth.
For the interpolation t value you should use a variable that stores the time elapsed since the last FixedUpdate (you add that up from time.deltatimes), and to get the t you just divide it by 0.02secs.
Then when the coroutin finishes, set the rotation to the final value.
I made a circle and attach a lazer box on top of it.
The lazer will fire a raycast to its upper y axis (straight up). I also add a line renderer to view it.
I want the raycast to rotate 90 degrees back and forth. Sort of like its scanning everything on top. My problem is that its not working properly. It does rotate back and forth but If I move the x position of the lazer object, the raycast will rotate in a weird angle.
Script for lazer object
public LineRenderer lineRenderer;
public LayerMask layerMask;
public float laserSpeed;
Vector3 pointA;
Vector3 pointB;
Vector3 castPosition;
RaycastHit2D rayCast;
float time;
void Start()
{
pointA = transform.eulerAngles + new Vector3(0f, 0f, 90f);
pointB = transform.eulerAngles + new Vector3(0f, 0f, -90f);
}
void Update()
{
time = Mathf.PingPong(Time.time * laserSpeed, 1);
transform.eulerAngles = Vector3.Lerp(pointA, pointB, time);
castPosition = new Vector3(transform.position.x, transform.position.y, transform.position.z);
rayCast = Physics2D.Raycast(castPosition, transform.TransformDirection(Vector2.up), 10f, layerMask);
lineRenderer.SetPosition(0, castPosition);
lineRenderer.SetPosition(1, transform.TransformDirection(Vector2.up) * 10f);
}
Using eulerAngles for continuous animations is quite "dangerous". Unity stores the rotations as Quaternion and there are multiple ways of how to represent these in euler space!
When you read the .eulerAngles property, Unity converts the Quaternion's internal representation of the rotation to Euler angles. Because, there is more than one way to represent any given rotation using Euler angles, the values you read back out may be quite different from the values you assigned. This can cause confusion if you are trying to gradually increment the values to produce animation.
To avoid these kinds of problems, the recommended way to work with rotations is to avoid relying on consistent results when reading .eulerAngles particularly when attempting to gradually increment a rotation to produce animation. For better ways to achieve this, see the Quaternion * operator.
so you should rather go for Quaternion and do e.g.
And then you are using transform.TransformDirection(Vector2.up) which is a direction and pass it to your line renderer as a position.
What you want there is rather the position combined from
transform.position + transform.up
So together it should probably rather be
public LineRenderer lineRenderer;
public LayerMask layerMask;
public float laserSpeed;
private Quaternion originalRotation;
private Quaternion minRotation;
private Quaternion maxRotation;
void Start()
{
originalRotation = transform.rotation;
minRotation = originalRotation * Quaternion.Euler(0, 0, -90);
maxRotation = originalRotation * Quaternion.Euler(0, 0, 90);
}
void Update()
{
// Note that Vector3 is a "struct" -> there is no need to manually use "new Vector3(transform.position.x, ...)"
var startPosition = transform.position;
lineRenderer.SetPosition(0, startPosition);
var factor = Mathf.PingPong(Time.time * laserSpeed, 1);
// instead of the eulers rather use Quaternion
transform.rotation = Quaternion.Lerp(minRotation, maxRotation, factor);
// "transform.up" basically equals using "transform.TransformDirection(Vector3.up)"
var rayCast = Physics2D.Raycast(startPosition, transform.up, 10f, layerMask);
if(rayCast.collider)
{
// when you hit something actually use this hit position as the end point for the line
lineRenderer.SetPosition(1, rayCast.point);
}
else
{
// otherwise from the start position go 10 units in the up direction of your rotated object
lineRenderer.SetPosition(1, startPosition + transform.up * 10f);
}
}
I want to rotate camera around an fbx object when a key is being pressed using unity 3d.How it do? I tried some examples but its not working. First i create a game object and add main camera child of it.
public class CameraOrbit : MonoBehaviour
{
public Transform target;
public float speed = 1f;
private float distance;
private float currentAngle = 0;
void Start()
{
distance = (new Vector3(transform.position.x, 0, transform.position.z)).magnitude;
}
void Update()
{
currentAngle += Input.GetAxis("Horizontal") * speed * Time.deltaTime;
Quaternion q = Quaternion.Euler(0, currentAngle, 0);
Vector3 direction = q * Vector3.forward;
transform.position = target.position - direction * distance + new Vector3(0, transform.position.y, 0);
transform.LookAt(target.position);
}
}
I dont have access to unity at the moment so i might have messed something up.
The idea is keep an angle that you change based on input. Create a Quaternion from the angle (the Quaternion say how to rotate a vector to a certain direction), then rotate a Vector to that direction. Starting from the targets position move in that direction a certain distance and then look at the targets position.
This only implements rotation around the y axis, if you want rotation around the x axis all you need is another angle variable and then change to this Quaternion.Euler(currentAngleX, currentAngleY, 0);