character keeps shaking while in topdown shooter - c#

ok well i am working on a top down shooter i have made my character look at the mouse using screentoworld view and i made my camera follow my players x, and z axis but anytime my character moves parallel from the mouse it starts shaking I think its because it looks at the mouse an the mouse real world position is dependent on the cameras position which is, but i am not sure.
here is the playerscript the many backslashes showed other things I've tried
gameObject.GetComponent<Rigidbody>().velocity = new Vector3(Input.GetAxisRaw("Horizontal")*movementspeed,0,Input.GetAxisRaw("Vertical")*movementspeed);
if (gameObject.GetComponent<Rigidbody> ().velocity.x != 0 && gameObject.GetComponent<Rigidbody> ().velocity.z != 0) {
gameObject.GetComponent<Rigidbody>().velocity = new Vector3(Input.GetAxisRaw("Horizontal")*movementspeeddiag ,0,Input.GetAxisRaw("Vertical")*movementspeeddiag);
}
// makes vector 3 type target equall to mouse position on pixial cordinates converted in to real world coordniates
target = camera.ScreenToWorldPoint (new Vector3(Input.mousePosition.x,Input.mousePosition.y,camera.transform.position.y - transform.position.y));
//Vector3 newtarget = target - transform.position;
//lerptarget = Vector3.Lerp (transform.eulerAngles,newtarget, 100);
// states the vector 3 values of target
// makes object local z face target and iniziates up axis
//Quaternion rotation = Quaternion.LookRotation (newtarget, Vector3.up);
//transform.eulerAngles = Vector3.up * Mathf.MoveTowardsAngle (transform.eulerAngles.y, rotation.eulerAngles.y, rotatespeed * Time.deltaTime);
transform.LookAt (target,Vector3.up);
and camera script
void LateUpdate(){
position = new Vector3 (player.transform.position.x, 10, player.transform.position.z);
transform.position = position;
//transform.localPosition = Vector3.Lerp (transform.position, position, speed *Time.deltaTime);
transform.eulerAngles = new Vector3 (90,0,0);
}

Probably, the problem is that you change the velocity of rigidbody. Becouse velocity changes applied each fixed update time interval, it doesnt match with frame updating time, and may lead to shaking. According to unity docs you should try to avoid directly changes of velocity. (http://docs.unity3d.com/ScriptReference/Rigidbody-velocity.html)
I'd suggest you to you use MovePosition alongside with Lerp function to implement player movement.
See this:
http://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html
http://docs.unity3d.com/ScriptReference/Vector3.Lerp.html

Related

Using MoveRotation in Unity 3D to turn player towards a certain angle

I've been told that Rigidbody.MoveRotation is the best way in Unity 3D to rotate the player between fixed positions while still detecting hits. However, while I can move smoothly from fixed position to position with:
if (Vector3.Distance(player.position, targetPos) > 0.0455f) //FIXES JITTER
{
var direction = targetPos - rb.transform.position;
rb.MovePosition(transform.position + direction.normalized * playerSpeed * Time.fixedDeltaTime);
}
I can't find out how to rotate smoothly between fixed positions. I can rotate to the angle I want instantly using Rigidbody.MoveRotation(Vector3 target);, but I can't seem to find a way to do the above as a rotation.
Note: Vector3.Distance is the only thing stopping jitter. Has anyone got any ideas?
First of all MoveRotation doesn't take a Vector3 but rather a Quaternion.
Then in general your jitter might come from overshooting - you might be moving further than the distance between your player and target actually is.
You can avoid that bit by using Vector3.MoveTowards which prevents any overshooting of the target position like e.g.
Rigidbody rb;
float playerSpeed;
Vector3 targetPos;
// in general ONLY g through the Rigidbody as soon as dealing wit Physics
// do NOT go through transform at all
var currentPosition = rb.position;
// This moves with linear speed towards the target WITHOUT overshooting
// Note: It is recommended to always use "Time.deltaTime". It is correct also during "FixedUpdate"
var newPosition = Vector3.MoveTowards(currentPosition, targetPos, playerSpeed * Time.deltaTime);
rb.MovePosition(newPosition);
// [optionally]
// Note: Vector3 == Vector3 uses approximation with a precision of 1e-5
if(rb.position == targetPos)
{
Debug.Log("Arrived at target!");
}
Then you can simply apply this same concept also to rotation by going through the equivalent Quaternion.RotateTowards basically just the same approach
Rigidbody rb;
float anglePerSecond;
Quaternion targetRotation;
var currentRotation = rb.rotation;
var newRotation = Quaternion.RotateTowards(currentRotation, targetRotation, anglePerSecond * Time.deltaTime);
rb.MoveRotation(newRotation);
// [optionally]
// tests whether dot product is close to 1
if(rb.rotation == targetRotation)
{
Debug.Log("Arrived at rotation!");
}
You can go one step further and use a tweeting library to tween between rotations.
DOTween
With that you can call it like this:
rigidbody.DoRotate(target, 1f) to rotate to target in 1 second.
Or even add callbacks.
rigidbody.DoRotate(target, 1f).OnComplete(//any method or lambda you want)
If at some point you want to cancel the tween yuou can save it on a variable and then call tween.Kill();
So, you want to animate the rotation value over time until it reaches a certain value.
Inside the Update method, you can use the Lerp method to keep rotating the object to a point, but you will never really reach this point if you use Lerp. It will keep rotating forever (always closer to the point).
You can use the following:
private bool rotating = true;
public void Update()
{
if (rotating)
{
Vector3 to = new Vector3(20, 20, 20);
if (Vector3.Distance(transform.eulerAngles, to) > 0.01f)
{
transform.eulerAngles = Vector3.Lerp(transform.rotation.eulerAngles, to, Time.deltaTime);
}
else
{
transform.eulerAngles = to;
rotating = false;
}
}
}
So, if the distance between the current object angle and the desired angle is greater than 0.01f, it jumps right to the desired position and stop executing the Lerp method.

Unity: How to limit the rotation of the z axis using Wheel Joint 2D?

I am creating a Jump and run game but you are driving with a car. I use the the Wheel Joint 2D collider and I am also able to jump. Here is my code for the movement:
void Update()
{
movement = Input.GetAxis("Horizontal");
if (Input.GetButtonDown("Jump") && IsGrounded())
{
carRb.velocity = new Vector2(carRb.velocity.x, jumpForce);
}
if (Input.GetButtonUp("Jump") && carRb.velocity.y > 0f)
{
carRb.velocity = new Vector2(carRb.velocity.x, carRb.velocity.y * 0.5f);
}
}
private void FixedUpdate()
{
backTire.AddTorque(-movement * speed * Time.fixedDeltaTime);
frontTire.AddTorque(-movement * speed * Time.fixedDeltaTime);
carRb.AddTorque(-movement * carTorque * Time.fixedDeltaTime);
}
It works just fine but when I am fast and jump I rotate completely around my own axis and I land on my head and can't move anymore. Therefore I want to limit the rotation of the z-axis to a certain degree so that it won't happen anymore. I looked up how to do it but it doesn't fit for my car-context. Do you have any idea?
I would be very grateful
You have multiple options to solve this:
Freeze the rotation axis on the rigibody (very limiting option)
Detect the faulty position as "upside-down + grounded" and respawn the vehicle after 2s
Forcefully rotate the vehicle back to normal rotation when it's grounded (not in air anymore, so you still allow flips in the air)
Limit the angle via script.
Last thing could be done like this:
Vector3 eulerRot = rb.rotation.eulerAngles; // read current rotation
eulerRot.z = Mathf.Clamp(eulerRot .y, minRotation, maxRotation); // clamp it only on z axis.
rb.rotation = Quaternion.Euler(eulerRot); // set clamped rotation

FlyCamera with collision

How to create collisions for a flying camera in Unity 3d? to prevent the camera from falling into objects on the scene. My camera script snippet:
public class FlyCamera : MonoBehaviour
{
void FixedUpdate()
{
if (!isAlternative)
SetCameraMovement();
}
private void SetCameraMovement()
{
lastMouse = Input.mousePosition - lastMouse;
lastMouse = new Vector3(-lastMouse.y * camSens, lastMouse.x * camSens, 0);
lastMouse = new Vector3(transform.eulerAngles.x + lastMouse.x, transform.eulerAngles.y + lastMouse.y, 0);
if (Input.GetMouseButton(1))
transform.eulerAngles = lastMouse;
lastMouse = Input.mousePosition;
GetBaseInput();
transform.position = Vector3.Lerp(transform.position, NewPosition, Time.deltaTime * movementTime);
}
private void GetBaseInput()
{
Speed = Input.GetKey(KeyCode.LeftShift) ? superSpeed : mainSpeed;
if (Input.GetKey(KeyCode.W))
NewPosition += transform.forward * Speed;
if (Input.GetKey(KeyCode.S))
NewPosition += transform.forward * -Speed;
if (Input.GetKey(KeyCode.A))
NewPosition += transform.right * -Speed;
if (Input.GetKey(KeyCode.D))
NewPosition += transform.right * Speed;
}
}
If you want to achieve a RTS-like flying camera, you can set the camera to a parent dolly-like object and keep it looking at that object:
Object (Camera Dolly) -> this is moved by wasd and is rotated to the desired view angle
Child object: Camera -> only the camera's position.y is ever changed
Then, on a custom script on the dolly in Update(), send a LineCast from the dolly to the camera. When the cast hits a collider (important to use e.g. tags here so it does not collide with anything, like e.g. units) in the world, gradually (over time) reduce the camera's position.y and if nothing is hit, gradually increase it to the desired height. It will not entirely prevent the camera from clipping trough bigger objects but move it so it doesn't stay "inside" a collider. Something like that is also typically used in RPG third person cameras.
Please let me know if more clarification is needed!
Edit: I'd do that only for really big view-blocking objects though, not for the terrain or smaller buildings/units - at least if your goal is more of an RTS or sim game.
Edit2: In a solar system setting, it is a bit more complicated. A few options:
Before translating the camera, do a SphereCast (in the desired (final) movement direction with sphere radius of minimum allowed distance to any object) against all celestial object SphereColliders and set your "NewPosition" to the nearest hitPoint minus the desired minimum allowed distance to a celestial object. Also add some margin here, so the camera doesn't get stuck. This would be a continuous collision detection approach.
Everytime after translating the camera , do a CheckSphere (sphere radius is allowed minium distance to any object) against all celestial objects and as long as there is any hitpoint, lerp the camera position out of it over time. This is a "softer" looking approach, but if you fly very fast, it can still clip into something.
You can savely do both in LateUpdate().
Better put you camera movement in the LateUpdate() as adviced in the docs.
With a rigidbody and a collider properly set for the collision detection. Check docs so that you can detect the collisions properly.
Check that " Notes: Collision events are only sent if one of the colliders also has a non-kinematic rigidbody attached", so your camera could be one gameObject with no rigidbody, so that you can fly free and collide.
I case you might need physics behaviour for yout camera and you want it also to behave as a rigidbody I would set the camera movement with rigidbody.SetPosition instead of with transform.position = new Vector3(), as you need to choose to set the transform in the geometric or the physics world.
Also you would need to uncheck the gravity option not to fall.

Gyroscope with compass help needed

I need to have a game object point north AND I want to combine this with gyro.attitude input. I have tried, unsuccessfully, to do this in one step. That is, I couldn't make any gyro script, which I found on the net, work with the additional requirement of always pointing north. Trust me, I have tried every script I could find on the subject. I deduced that it's impossible and probably was stupid to think it could be done; at least not this way (i.e. all-in-one). I guess you could say I surmised that you can't do two things at once. Then I thought possibly I could get the same effect by breaking-up the duties. That is, a game object that always points north via the Y axis. Great, got that done like this:
_parentDummyRotationObject.transform.rotation = Quaternion.Slerp(_parentDummyRotationObject.transform.rotation, Quaternion.Euler(0, 360 - Input.compass.trueHeading, 0), Time.deltaTime * 5f);
And with the game object pointing north on the Y, I wanted to add the second game-object, a camera in this case, with rotation using gyro input on the X and Z axis. The reason I have to eliminate the Y axes on the camera is because I get double rotation. With two things rotating at once (i.e. camera and game-object), a 180 degree rotation yielded 360 in the scene. Remember I need the game object to always point north (IRL) based on the device compass. If my device is pointing towards the East, then my game-object would be rotated 90 degrees in the unity scene as it points (rotation) towards the north.
I have read a lot about gyro camera controllers and one thing I see mentioned a lot is you shouldn't try to do this (limit it) on just 1 or 2 axis, when using Quaternions it's impossible when you don't know what you're doing, which I clearly do not.
I have tried all 3 solutions from this solved question: Unity - Gyroscope - Rotation Around One Axis Only and each has failed to rotate my camera on 1 axis to satisfy my rotational needs. Figured I'd try getting 1 axis working before muddying the waters with the 2nd axis. BTW, my requirements are simply that the camera should only rotate on 1 axis (in any orientation) based on the X axis of my device. If I could solve for X, then I thought it'd be great to get Z gyro input to control the camera as well. So far I cannot get the camera controlled on just 1 axis (X). Anyway, here are my findings...
The first solution, which used Input.gyro.rotationRateUnbiased, was totally inaccurate. That is, if I rotated my device around a few times and then put my phone/device down on my desk, the camera would be in a different rotation/location each time. There was no consistency. Here's my code for the first attempt/solution:
<code>
private void Update()
{
Vector3 previousEulerAngles = transform.eulerAngles;
Vector3 gyroInput = Input.gyro.rotationRateUnbiased;
Vector3 targetEulerAngles = previousEulerAngles + gyroInput * Time.deltaTime * Mathf.Rad2Deg;
targetEulerAngles.y = 0.0f;
targetEulerAngles.z = 0.0f;
transform.eulerAngles = targetEulerAngles;
}
</code>
The second solution was very consistent in that I could rotate my device around and then put it down on the desk and the unity camera always ended up in the same location/rotation/state so-to-speak. The problem I had was the camera would rotate on the one axis (X in this case), but it did so when I rotated my device on either the y or x axis. Either type of rotation/movement of my phone caused the unity camera to move on the X. I don't understand why the y rotation of my phone caused the camera to rotate on X. Here is my code for solution #2:
private void Start()
{
Input.gyro.enabled = true;
startEulerAngles = transform.eulerAngles;
startGyroAttitudeToEuler = Input.gyro.attitude.eulerAngles;
}
private void Update()
{
Vector3 deltaEulerAngles = Input.gyro.attitude.eulerAngles - startGyroAttitudeToEuler;
deltaEulerAngles.y = 0.0f;
deltaEulerAngles.z = 0.0f;
transform.eulerAngles = startEulerAngles - deltaEulerAngles;
}
The 3rd solution: I wasn't sure how to complete this last solution, so it never really worked. With the 2 axis zeroed-out, the camera just flipped from facing left to right and back, or top to bottom and back; depending on which axis were commented out. If none of the axis were commented-out (like the original solution) the camera would gyro around on all axis. Here's my code for attempt #3:
private void Start()
{
_upVec = Vector3.zero;
Input.gyro.enabled = true;
startEulerAngles = transform.eulerAngles;
}
private void Update()
{
Vector3 gyroEuler = Input.gyro.attitude.eulerAngles;
phoneDummy.transform.eulerAngles = new Vector3(-1.0f * gyroEuler.x, -1.0f * gyroEuler.y, gyroEuler.z);
_upVec = phoneDummy.transform.InverseTransformDirection(-1f * Vector3.forward);
_upVec.z = 0;
// _upVec.x = 0;
_upVec.y = 0;
transform.LookAt(_upVec);
// transform.eulerAngles = _upVec;
}
Originally I thought it was my skills, but after spending a month on this I'm beginning to think that this is impossible to do. But that just can't be. I know it's a lot to absorb, but it's such a simple concept.
Any ideas?
EDIT: Thought I'd add my hierarchy:
CameraRotator (parent with script) -> MainCamera (child)
CompassRotator (parent) -> Compass (child with script which rotates parent)
I'd do this in the following way:
Camara with default 0, 0, 0 rotation
Screenshot
Object placed at the center of the default position of the camera.
Script for the Camera:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
Camera m_MainCamera;
// Start is called before the first frame update
void Start()
{
// Disable the sleep timeout during gameplay.
// You can re-enable the timeout when menu screens are displayed as necessary.
Screen.sleepTimeout = SleepTimeout.NeverSleep;
// Enable the gyroscope.
if (SystemInfo.supportsGyroscope)
{
Input.gyro.enabled = true;
}
m_MainCamera = Camera.main;
m_MainCamera.enabled = true;
}
// Update is called once per frame
void Update()
{
if (m_MainCamera.enabled)
{
// First - Grab the Gyro's orientation.
Quaternion tAttitude = Input.gyro.attitude;
// The Device uses a 'left-hand' orientation, we need to transform it to 'right-hand'
Quaternion tGyro = new Quaternion(tAttitude.x, tAttitude.y, -tAttitude.z, -tAttitude.w);
// the gyro attitude is tilted towards the floor and upside-down reletive to what we want in unity.
// First Rotate the orientation up 90deg on the X Axis, then 180Deg on the Z to flip it right-side up.
Quaternion tRotation = Quaternion.Euler(-90f, 0, 0) * tGyro;
tRotation = Quaternion.Euler(0, 0, 180f) * tRotation;
// You can now apply this rotation to any unity camera!
m_MainCamera.transform.localRotation = tRotation;
}
}
}
With this script my Object always face SOUTH no matter what.
If you want the object to face NORTH you just have to turn the view 180ยบ on the Y axis as a last rotation:
Quaternion tRotation = Quaternion.Euler(-90f, 0, 0) * tGyro;
tRotation = Quaternion.Euler(0, 0, 180f) * tRotation;
//Face NORTH:
tRotation = Quaternion.Euler(0,180f, 0) * tRotation;
Hope this might help ;)

Camera is following not smoothly

The camera is supposed to follow a 2D character, here is code
void LateUpdate ()
{
var to = target.position;
to.z = transform.position.z;
var newPos = Vector3.Lerp(transform.position, to, speed * Time.deltaTime);
transform.position = newPos;
newPos.z = to.z;
Debug.DrawRay(newPos, Vector3.up, Color.green, 5);
}
I also draw positions of the character and the camera.
Red lines are character's positions, and green lines are camera's positions
What do I do wrong?
UPDATE:
I've figured out something interesting. On the picture below green lines are positions of the Camera that is moved by Vector3.Lerp inside a LateUpdate method. Yellow lines are positions of the character that I set to the character's Rigidbody2D inside FixedUpdate method, and the red lines are positions of the character's transform as they seen from inside the camera's LateUpdate.
What I want to say is that the actual position of the character is driven by its Rigidbody2D component. By changing Rigidbody2D's "Interpolate" option we can get different results.
The problem is that even if I add to the Rigidbody2D's position the same value every FixedUpdate tick, the result isn't so consistent. Sometimes the distance between new and old position is bigger than it should be.
Add to that the we set position of the camera in the LateUpdate method, which has different update rate than FixedUpdate, so even if we set the new position to the character's transform, and not to the Rigidbody2D, the movement of the camere still won't be smooth, because speed of the character will be different every frame.
For now I have only one solution.
Set the new position of the character to its transform, and not to the Rigidbody2D
Change position of the camere in its FixedUpdate, and not in the LateUpdate.
Then the positions will look like this
But since position of the camera is set in the FixedUpdate, it won't be so smooth as it might be, and also i'm not sure whether collision detection of the character will work good, since we set its position directly to its transform.
the problem could be coming from how you are using interpolation to determine how far to move the camera.
I don't know if Vector3.Lerp's behavior would be to extrapolate if the third parameter (its fraction) is higher than 1.0, but i suspect this could be the problem (specifically if there is a bit more time between frames, and speed * Time.DeltaTime becomes higher than 1.0)
A better way (eliminating the lerp) could be to do the interpolation of distance over speed and time yourself ;
void LateUpdate ()
{
var to = target.position;
to.z = transform.position.z;
//you can just multiply a Vector3 with a float
//so we can do the interpolation maths ourselves like this :
var distanceToMove = (to - transform.position) * speed * Time.deltaTime;
var newPos = transform.position + distanceToMove;
transform.position = newPos;
newPos.z = to.z;
Debug.DrawRay(newPos, Vector3.up, Color.green, 5);
}
(if that got confusing, here is cleaned up version to make it more concise)
void LateUpdate ()
{
var to = target.position;
to.z = transform.position.z;
transform.position += (to - transform.position) * speed * Time.deltaTime; ;
Debug.DrawRay(transform.position, Vector3.up, Color.green, 5);
}

Categories

Resources