Good evening. I am realizing (Unity3d) the moving forward (in the plane which is made by X- and Z-axis) using Rigidbody.velocity, the direction depends on MyPlayer.transform.localEulerAngles.y. Tell me, please:
Which of the realizations of the method for the button which moves (it will be called every frame if the button is pressed) is "cheaper":
a)
public void MoveForward()
{
PlayerRigidbody.velocity = new Vector3(speed * (float)(Math.Sin(((double)(transform.localEulerAngles.y / 180)) * Math.PI)), PlayerRigidbody.velocity.y, speed * (float)(Math.Cos(((double)(transform.localEulerAngles.y / 180)) * Math.PI)));
}
b)
public void MoveForward()
{
Quaternion rotation = Quaternion.AngleAxis((transform.localEulerAngles.y - 180), Vector3.up);
Vector3 temp = new Vector3(0, PlayerRigidbody.velocity.y, -speed);
PlayerRigidbody.velocity = rotation * temp;
}
Is there any dependency on Time.deltaTime?
You're setting the velocity, so if you mean speed when you use speed then there shouldn't be any need to include Time.deltaTime.
As far as performance goes, (1) you can implement it both ways and profile to find out which takes longer, and (2) the difference is almost certainly negligible and you should go with whatever is easier to read, because readability is key to maintainability.
Regarding point 2, IMO neither option is readable. Are you just trying to make the thing move in its local forward direction? Try using the TransformDirection method, like:
PlayerRigidbody.velocity = speed*transform.TransformDirection(Vector3.forward);
transform.TransformDirection says,
Description
Transforms direction from local space to world space.
so just pass that function the Vector3.forward and let Unity's built-in method handle the math.
Related
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).
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.
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.
I've searched for quiet some time now around the web for a solution to my problem, and couldn't find any sufficient enough resource to help me.
I'm developing a co-op RPG game in Unity 3D, using the UDP library https://github.com/RevenantX/LiteNetLib , and I managed to do a client-server connection, where I send vector of my player movement (time calculation is not applied- so the server can handle it on its own tick) to the server, and it calculates the new position where the character should be, and afterwards I broadcast to all players the new same vector that I sent to the server so they can also calculate the physics by themselves.
The problem I'm running into is that the movement seems very laggy and sometimes miscalculated.
I'm not sure if its due to my local PlayerController script or due to bad network, or bad design where I should actually send the new absolute position of the client.
I know this is a hard question, I am hoping for some guidelines or expertise of developers who tried to create multiplayer games themselves,
I should also note, that cheating doesn't concern me because it is a coop game and not a competitive one.
Thanks in advance for any help.
Here is a snippet of my local PlayerController code:
void Update()
{
// Get Input for axis
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
// Calculate the forward vector
Vector3 camForward_Dir = Vector3.Scale(UnityEngine.Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized;
Vector3 move = v * camForward_Dir + h * UnityEngine.Camera.main.transform.right;
if (move.magnitude > 1f) move.Normalize();
// Calculate the rotation for the player
move = transform.InverseTransformDirection(move);
// Get Euler angles
float turnAmount = Mathf.Atan2(move.x, move.z);
transform.Rotate(0, turnAmount * RotationSpeed * Time.deltaTime, 0);
if (_characterController.isGrounded)
{
_moveDir = transform.forward * move.magnitude;
_moveDir *= Speed;
}
_moveDir.y -= Gravity * Time.deltaTime;
var delta = _moveDir * Time.deltaTime;
_characterController.Move(delta);
if (!IsMoving())
{
//_wasMoving = false;
return;
}
// we send the move direction so that the server and other client`s can calculate for themselves
OnPlayerMoved?.Invoke(_moveDir, Time.deltaTime);
}
As I mentioned in my comment, people who still need help with this topic might want to view this demo https://github.com/RevenantX/NetGameExample it demonstrates how to create 2D player sync which can easily be applied for 3D, the code belongs to RevenantX,
and it relies on a UDP library which he also created for the community.
I have already this function from this question. I changed the sign of the rotation:
void rotateBotConnector()
{
Vector3 diff = (player.transform.position - botConnector.transform.position).normalized;
float rot_z = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
botConnector.transform.localRotation = Quaternion.Euler(0f, 0f, -(rot_z - 90f));
}
But the problem is, that now my object follows the player on the XZ plane but when the rotation reaches a certain degree, left or right, the object stops to rotate towards my player.
For better understanding: http://imgur.com/vWaqc31
Why not just use:
var target : Transform;
transform.LookAt(Vector3(target.transform.position.x, target.transform.position.y, transform.position.z);
It seems a lot easier than using euler. This way you look at target's x & y but transform your z.
Also I'm no expert with euler but it seems like it is limited to a 90 degree turn and I think this may be the reason why:
Quaternion.Euler(0f, 0f, -(rot_z - 90f));
Unless you have absolute necessity to rotate via angles maybe you'd rather go with manipulating the forward vector of the object, it's easier to understand the logic this way:
void rotateBotConnector()
{
Vector3 targetForwad = botConnector.transform.position - player.transform.position;
targetForward.y= 0f;
targetForward.Normalize(); // <-- this is very expensive function try avoid using it whenever possible
botConnector.forward = Vector3.Lerp(botConnector.forward, targetForward, Time.deltaTime));
}
once again, if you are short on cpu cycles you may want to go for trying to calculate angles instead of vectors. But in most cases this is ok. (the code needs a spell checking)