So, I'm trying to make my own movement script for Unity with a Rigidbody component attached to my character, and I'm using this code inside of the update function:
// Up above, in public scope, before Start and Update functions
public float speed = 1f;
public Rigidbody body;
void Update() {
if (Input.GetKey(KeyCode.LeftArrow())) { // Left arrow key
body.AddForce(transform.forward * speed * Time.deltaTime);
}
}
My problem is, since it's inside of the Update() function, it keeps going, it keeps turning, even if I don't have the key pressed.
Does anyone know how to fix this problem? I have tried taking away Time.deltaTime but Brackeys said you use it so that it's frame rate dependent, so if you have a higher frame rate it doesn't turn faster, and same with a low frame rate, you turn slower. Thanks in advance.
It keeps turning even after releasing the key? Yep that's what AddForce will do.
After you apply a force to an object it will continue to move based on that force uless it has drag or an opposing force is applied. Go into the rigidbody to set the drag or create a script that applies an opposing force once you stop pressing the button.
If you put a Debug.Log within the if statement you will see it is not called every frame but only when you have the button held down.
And last, I don't think that your keycode should have () after LeftArrow, does this not produce an error?
An object in motion stays in motion with the same speed and in the
same direction unless acted upon by an unbalanced force.
---- Isaac Newton
So if you want to stop the object, you can do:
Add an oppsite force
if (Input.GetKey(KeyCode.LeftArrow())) {
...
} else if (!body.IsSleeping()) {
body.AddForce(-body.velocity * factor);
}
Give a drag value
body.drag = dragValue;
You have two questions, one on the title and another one on the description.
For the title one:
The only way to stop/break Update() cycle is to disable the script, like this:
enabled = false;
For the description one:
You have to stop the force applied to the object (as the rest of the answers exemplify).
You can apply an opposite force, or just set velocity to 0.
rigidbody.velocity = Vector3.zero;
Related
I have been programming a deeper version of Pong using skills.
One of the skills is that the player will make the other player's paddle stop, and instead the enemy will start moving its goal.
I start moving the goal with this method. The method is called in the FixedUpdate.
private void moveGoal(string vertical, Vector3 nextPosition)
{
nextPosition += speed * Time.fixedDeltaTime * Input.GetAxis(vertical) * Vector3.up;
nextPosition.y = Mathf.Clamp(nextPosition.y, goalMinY, goalMaxY);
rb.transform.position = nextPosition;
}
We have tried doing it in the Update and using the Time.deltaTime, but the result is the same.
We recently changed to "rb.transform.position" from "rb.MovePosition(nextPosition)" because the problem was way worse.
The position is reset with a method inside the skill's script where we have saved the base position of the goal, and once the skill gets deactivated it automatically reset the goal's position to its base position.
The problem is that if the goal starts in the Y position 1.4, after it has been reset the y position changes slightly, for example going from 1.4 to 1.25.
We do not understand why it is moving even though the position we set it to is always the same.
I am sorry if the post sounds confusing, but the problem itself is very confusing and very difficult to explain.
Sorry if I am misunderstanding your issue. Could it be that you reset the position of the goal but within the same frame it executes moveGoal() once, setting it slightly off your original position? Goodluck.
I was trying to make my object change direction when it moves by 1 unit, so i set its rigidbody.velocity to be 0.25, and set a coroutine that changes its direction every 4 seconds. But i noticed that it has some small inaccuracies (such as changing direction when moved by 0.998), which builds up to a lot after running for some time.
Now I know the best way is probably to just directly change transform.position in this case, but could someone tell me why does my previous method have these inaccuracies?
Edit:
To add a bit of context, im trying to replicate a traditional snake game but with smooth movement(in stead of jumping between the grids). It was a bit hard to explain how the direction can only be changed when the snake has reached a whole unit so i just said that the direction is changed every 4 seconds in the original post. Here is the code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SnakeHead : MonoBehaviour
{
private Rigidbody2D rigidBody;
private Vector2 currDir;
[SerializeField] private float speed;
void Start()
{
rigidBody = gameObject.GetComponent<Rigidbody2D>();
currDir = Snake.Instance.GetDir();
StartCoroutine(Move());
}
IEnumerator Move()
{
while (GameManager.gameIsRunning)
{
currDir = Snake.Instance.GetDir();
rigidBody.velocity = currDir * speed;
yield return new WaitForSeconds(1 / speed);
}
}
}
note that i set speed to be 0.25 in the unity editor window.
im aware that changing transform.position directly is bad practice, but ive watched several tutorials and all of them did that on the snake's movement. Does anyone know if there's a better way?
It's pretty hard to make it such that you change direction exactly after 4 seconds. Using WaitForSeconds(4) will not wait exactly 4 second, instead it will wait at least 4 seconds as correctly mentioned in the comments. It largely depends on how frequently your coroutine is checked by the engine.
As you already mentioned, it would probably be a good idea to do some explicit actions. Either moving it "by hand" (not through the physics simulation), or periodically set the position to a known value.
I have a problem with physics. It is my first time doing in 3D, so it may be just a beginner mistake.
I just wanted to create a simple player controller and make it so that it can not pass trough cubes.
The problem is that when going straight into the cube, part of the player is in the cube itself. When stop moving, it pushes me, so they are not intersecting (that makes sense).
I tried moving the player using .Transalte, .MovePosition and by changing the velocity of rigidbody itself. None of it change anything. The player can always move a part of him into the cube.
Any ideas how to solve this?
My player controller:
(The 2 lines commented out in Move() are just other ways to move the player.)
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField]
private float movementSpeed;
private Vector3 input;
private void Update()
{
GetInput();
}
private void FixedUpdate()
{
Move();
}
private void GetInput()
{
float inputHorizontal = Input.GetAxisRaw("Horizontal");
float intputVertical = Input.GetAxisRaw("Vertical");
input = Vector3.ClampMagnitude(new Vector3(inputHorizontal, 0, intputVertical), 1);
}
private void Move()
{
GetComponent<Rigidbody>().velocity += input * movementSpeed;
//GetComponent<Rigidbody>().MovePosition(GetComponent<Rigidbody>().position + input * movementSpeed * Time.deltaTime);
//transform.Translate(input * movementSpeed * Time.deltaTime, Space.World);
}
}
Player is standing still
Player is moving towards cube
Settings of the Game Objects itself
Now I think I understand your problem.
The collider is a geometric shape that is checked but the outcome wont take place until the collision has actually taken place, this means, one geometric shape being inside the other. By this I mean, that what you are experiencing is the normal behaviour of the collision. If both elemnts are rigid bodies, both will move and your problem wont be perceivable, but if your cube is not a rigid body or is kinematic, will stand still in the same position, and depending on the other object speed, its normal that an invasion/superposition of the elements is perceivable, because that is the frame were the collision took place, and were your element needs to be moved back because it has collided.
Consider that if the speed is high enough, and the position from one frame to another varies enough, the collision might not even take place, because the geometric parts do not interfere between frames, as the position variation might be bigger than the bounds of the collider itself. The collision check at the end of the day, is dicrete, and not continuous (even you can set it as continuous to be as frecuent as possible).
To solve or improve that, you can adjust the speeds to avoid that being perceivable + adjust your collider to make it react before the graphic superposition occurs. This means making the capsule or the cube collider bigger than the graphic element itself. You can even calculate , to make it as bigger as much as your your speed * Time.deltaTime result or speed * FixedTimeStep result, depending on your safety distance needs. I think one of those should be the safety distance you need to take into account before the graphic collision occurs.
Another thing you can do is tight more the times physics calculations are done.
https://docs.unity3d.com/Manual/class-TimeManager.html
But need to be careful with this as this can be a performance devourer and need to be handled with care. You can make some trials with that and check your problem can improve.
Hope that helps
You can increase the scale of your player's collider from the Collider component attached to it. You can check how big the collider is from the editor scene view.
Edit: The issue might be that your movement or collision code is called in Update instead of FixedUpdate. When working with rigidbodies, you want to call the physics calculations inside FixedUpdate.
remove rigidbody from the cube, you can click on 'Gizmos' in the top right of the editor and make sure the colliders are at the edges of the objects.
I need to move some transforms with attached colliders to a specific position, then check if one of them is hit by raycast.
I've done that the naive way (pseudo code) :
foreach(object in objects){
actual_position = object.transform.position
object.transform.position = object.new_position
}
if(Physics.Raycast(...)) objectHit();
// Then I revert each of them them back to their actual_position
After testing multiple times with the same context (same positions between tests for each objects), the raycast sometimes miss, sometimes not (~50/50).
Done a bit of research and found that in the Raycast doc page :
If you move colliders from scripting or by animation, there needs to
be at least one FixedUpdate executed so that the physics library can
update its data structures, before a Raycast will hit the collider at
it's new position.
So I calmed my anger and started looking for a solution.
This thread has a way using a coroutine to wait the next tick :
Raycast doesn't work properly
I'm affraid it won't work for me, as I need the objects to get back to their real position instantly.
This process can happen multiple times per frame (each time a player fire his weapon)
Is there a way to force the colliders update ?
If not... should I make my own raycasts and colliders ? :(
Thanks
Another workaround is to deactivate and activate Gameobject (attached collider) immediately. In this case collider position will be updated in single frame.
Another solution is
Physics.autoSyncTransforms and Physics.SyncTransform
Maybe you can try this out (adding to your pseudo-code):
foreach(object in objects)
{
actual_position = object.transform.position;
object.transform.position = object.new_position;
StartCoroutine(CheckToRevertOnNextFixedUpdate(object, actual_position));
}
IEnumerator CheckToRevertOnNextFixedUpdate(object, actual_position)
{
yield return new WaitForFixedUpdate();
if(Physics.Raycast(...)) objectHit();
// Then I revert each of them them back to their actual_position
}
Essentially this delays your check to the next FixedUpdate() for each object - and reverts each of them if needed. I hope this is not overcomplicated, since you only add a few lines of code.
I'm also assuming that moving the object's position for 1 FixedUpdate() frame would not have a visual effect of the the object teleporting to the new position and back. However, you can always move the collider only, and then move the rest of the transform there after the FixedUpdate().
Performance-wise, the best method seems to be updating the RigidBody.position:
private Rigidbody Rigidbody;
void Start()
{
Rigidbody = gameObject.GetComponent<Rigidbody>();
}
void Upate()
{
//.... your code
Rigidbody.position = newPosition;
}
Much faster then deactivate/activate or Physics.SyncTransform().
I'm trying to get my basic game online and got into some problems.
I followed this tutorial which is really nice and everything worked but one thing, the interpolation.
Without interpolation, I have too much latency issues (the other player is "lagging" a lot). I tried to follow the tutorial, adding
void FixedUpdate()
{
if (networkView.isMine)
else{
syncTime += Time.deltaTime;
rigidbody2D.position = Vector3.Lerp(syncStartPosition, syncEndPosition, syncTime / syncDelay);
}
And using this method:
void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
{
Vector3 syncPosition = Vector3.zero;
if (stream.isWriting)
{
syncPosition = rigidbody2D.position;
stream.Serialize(ref syncPosition);
}
else
{
stream.Serialize(ref syncPosition);
syncTime = 0f;
syncDelay = Time.time - lastSynchronizationTime;
lastSynchronizationTime = Time.time;
syncStartPosition = rigidbody2D.position;
syncEndPosition = syncPosition;
}
}
But now the other player is invisible when not moving, and is visible but flickering a lot when moving (flickering so much I hardly see it). Since my method is called from FixedUpdate, I am wondering if the gameobject is simple refreshing so much that I don't even see it. Is that possible?
In any case, would you have an idea on how to fix this?
Thank you very much for your help in advance!
PS: I tried to lerp only if syncTime/syncDelay < 1.0 to be sure I don't extrapolate, but it did not change a thing.
PPS: When I use the interpolation, it flickers a lot but I can see that the position is right, it is not lagging like before, so the best would be to resolve how to make my interpolation and not removing it.
First of all, do not use Time.deltaTime in FixedUpdate(). FixedUpdate() is for physics only and actually runs around 50fps. On the other hand Time.deltaTime is the time for the last frame. So, technically you will get flickering in your lerp function. Use the function in Update(). And I can see, you are just transforming positions. So, why rigidbody2D.position? you can use transform.position and move the whole codes to Update(). Hope it will help.