Unity rope swinging physics algorithm: Player stuck mid-swing - unity3d

I have implemented the following tutorial in Unity (2D) attempting to create a rope swinging platformer: https://gamedevelopment.tutsplus.com/tutorials/swinging-physics-for-player-movement-as-seen-in-spider-man-2-and-energy-hook--gamedev-8782
void FixedUpdate()
{
Vector2 testPosition = playerRigidbody.position + playerRigidbody.velocity * Time.deltaTime;
Hooked(testPosition);
}
private void Hooked(Vector2 testPosition)
{
Vector2 t = new Vector2(tetherPoint.position.x, tetherPoint.position.y);
Debug.DrawLine(tetherPoint.position, playerRigidbody.position);
float currentLength = (testPosition - t).magnitude;
if (currentLength < tetherLength)
{
currentLength = (playerRigidbody.position - t).magnitude * Time.deltaTime;
}
else
currentLength = tetherLength;
if ((testPosition - t).magnitude > tetherLength)
{
Vector2 x = (testPosition - t).normalized;
testPosition = new Vector2(x.x * currentLength, x.y * currentLength);
playerRigidbody.velocity = (testPosition - playerRigidbody.position) * Time.deltaTime;
playerRigidbody.position = testPosition;
}
}
It seems to function correctly on the downward swing but when the player begins to travel upwards they become stuck floating in the air and don't drop to the middle of the arc. The swing also does not propel them very high on the other side even when dropped from height.
EDIT (Further clarification): Interestingly When the player is dropped from the other side of the tetherPoint it stops in the same spot, this time only half-way down. It's as if the player is being pulled toward a single position even when manually moved in the editor while playing no matter the direction.
EDIT: User John cleared up my concerns about deltaTime.
I've tried examining the change in variables during play but I just can't figure out why it's not working correctly. I think the issue lies somewhere in my interpretation of the original psudeo-code to C#.
Another question on the same tutorial has been asked previously but unfortunately that users implementation was very different than mine: Game rope swing physics acting weird
EDIT: Since posting I've updated the code to use AddForce and MovePosition instead but it's still the same.
playerRigidbody.AddForce((testPosition - playerRigidbody.position) * Time.deltaTime);
playerRigidbody.MovePosition(testPosition);

It looks like you're using Time.deltaTime from a method that is called from FixedUpdate. What you want to use instead is Time.fixedDeltaTime.
FixedUpdate is called at a set interval (eg. 50fps) for physics updates, but regular Update is called at a different varying frequency (up to hundreds of times a second if you've got a fast computer/simple game).
Time.deltaTime is used for the Update method, and so the value of it can be different each time Update is called, as the time between Update calls varies.
However, because FixedUpdate is called at the same interval each time, Time.fixedDeltaTime is constant and (normally) much larger than Time.deltaTime. Your code doesn't work well with Time.deltaTime, as it doesn't represent the actual difference in time between each FixedUpdate call, but Time.fixedDeltaTime should work.
As a side note, you're correct that you should be multiplying by the time delta rather than dividing. the time delta should be multiplied when calculating positions (eg. for the Vector2 testPosition assignment and the currentLength calculation), but for calculating the velocity you should be dividing the time delta (because velocity = distance/time).

Related

unity movement, object keeps falling even when key is held down

I'm still new to unity and I'm trying to create controls for a hot air balloon, however even though I'm still holding down the up arrow the object ends up falling after a few seconds and I'm not sure why.
if (Input.GetKey(KeyCode.UpArrow))
{
transform.position += new Vector3(0, 1, 0) * Time.deltaTime * speed;
fuel = fuel - 3 * Time.deltaTime;
}
When you say that the object is "falling" I take that to mean that you've added a Rigidbody (or Rigidbody2D) component to it with gravity enabled.
If you're using a Rigidbody to simulate gravity, you can't just manually manipulate the position like this. You'll want to use Rigidbody.AddForce() instead.

Forward and Sideways Player movement

So my friends and I are developing a game where you play as a snowball rolling down a hill avoiding obstacles. We're having trouble with our movement, however. We want our snowball to gains speed the larger it gets and the longer it goes without hitting something. Our forward movement is controlled by
void FixedUpdate(){
rb.velocity += Physics.gravity * 3f * rb.mass * Time.deltaTime;
//to accelerate
rb.AddForce(0, 0, 32 * rb.mass);
}
We're applying a sideways force on key input
if (Input.GetKey(ControlManager.CM.left))
{
if (rb.velocity.x >= -15 - (rb.mass * 8))
{
rb.AddForce(Vector3.left * sidewaysForce * (rb.mass * .5f), ForceMode.VelocityChange);
}
}
if (Input.GetKey(ControlManager.CM.right))
{
if (rb.velocity.x <= 15 + (rb.mass * 8))
{
rb.AddForce(Vector3.right * sidewaysForce * (rb.mass * .5f), ForceMode.VelocityChange);
}
}
The mass increases as the scale increases and vice versa.
The issue comes when the snowball gets larger than a certain scale. Once it hits that point it massively accelerates left and right while you push the keys then snaps back to its forward speed when you let go. I assume it's something to do with the mass and the way the applied forces are compounding.
Is there a better way to accelerate our snowball downhill or move it left and right? I've tried transform.Translate and transform.MovePosition, but they lead to choppy left and right movement.
For one, most movement code should multiply the velocity by Time.deltaTime. In a hypothetical game, if you increased the velocity by a certain amount each frame, then somebody with a beefy 60 fps computer will go twice as fast as a poor 30 fps laptop gamer because they will accelerate less frequently. In order to fix this, we multiply acceleration by Time.deltaTime, which is the time since the last frame.
Instead of this code, where framerate would determine speed;
Vector3 example = new Vector3(1,1,1);
void Update()
{
rb.AddForce(example);
}
We would use this code. If the framerate is half as much, Time.deltaTime will be twice as much, so the acceleration will be constant for everyone using it.
Vector3 example = new Vector3(1,1,1);
void Update()
{
rb.AddForce(example * Time.deltaTime);
}
There may be another source of this problem, although getting rid of frame-rate dependencies is a good place to start. It looks like you already have used Time.deltaTime for downward velocity, but not for sideways movement. Even if it doesn't fix the problem, using Time.deltaTime is essential for any consistent game.

How do I make something happen at a specified rotation which I incrementally go towards and may over shoot?

So I have a script for a day/night cycle attached to the directional light in unity. It slowly rotates the light which creates an effective day/night cycle. There's an event that I want to call once every sunset, or more specifically when the x rotation of the light is at 200 degrees. The problem is my script rotates a little bit each frame, according to Time.deltatime which is obviously not perfectly consistent. Because of this, I might be at a rotation just below 199 and then at the next frame, I might be at a rotation just above 200 degrees, overshooting it so that it's never actually 200 degrees. I tried to get around this by checking if the x rotation is above 200 AND the x rotation - my rotate amount in that frame is below 200, then calling the event. That was the idea but it didn't work for some reason. It never calls the event. Here's my script.
using UnityEngine;
using UnityEngine.Events;
public class DayNightCycle : MonoBehaviour
{
public TerrainGenerator terrainGenerator;
public float dayLength = 3;
float rotationSpeed;
public UnityEvent night;
public float timeNightStarts = 200;
// Start is called before the first frame update
void Start()
{
rotationSpeed = 360 / (dayLength * 60);
}
// Update is called once per frame
void Update()
{
if (terrainGenerator.mapLoaded)
{
Vector3 rotateAmount = Vector3.right * rotationSpeed * Time.deltaTime;
transform.Rotate(rotateAmount, Space.World);
float xRotation = transform.eulerAngles.x;
if (xRotation >= timeNightStarts && xRotation - rotateAmount.x < timeNightStarts)
{
night.Invoke();
}
}
}
}
The problem you are facing is expected since Unity uses Quaternions under the hood and quaternion to euler conversions are not stable.
Quote from Unity docs:
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.
If you want to avoid Quaternions, you can represent the eulerX angle as a float variable in your code. Increment its value, always set the transform.euler.x from it, but never read it back from the transform. If no other script or physics affects your transform (which should be the case for sun) you will be fine.

How to make a model appear in front of AR Camera after the session starts using ARFoundation?

I was looking to update the ARcamera position.I am doing ImageTracking project.It detects an image and a corresponding prefab is shown in front of the camera.It starts playing an animation.After the animation I want the prefab to come really close towards the camera.When I give the code prefab.position=ARcamera.position; after animation code,I think the prefab goes to the initial position where the ARCamera was when the app had started that is (0,0,0).
How to make the prefab come really close towards the front camera.
speed = 10f;
float step = speed * Time.deltaTime;
Showprefabs.transform.GetChild(0).position = Vector3.MoveTowards(Showprefabs.transform.GetChild(0).position,
new Vector3(Arcam.transform.position.x, Arcam.transform.position.y + 0.2f,
Arcam.transform.position.z + 6.3f), step);
//The values 0.2f and 6.3f I added using the Editor to make the prefab come near the camera(But in world position it is different.)
First of all I hope by "prefab" you mean already Instantiated GameObject. It makes no sense to move a prefab ;)
You tried to calculate the target position but did it with World-Space coordinates.
You probably want to do something like
var targetObject = Showprefabs.transform.GetChild(0);
var currentPosition = targetObject.position;
var targetPosition = Arcam.transform.position
// Place it 60cm in front of the camera
+ Arcam.transform.forward * 0.6f
// additionally move it "up" 20cm perpendicular to the view direction
+ Arcam.transform.up * 0.2f;
targetObject.position = Vector3.MoveTowards(currentPosition, targetPosition, step * Time.deltaTime);
If you want that movement a bit smoother so it moves slower if already close and faster if further away you might be interested in rather using Vector3.Lerp here
public float smoothFactor = 0.5f;
targetObject.position = Vector3.Lerp(currentPosition, targetPosition, smoothFactor);
Where a smoothFactor of 0.5 reads: Every frame set the object to a position in the center of the currentPosition and targetPosition. A value closer to 0 results in slower movement, closer to 1 in faster reaching the targetPosition.
Note that actually this approach will never really fully arrive at the targetPosition but only come very very close but usually this doesn't matter in AR where the Camera constantly moves a bit anyway.

Rigidbody.Moveposition issue

I'm trying to make a type of projectile with Rigidbody:
private void FiredAsProjectile(GameObject target)
{
Vector3 moveVector = Vector3.Zero;
float velMod = 8f // A placeholder value to later calculate with mass / velocity
if(target != null)
{
moveVector = (target.transform.position - transform.position);
m_rb.MovePosition(transform.position + moveVector * Time.deltaTime * velMod);
}
}
This is updated via FixedUpdate upon calling this method somewhere else. I have a few things I needed this to behave: to have velocity output and move to a position, not direction.
However, I'm getting a weird outcome. Even though this object moves as a kinematic rigidbody and its interpolation is set to None this object slows down before reaching the target vector as if it has interpolation. Let's say I wanted a bullet to be fired from the barrel and fly to a point in world instead of direction, that's what I wanted this object to behave like, but not quite so.
Am I missing something or misunderstanding something?
Is there a better way to move this kinematic rigidbody whilst still outputting rigidbody.velocity and still collide?
This happens because you don't Normalize the moveVector.
To explain why this happens, we'll suppose that Time.deltaTime = 0.01 every step:
At t0 time, let's say that the distance between the moving object (in x = 0) and the target object (x = 100) is 100.
The argument of MovePosition is 0 + 100*8*.01 = 8 and the object is moved accordingly to this new position (since you said it's Kinematic without interpolation)
At t1 time, moveVector = 100-8 = 92
The new argument now is 8 + 92*8*.01 = 15.36
Notice that between t0 and t1 you moved 8 units, whereas between t1 and t2 you moved only 7.36 units. So, the more your moving object is near the target object, the less you'll move the object, giving the effect of "slowing down" (and moreover it'll never reach the target object, you'll stop at a distance equal to the minimum floating number precision).
In order to fix this, you just need to normalize the moveVector, i.e. making its module fixed every step. So, just add this line:
moveVector = (target.transform.position - transform.position);
moveVector.Normalize();
m_rb.MovePosition(transform.position + moveVector * Time.deltaTime * velMod);
and the distance moved every step will always be the same regardless of distance.