Unity: Set RigidBody velocity in FixedUpdate() or Start()? - c#

I'm developing a simple 2D game in Unity (balls hitting bricks), so i have a ball and i want to fire the ball to hit bricks, now to fire the ball i have to set Velocity on the RigidBody component attached to the ball's GameObject.
There are two ways to do that:
Method 1:
Set the velocity inside the Start() method
private void Start()
{
rb.velocity = direction * speed;
}
Method 2:
Write a FixedUpdate() method and put the statement in there. because:
Use FixedUpdate when using Rigidbody.(Unity Docs)
private void Start()
{
_setVelocity = true;
}
private void FixedUpdate()
{
if (!_setVelocity) return;
rb.velocity = direction * speed;
rb.angularVelocity = 0;
rb.gravityScale = 0;
_setVelocity = false;
}
But the thing is: i don't need the FixedUpdate() method for anything else, because i only fire the ball once in its script's lifespan, so after i fire the ball unity will continue to run the FixedUpdate()method and check for _setVelocity's value and return (exit) every time because it will always be false after i shoot the ball the first time.
And this will happen every fixed frame-rate frame which is expensive i guess for just setting the velocity only once.
SO:
Is it expensive to have a FixedUpdate() method that checks for false value and returns every frame?
Which of the two methods performs better in this case?

Is it expensive to have a FixedUpdate() method that checks for false value and returns every frame ?
A simple boolean condition such as yours is not that greedy so do not worry about that. However, any line of code that runs decrease your performances. The Update() method itself is more greedy in terms of performance than your "trivial" condition. Disabling your component script or removing the object itself is more optimize than keep it running for nothing. Just keep that in mind :)
Which of the two methods performs better in this case ?
The first one is more efficient because your velocity is fixed, so there is no point of using an update method (Update() or FixedUpdate()). You should initialize your rigidbody's velocity directly in your Start() method as you did in your post, so you can get rid of your boolean and its condition.
Unity's documentation is pretty clear about rigidbody and FixedUpdate() but I have the impression that you misunderstood some aspects.
They tell you that if you want to modify any data of your rigidbody during the run-time, prefer to use FixedUpdate() instead of Update() to avoid weird behavior.
But why should you use FixedUpdate() instead of Update() ?
Because Update() is called at each frame, so if your pc runs at 100 fps, Update() will be called 100 times. And if your pc runs at 200 fps, it'll be called 200 times. It is kind of problematic in some cases, especially when you interact with physics components.
In order to understand, lets admit that we want to apply a force to our rigidbody (rb.AddForce(100)). If you call it in your Update() method and your pc runs at 100 fps, you'll apply 10000 force in one second. However, if your pc runs at 50 fps, then you'll apply 5000 force in one second. It'll cause you some weird behavior that could be avoided thanks to FixedUpdate(), which is called at a fix number of times per second (see unity doc).
However, there is no point of using FixedUpdate() with your rigidbody component if you do not want to modify it. They tell you that if you want to update your rigidbody component, you should do it inside the FixedUpdate method instead of Update().
So for your question, you can simply do the following:
Awake() {
// getting your components
rb = GetComponent<Rigidbody2D>();
}
Start() {
// initiliaze your components
rb.velocity = direction * speed;
rb.angularVelocity = 0;
rb.gravityScale = 0;
}
Hoping that I've helped you, good luck with your project !

Related

My animations aren't playing when they should be

So Basically, I am creating a wandering ai character in unity using c# and the wandering is all working fine, but when the certain animations are supposed to play, they don't. I will include the code I am using to make this happen. I am also using an animator component on the model and the animations are all properly named and align with the animation in the animator's names and the animations are from mixamo. Any help is much appreciated because I am completely stuck!
void Update()
{
if (isWandering == false)
{
StartCoroutine(Wander());
}
if (isRotatingRight == true)
{
gameObject.GetComponent<Animator>().Play("idle");
transform.Rotate(transform.up * Time.deltaTime * rotSpeed);
}
if (isRotatingLeft == true)
{
gameObject.GetComponent<Animator>().Play("idle");
transform.Rotate(transform.up * Time.deltaTime * -rotSpeed);
}
if (isWalking == true)
{
gameObject.GetComponent<Animator>().Play("waalk");
transform.position += transform.forward * moveSpeed * Time.deltaTime;
}
}
Really consider using Animator parameters and transitions, it will save a lot of headache later on.
Anyways, regarding the question: Your code is in Update, which means it runs every frame.
That means every frame you're telling the Animator to play the state with that name, so every frame it starts the animation of that state over again. The result would be that your object would be stuck on the first frame of whatever animation.
Instead, you should call Play just once, when the desired conditions change.
Incidentally, that's one example of when it would be more convenient to use Animator parameters, since with transitions you are querying for specific conditions that take the animator from one state to the other, so you would get this "run just once" behaviour for free.
SORRY I FOUND THE ANSWER I WAS DOING EVERYTHING TO A MODEL THAT WASN'T RIGGED 😅 IT'S ALL GOOD NOW THANKS FOR THE HELP :)

Unity how can I put multiple if statements in OnTriggerEnter2D?

Good day, I'm trying to create an enemy that damages the player because that's what enemies do. I have figured it out on how to do this the only problem is that the enemy kills the player almost instantly and is too overpowered... I was trying to put some cooldown before the enemy strikes again but unfortunately, an OnTriggerEnter2D seems to only accept 1 if statement.
void OnTriggerEnter2D(Collider2D col){
//What iam trying to achieve but this doesn't work
if(Time.time > nextDamageTime) {
if(col.CompareTag("Player")){
player.curHealth -= 1;
Debug.Log("Enemy damaged player!");
nextDamageTime = Time.time + cooldownTime;
}
}
}
First of all this is c#. There is nothing that can anyhow control how many if blocks you open within a method.
So, no there is no limit on conditions and if blocks within OnTriggerEnter2D!
I don't see a direct problem in your code actually except maybe: OnTrigggerEntet2D is called exactly once namely in the moment the trigger enters without exiting. So if this already instantly kills the player then reducing 1 might simply be a too high value.
With the timer nothing happens because as said it is called only once, so in the case the timer wasn't finished yet nothing happens until the enemy leaves and re-enters eventually.
You might want to rather use OnTriggerStay2D which is rather called every frame as long as a trigger collides.
float timer;
private void OnTriggerEnter2D (Collider other)
{
// reset the timer so the first hit happens immediately
timer = 0;
}
private void OnTriggerStays2D(Collider other)
{
if(!col.CompareTag("Player")) return;
timer -= Time.deltaTime;
if(timer > 0) return;
timer = cooldownTime;
player.curHealth -= 1;
}
Which now would reduce the players health by 1 every cooldownTime seconds.
In general especially for multiple enemies I personally would rather give the player the control over timer after it was last hit so the player itself has kind of an invincible time. Enemies can simply attack like they would do usually but the player itself decides whether the damage counts or not.

Camera following Rigidbody jitter every few seconds with background objects

Camera following Rigidbody2D jitter every few seconds with background (non rigidbody) objects (Obstacles). The FPS in profiler is fine it is near to 100. also Interpolate is also fine. Using Unity 2017.4.12 (LTS)
GIF GIF Video here
Camera Follow Script
public class CameraFollow : MonoBehaviour {
public float followRange = 0.5f;
public float cameraZ;
public Transform Player;
public Vector3 newPos;
void Start () {
cameraZ = transform.position.z;
}
void FixedUpdate() {
newPos = new Vector3(Player.position.x + followRange, 0, cameraZ);
transform.position = Vector3.Lerp(transform.position, newPos, 0.5f);
}
}
Player Script :
public class PlayerBall : MonoBehaviour {
public float xSpeed = 10;
// Update is called once per frame
void FixedUpdate () {
this.transform.position = new Vector3(this.transform.position.x + Time.fixedDeltaTime * xSpeed,
this.transform.position.y , transform.position.z);
}
}
Player Rigidbody
Project File Download
The rate at which FixedUpdate is called is different than the frame rate which dictates the rate at which Update and LateUpdate are called.
FixedUpdate should be used to set velocities on rigidbodies. Setting positions in FixedUpdate always runs the risk of jitter as it is called out of synch with your frame rate. Setting position on a simulated Rigidbody also goes against the point of simulating it as you are overriding whatever impact the physics might have on the Rigidbody.
The Manual page for Ridgidbody 2D also states that:
The Rigidbody 2D component overrides the Transform and updates it to a position/rotation defined by the Rigidbody 2D. Note that while you can still override the Rigidbody 2D by modifying the Transform component yourself (because Unity exposes all properties on all components), doing so will cause problems such as GameObjects passing through or into each other, and unpredictable movement.
[...] A Collider 2D should never be moved directly using the Transform or any collider offset; the Rigidbody 2D should be moved instead. This offers the best performance and ensures correct collision
detection.
For a Rigidbody that should be moving, you have the choice between using a Dynamic BodyType or a Kinematic one, depending on your usecase. If you want the plane to be pushed around by other non-static colliders, it should be Dynamic. If it should not be moved around, it should be Kinematic (which is also more performant).
Use-case 1: Dynamic Rigidbody 2D
This is the setup you have currently. However, the Manual page for Ridgidbody 2D also states that:
Do not use the Transform component to set the position or rotation of a Dynamic Rigidbody 2D. The simulation repositions a Dynamic Rigidbody 2D according to its velocity; you can change this directly via forces applied to it by scripts
, or indirectly via collisions and gravity.
So you should change the Player script to use the Rigidbody2D method AddForce
public class PlayerBall : MonoBehaviour {
public float thrust = 10;
Rigidbody2D body;
Vector2 forwardDirection = Vector2.right;
void Awake() {
body = GetComponent<Ridigbody2d>();
}
// FixedUpdate is called once per physics update
void FixedUpdate () {
body.AddForce(forwardDirection * thrust)
}
}
Now this will just continuously add a force to the plane, propelling it forwards and accelerating it. So you'll likely want to figure out a way to stabilize the speed by reducing the added force as you approach a target velocity (you can check the current velocity with body.velocity.x) and by slowing the plane down using drag.
I won't go further into the details because I suspect that Kinematic is actually what you'd want to use:
Use-case 2: Kinematic Rigidbody 2D
A Ridgedbody2D with the Body Type set to Kinematic can safely be moved with the Ridgedbody2D components MovePosition method.
So with that setup, you're player script would look like this:
public class PlayerBall : MonoBehaviour {
public float xSpeed = 10;
Rigidbody2D body;
Vector2 forwardDirection = Vector2.right;
void Awake() {
body = GetComponent<Ridigbody2d>();
}
// FixedUpdate is called once per physics update
void FixedUpdate () {
body.MovePosition(forwardDirection + xSpeed * Time.deltaTime)
}
}
Note: I'm using Time.deltaTime here because the value returned by this property will always be the correct delta time for the type of Update method it is used in.
Following Camera
Updating the position of the camera should always be in sync with the framerate. Doing so in FixedUpate can cause stutter. Instead it should be done in Update or (if you want to e.g. wait until position changes due to animations are applied before you set the position) in LateUpdate
Lastly
If the bombs in the game are also moving, and are Ridgidbodies where the position is updated similarly in their FixedUpate, you should change them to use a solution as described in either of the 2 use cases.
Note that Kinematic Rigidbodies wont cause any collision events (trigger events will still be caused) If you want to move both the bombs and the plane as Kinematic Ridigbodies but collision events to still be cause, you can set Use Full Kinematic Contacts on the rigidbodies which should receive these updates.
I'm not sure if this would have any performance impacts (besides the one for generating these events) and you might still want to read the 1[full documentation on the Ridigbody2D component].
Overall TL;DR: what you're seeing is likely not a performance problem (or it would show up in the Profiler and you'd have fluttering and poorer FPS) but movement jitter due to using the wrong update methods and schemes to apply movement to physical bodies. This is a very common pitfall in Unity.
If there is a performance problem, please attach a screenshot of the profiler. With the FPS display method you are using, likely you are triggering a GC.Collect every so often for string manipulations in the FPS script (and GC.Allocs elsewhere).

AddForce Method Not Working

I am programming a top-down hack-and-slash game for a class. We want a mechanic where if you get hit by an enemy, or an enemy hits you, you get 'knocked back'. Unfortunately, no matter what I have tried, I cannot get either the enemy or the player to react to the force.
Here is a list of things I have checked that have been suggested in other questions like this:
Player/Enemy is NOT Kinematic
Tried with both Gravity on and off
No positions/rotations are frozen
Player/Enemy have Rigidbodies attached
Player/Enemy have colliders attached, one with and one without the 'isTrigger' function checked.
Tried both OnCollisionEnter and OnTriggerEnter
The force value is high, the mass, drag, and angular drag are low
I have run out of ideas. Any and all support you can give is greatly appreciated.
Here is a snippet of the code from a script from the player Object:
public void OnTriggerEnter(Collider col)
{
if (col.gameObject.tag == "EnemyHit" && !invincible)
{
Debug.Log("The player has been hit!");
//sets player as invincible
invincible = true;
// Set the damaged flag so the screen will flash.
hit = true;
timeOfHit = Time.time;
// Reduce the current health by the damage amount.
currentHealth -= 1;
GetComponent<Rigidbody>().AddForce(transform.forward * recoilThrust, ForceMode.Force);
Debug.Log("This is the force that is being added to the player when it is hit. : " + -transform.forward * recoilThrust);
//...
}
}
I can prove (using the Debug.Log) function, that the code reaches there, and the force is calculated.
To summarise the comments:
Rigidbody.AddForce doesn't work when the object has a CharacterController besides the rigidbody. In this case the effect has to be "faked". Possible ways can be found here:
The last couple of posts
Another solution, C# and Javascript
Basically you need to apply the force using CharacterController.Move.
Is Kinematic enabled? Doing so will make the object ignore physic forces.

Kinematic Rigidbody moves on its own

I have a very weird issue in my 2D Unity game, which I was able to reduce to the following core problem/minimal reproducing test case. Follow these steps to reproduce (Unity 5.1.1f1):
Create a player object (Cube) at location (0,0,0).
Remove the BoxCollider Component.
Attach the following C# script, Unity will automatically add the required Components and thereby make it a Rigidbody Collider.
Set the isKinematic flag.
Add another Cube to the scene at location (2,0,0).
Remove the BoxCollider Component and add a BoxCollider2D. This makes this cube a static Collider.
Set the isTrigger flag.
Run the scene.
Expected behavior:
The player cube accelerates towards the other cube and stops moving once it touches it.
Observed behavior:
The player cube accelerates towards the other cube, then continues moving at constant speed.
Additional implementation details:
I originally moved all objects by translating their transform and didn't use Rigidbodies at all because I didn't need collision detection. Now I do, so I want Rigidbodies. I dived into online resources and found out I'm supposed to use rigidbody.MovePosition() rather than transform.Translate() or transform.position. I changed my script, and the above error appeared.
Going back to transform.position fixes the issue, but that's not a good solution, as it involves bad practice which, according to what I read, produces significant CPU loads.
Failed attempts to solve:
Switching to Update() and Time.deltaTime didn't make any difference.
I tried not returning in the Update() and instead simply resetting the timestep to 0 while stop is set. No change.
Fiddling with the Inspector and doing stuff like freezing position on the rigidbody or setting the player objects to also be a trigger had no effect at all. Changing anything on the Rigidbody component while the game is running (after the collision) makes the cube stop immediately. Literally anything, even setting its mass to 0.
I also tried setting velocity to 0, resulting in no change. Which does make sense since Update() is skipped entirely (I also checked that with a Debug.Log() by the way).
So at this point, I'm down to barely 30 lines of code and I still have no idea what's causing this. Since the involved objects are a static Trigger collider and a kinematic rigidbody collider, both with no physics materials, there should be nothing that makes this thing move once the flag is set. But it does move.
SimpleController2D.cs
using UnityEngine;
using System.Collections;
[RequireComponent (typeof (BoxCollider2D), typeof (Rigidbody2D))]
public class SimpleController2D : MonoBehaviour {
public Vector3 velocity = Vector3.zero;
private Transform thisTransform;
private Rigidbody2D thisRigidbody;
public bool stop = false;
void Awake () {
thisTransform = GetComponent<Transform> ();
thisRigidbody = GetComponent<Rigidbody2D> ();
}
void FixedUpdate() {
float timestep = Time.fixedDeltaTime; // temporarily stored for ease of access
if (stop) {
return; // freeze on hit
}
velocity.x += timestep; // accelerate
/* add a second slash (/) to toggle between transform and rigidbody
thisTransform.position += velocity * timestep; /*/
thisRigidbody.MovePosition ((Vector3)thisRigidbody.position + velocity*timestep); //*/
}
void OnTriggerEnter2D(Collider2D col) {
stop = true;
}
}
Solution
This is a bug in Unity 5.1.1f1 and was fixed in the patch release 5.1.1p2 and later.
Get it here:
http://unity3d.com/unity/qa/patch-releases?version=5.1
What happened?
You can even reduce the problem to a single MovePosition call. MovePosition uses the physics engine to move the object. Therefore Unity calculates the velocity necessary to reach the target position within the next physics update. Version 5.1.1f1 fails to reset the velocity to zero after reaching the position, so the object will just continue moving with the calculated velocity.

Categories