I am working on a 3D Camera controller, where player is able to rotate the camera around an object using mouse. transform.RotateAround() is perfect for this, except for one thing - I can't figure out a way to smoothly lerp it.
For reference - this is old code, which made camera rotate smoothly, but without a specific Target, just around an arbitrary point:
private void Rotate(){
if(!EnableRotation || RotationTarget == null) return;
if(Input.GetKeyDown(KeyCode.R)) _newRotation = Quaternion.identity;
if(Input.GetKey(KeyCode.Mouse1)){
float xInput = Input.GetAxis("Mouse X");
_newRotation *= Quaternion.Euler(Vector3.up * RotationSpeed * xInput * RotationSensitivity);
}
}
private void UpdateRotation(){
transform.rotation = Quaternion.Lerp(transform.rotation,_newRotation,Time.deltaTime * RotationDampening);
}
And this, is what I have currently:
private void Rotate(){
if(!EnableRotation || RotationTarget == null) return;
if(Input.GetKeyDown(KeyCode.R)) _newRotation = Quaternion.identity;
if(Input.GetKey(KeyCode.Mouse1)){
float xInput = Input.GetAxis("Mouse X");
transform.RotateAround(RotationTarget.position,Vector3.up,RotationSpeed * xInput);
}
}
So my big question is - is there a way to lerp the rotation of transform.RotateAround()? Make it smooth. Or are there any other ways of doing this?
Thanks in advance.
I feel like I've tried everything, but no success so far.
Where is Rotate() called? Is the rotationSpeed * xInput meant to represent degrees as required?
I figure first the UpdateRotation() method was called frequently, while the Rotate() method was called to set the new rotation, in the new version you do the rotation on Rotate() are you calling it as often as you did with the UpdateRotation()?.
You could Lerp 360 degrees such as Vector3.Lerp(0, 360, t) where t should range from 0 to 1. Or simply adjust the rotation speed accordingly, making sure you call the method enough times to have a smooth rotation.
Related
Good times. How do I implement the NPC rotation towards the selected object?
public GameObject BufferObject;
public float MoveSpeed = 1f;
void Update()
{
float step = MoveSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, BufferObject.transform.position, step);
}
Here is the script for moving the NPC to the selected object (Buffer Object) and the movement works perfectly, but the implementation of the rotation causes Me difficulties, please advise.
For Unity2D.
Simply get the desired direction
var direction = (BufferObject.transform.position - transform.position).normalized;
and then the desired rotation using Quaternion.LookRotation like e.g.
var targetRotation = Quaternion.LookDirection(direction);
Then either apply it directly if you want it immediately
transform.rotation = targetRotation;
Or if you want it smooth you could use e.g. Quaternion.RotateTowards like
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, anglePerSecond * Time.deltatime);
Have in mind though that it might look awkward if the rotation is too slow since your NPC could move back/sidewards already while still not fully looking towards the target ;)
So you might want to wait until
if(Quaternion.Angle(targetRotation, transform.rotation) <= certainThreshold)
{
... your movement code
}
So the answer and the solution from Me, albeit stupid, but working. In order to reflect the sprite, you need to get a variable, either 1 or -1 (For Scale). This code will show the distance from one NPC to the object:
BufferObject.transform.position.x - transform.position.x
And here I get a lot of values as if to the left of the NPC, then -x... , and if to the right, then x... thereby it is possible to determine which side of the object, so also level the value from here (Optional) to 1 or -1 and apply this result to transform.localScale and thereby solve the problem of how to reflect (Rotate the sprite) towards the object. Use it for your health :)
Complete code:
float localPositionAmount = BufferObject.transform.position.x - transform.position.x;
if (localPositionAmount >= 1)
{
gameObject.transform.localScale = new Vector3(1, transform.localScale.y, transform.localScale.z);
}
if (localPositionAmount <= -1)
{
gameObject.transform.localScale = new Vector3(-1, transform.localScale.y, transform.localScale.z);
}
if (localPositionAmount == 0)
{
gameObject.transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y, transform.localScale.z);
}
Yes, the code is the simplest and without enumerations and other things, but it works perfectly. I still had to figure it out myself...
Im trying to code so that my Character dashes to the right when pressing the Left mouse button, but instead of dashing it just starts slowly glieding or lets say floating.
This is the code i´ve used;
if (Input.GetMouseButton(0))
{
rb.velocity = Vector2.right * DashSpeed;
}
Im not sure but a other part of my code might be the reason for this problem but if so i would like to know how i could solve it. Thats the part im talking about
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
thats the code im using for movement.
void Start()
{
cam = Camera.main;
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
horizontal = Input.GetAxisRaw("Horizontal");
animator.SetFloat("Horizontal", Input.GetAxis("Horizontal"));
if (Input.GetKeyDown(KeyCode.Space) && isGrounded == true)
{
float jumpVelocity = 7f;
rb.velocity = Vector2.up * jumpVelocity;
jumpsound.Play();
}
Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (Input.GetKey(KeyCode.RightAlt))
{
Dashing();
}
}
void FixedUpdate()
{
isGrounded = Physics2D.OverlapCircle(groundCheck.position, CheckRadius, whatisGround);
moveInput = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
if (Input.GetKeyDown(KeyCode.Escape))
{
SceneManager.LoadScene("Main menu");
}
}
void Dashing()
{
rb.AddForce(Vector2.right * DashSpeed, ForceMode2D.Impulse);
}
The issue with your current code is you are directly changing velocity in a few places. Some basic physics, the integral of position vs. time graph is velocity, and the integral of velocity is acceleration vs. time. To get a more realistic movement, it is better to apply a Force to objects. When doing this, the physics engine Unity uses can add a new force at a given time, then using acceleration can accelerate the object in that direction over time, then can change the velocity over time which will result in the position changing over time.
The example code you posted, you are directly setting velocity in a few places.
rb.velocity = Vector2.up * jumpVelocity; (Jump)
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y); (Movement)
rb.velocity = Vector2.right * DashSpeed; (Dash)
When directly setting these values, the new values overwrite the old ones. As your movement code is not in any sort of if conditional it will continually write to the velocity causing the dash to never change anything regardless if you use add-force or change velocity directly.
I would consider making both your jump and dash use AddForce, and if you like the feel of your movement by applying velocity directly, then add the velocity do not set it.
Your previous line rb.velocity = new Vector2(moveInput * speed, rb.velocity.y); would then become rb.AddForce(new Vector2(moveInput * speed, 0), ForceMode2D.Impulse);. Similarly you can update your jump and dash to match this. Let me know if you get this working or have more questions.
It could be a problem with your animation. Link to a thread on unity answers:
https://answers.unity.com/questions/674516/rigidbody-character-problems-constant-floating-jum.html
You should go over to the animation place and hit bake into pose.
You should use the Rigidbody2D.AddForce(Vector2, ForceMode2D). What this does is moving the GameObject in the direction of the Vector2, with the force mode of ForceMode2D. What is different about this from just translating it, is that it interacts with physics and improves the quality of your game. Here is a link, and the script:
https://docs.unity3d.com/ScriptReference/Rigidbody2D.AddForce.html
Rigidbody2D rb;
float dashSpeed;
void Update()
{
if (Input.GetMouseButton(0))
{
rb.AddForce(Vector2.right * dashSpeed);
}
}
And if the other part of the code you were talking about, if that is glitching, then do the same trick.
My character is a car and I try to rotate it the direction it move, so far so good I succeeded to do that but once I stop moving the character flips back to the direction it was on the start.
Also how can I make my turns from side to the opposite site smooth ?
Here is my code so far:
[SerializeField] float driveSpeed = 5f;
//state
Rigidbody2D myRigidbody;
// Start is called before the first frame update
void Start()
{
myRigidbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
Move();
}
private void Move()
{
//Control of velocity of the car
float HorizontalcontrolThrow = CrossPlatformInputManager.GetAxis("Horizontal"); // Value between -1 to 1
float VerticalcontrolThrow = CrossPlatformInputManager.GetAxis("Vertical"); // Value between -1 to 1
Vector2 playerVelocity = new Vector2(HorizontalcontrolThrow * driveSpeed, VerticalcontrolThrow * driveSpeed);
myRigidbody.velocity =playerVelocity;
**//Direction of the car**
Vector2 direction = new Vector2(HorizontalcontrolThrow, VerticalcontrolThrow);
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
myRigidbody.rotation = angle;
}
I'm not sure about this, but maybe that last line "myRigidbody.rotation = angle" being called every frame is what is making your car reset its rotation.
Maybe change it to "myRigidbody.rotation *= angle" or "myRigidbody.rotation += angle".
It looks like it may be because HorizontalcontrolThrow and VerticalcontrolThrow are going to be reset when you release the controls. If it's resetting to its original orientation, then what's happening is that until you move, those two values are going to be at their default value. You then move and it affects the rotation. But when you release the controls, those values are back to the starting values again, and so is your rotation.
What you therefore need to do is try to separate the HorizontalcontrolThrow and VerticalcontrolThrow from the rest of the code, which should only be activated when at least one of these two variables are not at their default setting (I can't remember what the axis functions return at the moment).
Edit:
An IF statement should suffice (some rough pseudo code):
if (horizontalAxis != default || verticalAxis != default)
{
Rotate/Move
}
I solved the snap rotation using Quaternion at rotation, the issiu I had with it was to convert it from 3d to 2d, through the guide of this clip: youtube.com/watch?v=mKLp-2iseDc and made my adjustments it works just fine !
In my scenario, I have a table (plane) that a ball will roll around on using nothing but physics giving the illusion that the mobile device is the table using Input.gyro.attitude. Taking it one step further, I would like this relative to the device origin at the time Start() is called, so if it is not being held in front of a face or flat on the table, but just relative to where it started, and may even be reset when the ball is reset. So the question is, is how do I get the difference between the current attitude and the origin attitude, then convert the X and Z(?) difference into a Vector3 to AddForce() to my ball object whilst capping the max rotation at about 30 degrees? I've looked into a lot of Gyro based input manager scripts and nothing really helps me understand the mystery of Quaternions.
I could use the relative rotation to rotate the table itself, but then I am dealing with the problem of rotating the camera along the same rotation, but also following the ball at a relative height but now with a tilted offset.
AddForce() works well for my purposes with Input.GetAxis in the Editor, just trying to transition it to the device without using a Joystick style UI controller.
Edit: The following code is working, but I don't have the right angles/euler to give the right direction. The game is played in Landscape Left/Right only, so I should only need a pitch and yaw axis (imagine the phone flat on a table), but not roll (rotated around the camera/screen). I may eventually answer my own question through trial and error, which I am sure is what most programmers do.... XD
Started on the right track through this answer:
Answer 434096
private Gyroscope m_Gyro;
private speedForce = 3.0f;
private Rigidbody rb;
private void Start() {
m_Gyro = Input.gyro;
m_Gyro.enabled = true;
rb = GetComponent<Rigidbody>();
}
private Vector3 GetGyroForces() {
Vector3 resultantForce = Vector3.zero;
//Quaternion deviceRotation = new Quaternion(0.5f, 0.5f, -0.5f, -0.5f) * m_Gyro.attitude * new Quaternion(0,1,0,0);
float xForce = GetAngleByDeviceAxis(Vector3.up);
float zForce = GetAngleByDeviceAxis(Vector3.right);
//float zForce = diffRot.z;
resultantForce = new Vector3(xForce, 0, zForce);
return resultantForce;
}
private float GetAngleByDeviceAxis(Vector3 axis) {
Quaternion currentRotation = m_Gyro.attitude;
Quaternion eliminationOfOthers = Quaternion.Inverse(Quaternion.FromToRotation(axis, currentRotation * axis));
Vector3 filteredEuler = (eliminationOfOthers * currentRotation).eulerAngles;
float result = filteredEuler.z;
if (axis == Vector3.up) {
result = filteredEuler.y;
}
if (axis == Vector3.right) {
result = (filteredEuler.y > 90 && filteredEuler.y < 270) ? 180 - filteredEuler.x : filteredEuler.x;
}
return result;
}
void FixedUpdate() {
#if UNITY_EDITOR
rb.AddForce(new Vector3(Input.GetHorizontal * speedForce, 0, Input.GetVertical * speedForce));
#endif
#if UNITY_ANDROID
rb.AddForce(GetGyroForces);
#endif
}
I am working on a first person game where the character is able to latch on to any flat surface and walk on it (kind of like moon boots). I am having issues with the camera since most implementations of mouse look I have been able to find depend on the player being oriented straight up and down in the world, so as soon as my "flipping" animation (the player orienting to the new surface) has finished, the camera will instantly flip back to straight up and down as defined by the world. What I need is a way to implement mouse look so that it does not reset the rotation after my animation. I am currently using:
transform.Rotate(-Input.getAxis("Mouse Y") turnSpeed Time.deltaTime, Input.getAxis("Mouse X") turnSpeed Time.deltaTime, 0);
in order to perform my mouse look rotations while avoiding the standard method, but this is clearly incorrect since I get some weird rotations when turning the camera horizontally. Is there a better way to implement mouse look so that it works correctly regardless of the orientation of the player?
You can plance camera inside player Transform and add this script to player:
public class MouseLook : MonoBehaviour {
[SerializeField]
Camera Camera;
[Range(10f,50f)]
public float Speed = 30;
void Update () {
transform.rotation = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * speed * Time.deltaTime, transform.rotation * Vector3.up)*transform.rotation;
Camera.transform.rotation = Quaternion.AngleAxis(-Input.GetAxis("Mouse Y") * Speed * Time.deltaTime, Camera.transform.rotation * Vector3.right)* Camera.transform.rotation;
}
}
You add reference to Camera in inspector. You will have to check not to rotate above +/- 90 degrees on camera to avoid over rotating to back. If you will have issue with that I will help you.