Basically, I have two gun objects on my fps player. The real gun and the gun that the player sees. I have a script on the gun that the player sees to follow the position and rotation of the real gun which is a child of the camera via interpolation.
Rotation is fine, but I get jittery movement with the position of the gun. Here is my smooth move script on the gun that the player sees:
public Transform target; //The gun that the player doesn't see
public float moveSpeed = 0.125f;
public float rotateSpeed = 0.125f;
void LateUpdate()
{
//this is causing the jittery motion
transform.position = Vector3.Lerp(transform.position, target.position, moveSpeed);
//this is very smooth
transform.rotation = Quaternion.Lerp(transform.rotation, target.rotation, rotateSpeed);
}
Does anybody know why?
Edit:
Otherwise, I would go with a more sensible Unit for your moveSpeed, something that makes sense in your world e.g. 1 meter, then I would use Time.deltaTime to smooth the movement. If it's too slow or too fast you can tweak the speed unit.
[SerializeField] private float moveSpeed = 1f;
void LateUpdate()
{
var t = moveSpeed * Time.deltaTime; //< just to highlight
transform.position = Vector3.Lerp(transform.position, target.position, t);
}
The practical reason for using Time.deltaTime is to help smooth depending on your frame rate, if you get a lower frame the Lerp will then adjust. This common method is also explained in this answer by Eric5h5 from Unity forum.
Hmm, I wonder if you really need a Lerp to update the Gun position, if you want to sync both I would just update your transform.position = target.position since it's done every frame, they would be in sync.
Adding a lerp would just delay that sync.
The fact that you see a difference between the Quaternion and the Lerp is just that you cannot compare those two speed, I believe the speed you set on a rotation is much faster than the one for the lerp.
Related
I've been using the transform property for movement so far and that left my Physics a little choppy on collision. To counter this I switched movement using Rigidbody.velocity. This is a sample of my code.
//Start
rb = GetComponent<RigidBody>();
jumpForce = 5;
//Update
horizontalInput = Input.GetAxis("HorizontalInput");
Vector3 movement = new Vector3(horizontalInput, 0.0f, 0.0f);
rb.velocity = movement * speed;
this worked when it came to left and right, however when I want to jump I use this function and my character does not move, is there something wrong I'm doing or am I too much of a beginner to understand?
if (Input.GetKeyDown(KeyCode.UpArrow)) {
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
Looks like you're overriding the velocity when you do horizontal movement so the jump velocity has no effect. You should use rb.AddForce() for horizontal movement too.
You have to be careful not to add too much force though. For an example see this answer.
Vector3.up is the same as new Vector3(0f, 1, 0f) and since X and Z axis are 0 => your character will not move when jumping.
You need to save existing X and Z velocity in rb.velocity without any changing them when jumping:
if (Input.GetKeyDown(KeyCode.UpArrow))
{
Vector3 velocity = rb.velocity;
rb.velocity = new Vector3(velocity.x, jumpForce, velocity.z);
}
Since you change velocity instantly - you'll get instant velocity change which is the same as using AddForce with ForceMode.Impulse
So, basically I had the arms and head as a child of the camera but now I added some animations to my character and I have to use them in their initial position so the animation will work (I made the script for looking around and that is why I had them as a child of the camera). That means I have to find a different method for the hands to follow the camera. How should I approach this?
You should use this code. Basically what you want is to set a Vector3 to hand offset, and set a gameObject to the camera, and calculate from there:
Vector3 offset = new Vector3(0, 0, 0.3);
GameObject cam;
void Update(){
transform.position = cam.transform.position + offset;
}
Since your question wasn’t clear, and that might not have been what you wanted, you could use a parent game object, and set the camera as a child of it, and set the rest of the objects as child of that parent game object. Also make sure your player script should be in the parent game object.
Edit:
Make a parent game object, and name it “Player Rig”. Then out the camera underneath it in the hierarchy, and same for the hands. Place the hands where you want them to be. Then instead of having your scripts on the camera, put it on the Player Rig. Change the looking portion of your controller script to this:
void Update(){
float X = Input.GetAxis(“Mouse X) * rotSpeed * Time.deltaTime;
float Y = Input.GetAxis(“Mouse Y) * rotSpeed * Time.deltaTime;
Y = Mathf.Clamp(Y, -89.5f, 89.5f);
cam.transform.localRotation = Quaternion.Euler(Y, 0f, 0f);
arms.transform.localRotation = Quaternion.Euler(Y, 0f, 0f);
transform.rotation = Quaternion(0f, X, 0f);
}
My ball can only bounce on the y axis. X and Z movement as well as all rotation are locked in the rigidbody component. Originally I was planning to handle this using a combination of the two methods below. I'm adding my own faux-gravity to the object in FixedUpdate and bouncing the ball upwards when it collides with a platform in OnCollisionEnter. Esentially sending it bouncing up and down forever.
void OnCollisionEnter(Collision collision)
{
ball.AddForce(Vector3.up * force, ForceMode.Impulse);
}
void FixedUpdate()
{
Vector3 gravity = globalGravity * Vector3.up;
ball.AddForce(gravity, ForceMode.Acceleration);
}
The problem is that if I place an angled platform under the ball, it no longer bounces vertically with the same force. I believe this is because Unity Physics is calculating the angle/force the object should bounce in a 3D space, and then because of my script only applying the upward force of that calculation. However, I'd like my ball to bounce with a consistent vertical force regardless of the angle of the platform placed under it.
This could be achieved using a coroutine and Lerp to a certain height, which I tried using the script below, using Physics gravity this time, but it didn't have the same natural bouncing feel as Unity Physics. I'd like to go back to using the Physics system but I don't know how to stop Unity from doing the angular calculation and just launch my ball vertically with the force I want.
IEnumerator MoveBall(Vector2 newPos, float time)
{
ball.useGravity = false;
float elapsedTime = 0;
Vector2 startingPos = transform.position;
while (elapsedTime < time)
{
transform.position = Vector2.Lerp(startingPos, newPos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
ball.useGravity = true;
}
It sounds like you want your collider to be setup as a trigger. On your ball's collider, set isTrigger to true and then use OnTriggerEnter instead of OnCollisionEnter. This will prevent Unity's physics engine from creating and resolving a Collision between the ball and whatever it hits.
If you'd also want the ball to bounce off other objects in way that uses Unity's Physics, you'll need to then get creative with which colliders are/arent' triggers, and which physics layers they belong to.
This is the image of what I'm trying to achieve, in a 3D space. I want the object to follow my finger within the green zone, but stay on the edge of the green zone if I move my finger outside of it. I have achieved this with the code below, but when moving my finger around the red zone a lot of jitters and clipping occurs as the object keeps snapping back within it's bounds. The jitters I'm seeing are caused when holding my finger in the red zone out of the players circle bounds. Instead of being "stuck" in the bounds the player is trying to continue and then being positioned back within the bounds, causing jitters. I'm looking for a way to limit the movement of the player within the bounds without having to reset it's position. My main camera is attached to the moving object so it's important that I eliminate the jitters. How can I smooth this out?
public class Player : MonoBehaviour
{
public Camera movementCam;
readonly float radius = 0.45f;
readonly float speed = 3f;
Ray firstTouchPos;
Vector2 playerPos;
[SerializeField] Vector3 targetPosition;
readonly float followDelay = 20f;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
firstTouchPos = movementCam.ScreenPointToRay(Input.mousePosition);
playerPos = transform.position;
}
if (Input.GetMouseButton(0))
{
Ray currentTouchPos = movementCam.ScreenPointToRay(Input.mousePosition);
Vector2 direction = currentTouchPos.origin - firstTouchPos.origin;
float distance = Vector3.Distance(transform.position, Vector3.zero);
targetPosition = distance >= radius ? (Vector3) . (direction.normalized * radius) : (Vector3)(playerPos + direction * speed);
}
transform.position = Vector3.Lerp(transform.position, targetPosition, followDelay);
}
}
Your issue is incorrectly clamping, a simple fix would be:
if (Input.GetMouseButton(0))
{
Ray currentTouchPos = movementCam.ScreenPointToRay(Input.mousePosition);
Vector2 direction = currentTouchPos.origin - firstTouchPos.origin;
float distance = Vector3.Distance(transform.position, Vector3.zero);
targetPosition = (Vector3)(playerPos + direction * speed);
if (targetPosition.sqrMagnitude > radius * radius) //if our calculated position is greater than the radius...
targetPosition = targetPosition.normalized * radius; //set our calculated position to be exactly on the radius.
}
The jitter was caused by your object leaving the radius one frame, and on the next frame would be clamped back to the radius, only for it to attempt to move outside the radius again the next frame.
This way removes the ternary operator, which means it will behave consistently across frames, rather than switching between clamp and movement each frame.
Here are some additional pieces of advice for this issue, once you fix the above problem:
You should multiply speed and followDelay by time.deltaTime in order to smooth them across frames correctly.
You should probably apply your camera motion during LateUpdate() instead of Update(), LateUpdate() happens after all your updates happen, what can happen is during Update() objects can move around before and after your camera code is called, causing it to behave slightly inconsistently from frame to frame, applying the motion in LateUpdate() to the camera means your camera moves only when all your objects have 'settled' into place after their update, making it behave more consistently.
Additionally you're technically using Lerp() wrong here, it shouldn't cause jitter but it's not exactly how lerp should be used. Are you sure you don't want Vector3.MoveTowards() instead?
I am making a 2D game in Unity. I have added a 2D box collider and a circle collider as trigger on my sprite character. The platform on which the character is standing also have a 2D box collider. So, when my character moves near edge of platform, it experiences a force or something that pulls it away from the edge. You can think it as a protective force that helps your character not falling down the plane but the problem is that this force is not part of game and that's why it should not be there. Following is the code I am using to move my character:
// call this method to move the player
void Move(float h)
{
// reduces character velocity to zero by applying force in opposite direction
if(h==0)
{
rigidBody.AddForce(new Vector2(-rigidBody.velocity.x * 20, 0.0f));
}
// controls velocity of character
if(rigidBody.velocity.x < -topSpeed || rigidBody.velocity.x > topSpeed)
{
return;
}
Vector2 movement = new Vector2 (h, 0.0f);
rigidBody.AddForce (movement * speed * Time.deltaTime);
}
Here is image of properties.
If I keep pushing the character it will fall off the edge but if I stop just by edge the unwanted protective force pulls it back on plane.
Also, if the character bumps into another 2D box collider, it bounces back instead of just falling down.
EDIT- Bouncing effect arises mostly when player bump into other objects while jumping. Code for jumping is
void Update()
{
// The player is grounded if a linecast to the groundcheck position hits anything on the ground layer.
grounded = Physics2D.Linecast(transform.position, groundCheck.position, 1 << LayerMask.NameToLayer("Ground"));
// If the jump button is pressed and the player is grounded then the player should jump.
if(Input.GetButtonDown("Jump") && grounded)
jump = true;
}
void Jump()
{
if (jump)
{
jump = false;
rigidBody.AddForce (Vector2.up * jumpSpeed * Time.deltaTime);
}
}
The problem lies in how you're implementing the "drag" force that slows your player down to zero. Because game physics happens in steps rather than continuously, your opposing force can overshoot and briefly cause the character to move in the opposite direction.
In this situation your best bet is to multiply your speed by some fraction each frame (likely between 0.8 and 0.95), or to use Mathf.Lerp to move your X velocity towards zero.
rigidbody.velocity += Vector2.right * rigidbody.velocity.x * -0.1f;
OR
Vector3 vel = rigidbody.velocity; //Can't modify rigidbody.velocity.x directly
vel.x = Mathf.Lerp(vel.x, 0, slowRate * Time.deltaTime);
rigidbody.velocity = vel;
Also give some thought to doing your movement/physics related code within FixedUpdate() instead of Update() and using Time.fixedDeltaTime instead of Time.deltaTime. This will allow for more consistent results independent of framerate.