How to stop drag after reaching a specific speed - unity3d

I have the following code which adds force to a rigidbody2d in a random direction and attempts to set the linear drag to 0 when a specific speed is reached, essentially letting the gameobject slowly float away.
The problem is that the drag seems to be set to 0 straight away, and I do not understand why, and therefore I am unable to solve the problem
void Update () {
if (Input.GetKeyDown ("space")) {
rb.velocity = Vector3.zero;
Vector3 dir = Random.onUnitSphere;
rb.AddForce (dir * 10, ForceMode2D.Impulse);
}
if (rb.velocity.magnitude <= 1) {
rb.drag = 0;
}
}
Thank you
Crouz

As #Maakep said, the problem comes from the fact you set the Rigidbody velocity to 0 and check if this velocity is <= 1 right afterward (the force is only applied later).
To solve your problem you could do something like this:
public bool applyNextFrame = false;
protected override void Update()
{
if(applyNextFrame && rb.velocity.magnitude <= 1)
{
rb.drag = 0;
applyNextFrame = false;
}
if(Input.GetKeyDown(KeyCode.Space))
{
rb.velocity = Vector3.zero;
Vector3 dir = Random.onUnitSphere;
rb.AddForce(dir * 10, ForceMode2D.Impulse);
applyNextFrame = true;
}
}
Keep in mind this solution is far from flawless since you can have a problem if your Rigidbody configuration (mass, drag and force applied) applies different velocity values: therefore, you won't be able to fully control when the drag will reach 0 (this may be completely intentional depending on your project !) :)
You can look to the execution order diagram located in this page for further information: you can see the Internal physics update happens before the Update() (when you call rb.AddForce(...) it's actually only applied during the next Internal physics update).
Also note I changed Input.GetKeyDown("space") to Input.GetKeyDown(KeyCode.Space): this avoids typo problems if you mistype space.
Hope this helps,

Related

turning towards an object in Unity

Good times. How do I implement the NPC rotation towards the selected object?
public GameObject BufferObject;
public float MoveSpeed = 1f;
void Update()
{
float step = MoveSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, BufferObject.transform.position, step);
}
Here is the script for moving the NPC to the selected object (Buffer Object) and the movement works perfectly, but the implementation of the rotation causes Me difficulties, please advise.
For Unity2D.
Simply get the desired direction
var direction = (BufferObject.transform.position - transform.position).normalized;
and then the desired rotation using Quaternion.LookRotation like e.g.
var targetRotation = Quaternion.LookDirection(direction);
Then either apply it directly if you want it immediately
transform.rotation = targetRotation;
Or if you want it smooth you could use e.g. Quaternion.RotateTowards like
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, anglePerSecond * Time.deltatime);
Have in mind though that it might look awkward if the rotation is too slow since your NPC could move back/sidewards already while still not fully looking towards the target ;)
So you might want to wait until
if(Quaternion.Angle(targetRotation, transform.rotation) <= certainThreshold)
{
... your movement code
}
So the answer and the solution from Me, albeit stupid, but working. In order to reflect the sprite, you need to get a variable, either 1 or -1 (For Scale). This code will show the distance from one NPC to the object:
BufferObject.transform.position.x - transform.position.x
And here I get a lot of values as if to the left of the NPC, then -x... , and if to the right, then x... thereby it is possible to determine which side of the object, so also level the value from here (Optional) to 1 or -1 and apply this result to transform.localScale and thereby solve the problem of how to reflect (Rotate the sprite) towards the object. Use it for your health :)
Complete code:
float localPositionAmount = BufferObject.transform.position.x - transform.position.x;
if (localPositionAmount >= 1)
{
gameObject.transform.localScale = new Vector3(1, transform.localScale.y, transform.localScale.z);
}
if (localPositionAmount <= -1)
{
gameObject.transform.localScale = new Vector3(-1, transform.localScale.y, transform.localScale.z);
}
if (localPositionAmount == 0)
{
gameObject.transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y, transform.localScale.z);
}
Yes, the code is the simplest and without enumerations and other things, but it works perfectly. I still had to figure it out myself...

Player stops move the character direction resets [Unity 2D]

My character is a car and I try to rotate it the direction it move, so far so good I succeeded to do that but once I stop moving the character flips back to the direction it was on the start.
Also how can I make my turns from side to the opposite site smooth ?
Here is my code so far:
[SerializeField] float driveSpeed = 5f;
//state
Rigidbody2D myRigidbody;
// Start is called before the first frame update
void Start()
{
myRigidbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
Move();
}
private void Move()
{
//Control of velocity of the car
float HorizontalcontrolThrow = CrossPlatformInputManager.GetAxis("Horizontal"); // Value between -1 to 1
float VerticalcontrolThrow = CrossPlatformInputManager.GetAxis("Vertical"); // Value between -1 to 1
Vector2 playerVelocity = new Vector2(HorizontalcontrolThrow * driveSpeed, VerticalcontrolThrow * driveSpeed);
myRigidbody.velocity =playerVelocity;
**//Direction of the car**
Vector2 direction = new Vector2(HorizontalcontrolThrow, VerticalcontrolThrow);
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
myRigidbody.rotation = angle;
}
I'm not sure about this, but maybe that last line "myRigidbody.rotation = angle" being called every frame is what is making your car reset its rotation.
Maybe change it to "myRigidbody.rotation *= angle" or "myRigidbody.rotation += angle".
It looks like it may be because HorizontalcontrolThrow and VerticalcontrolThrow are going to be reset when you release the controls. If it's resetting to its original orientation, then what's happening is that until you move, those two values are going to be at their default value. You then move and it affects the rotation. But when you release the controls, those values are back to the starting values again, and so is your rotation.
What you therefore need to do is try to separate the HorizontalcontrolThrow and VerticalcontrolThrow from the rest of the code, which should only be activated when at least one of these two variables are not at their default setting (I can't remember what the axis functions return at the moment).
Edit:
An IF statement should suffice (some rough pseudo code):
if (horizontalAxis != default || verticalAxis != default)
{
Rotate/Move
}
I solved the snap rotation using Quaternion at rotation, the issiu I had with it was to convert it from 3d to 2d, through the guide of this clip: youtube.com/watch?v=mKLp-2iseDc and made my adjustments it works just fine !

GameObject is vibrating throughout the movement

I have a GameObject that takes position coordinates from a file. This GameObject moves with vibration rather than smoothly. (as if moving back and forth.)
Here is the code that provides the movement:
int counter = 0;
void Update()
{
var maxDistance = speed_* Time.deltaTime;
var oldPosition = transform.position;
var newPosition = Vector3.MoveTowards(transform.position, positions[counter], maxDistance);
var actualDistance = Vector3.Distance(newPosition, oldPosition);
var distanceRemainder = maxDistance - actualDistance;
if (distanceRemainder > 0.0001)
{
newPosition = Vector3.MoveTowards(newPosition, positions[counter], distanceRemainder);
counter++;
}
transform.localPosition = newPosition;
}
NOTE: The data read from the file is in the "positions" array (x,y,z coordinates).
When I lower the 300f value in the variable maxDistance, the vibration stops and the motion becomes more fluid. However, Gameobject speed is also slowing down. How can I ensure a fast and smooth movement?
SOLUTION:
While looking for a solution of the problem, I came across the this topic. It helped me learn the source of the problem.
I have observed that the GameObject is not vibrating in Scene view, it was moving smoothly. But the object seemed to be vibrating in GameView. The problem is not the moving object, it's the camera function I write to follow it.
The camera function that was written to follow the object has been updated and the problem has disappeared.
One can see more of the solution by going to Post #13 in the link above.
Thanks to everyone trying to help.
This is caused cause you are using your distanceRemainder as your maxDistanceDelta, which I think is incorrect, if you want a smooth movement, you should multiply it for the Time.deltaTime, try:
newPosition = Vector3.MoveTowards(newPosition, positions[counter], distanceRemainder*Time.deltaTime);
Or simply declare speed variable and do:
newPosition = Vector3.MoveTowards(newPosition, positions[counter], speed*Time.deltaTime);
I assume what you want is your object moving with a constant speed to the first position. Once it reaches it, move to the next one.
I would do it in a simple Coroutine which is better to understand and maintain than doing stuff in Update:
private void Start()
{
// todo assign positions
StartCoroutine(RunPositions());
}
private IEnumerator RunPositions()
{
foreach(var position in positions)
{
while(!Mathf.Approximately(Vector3.Distance(transform.position, position), 0))
{
var maxDistance = speed_* Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, positions[counter], maxDistance);
// render and continue in the next frame
yield return null;
}
}
}
If you are fine with a precision of 0.00001 you can also simply use
while(transform.position != position)
instead.

IsGrounded in CharacterController not stable

I have following simple scene: scaled box at (0, 0, 0) for floor and 1 size box at (0, 2, 0). I try to do simple gravity and use following code:
private float gravity = 1.0f;
private Vector3 moveVector;
void Update()
{
if (characterController.IsGrounded)
{
Debug.Log("is grounded");
verticalVelocity = 0;
}
else
{
Debug.Log("not grounded");
verticalVelocity -= gravity;
}
Debug.Log("vertical velocity:" + verticalVelocity);
moveVector.x = 0;
moveVector.y = verticalVelocity;
moveVector.z = 0;
characterController.Move(moveVector * Time.deltaTime);
}
I see in log that event when object is on the floor I have grounded/not grounded messages. Visually object is on the floor and not oscillating.
Why I don't have constant "is grounded" when object is on the floor?
Maybe it is how CharacterController works but I can't find anything about it in documentation.
You have to set Character Controller Min Move Distance to '0' (zero) or else you will always get this behavior, it is trying to limit how many times it checks if it's grounded, but at the end of the day gamers don't care, they will mash the jump button and call it buggy
Had the same issue
Just had some gravity when the character controller is grounded
Replace verticalVelocity = 0;
By
verticalVelocity = -gravity * Time.deltaTime;
Setting "Min Move Distance" to 0 in the Character Controller Inspector solved a very similar problem for me.
Strubble is right. You have to add some small gravity even if character is grounded.
if (characterController.IsGrounded)
{
// Press the character down to the floor to avoid jitter "true-false"
// of the isGrounded property.
// To do it, add some small gravity (or velocity in your terms).
verticalVelocity = -gravity * 0.1f;
}
just use a Raycast with Vector3.down because I've tired of fixing it and it's really unreliable to use even while prototyping.

Unity 3D game jumping randomly

I am developing my first game with the Unity3D engine and I have run into my first problem! Not as exciting as I thought. If I spam the jump button w my ball jumps the first time then when it lands it does not jump right away but it jumps randomly after a number of button presses. The code I am using is below.
#pragma strict
var rotationSpeed = 100;
var jumpHeight = 8;
private var isFalling = false;
function Update ()
{
//Handle ball rotation.
var rotation : float = Input.GetAxis ("Horizontal") * rotationSpeed;
rotation *= Time.deltaTime;
rigidbody.AddRelativeTorque (Vector3.back * rotation);
if (Input.GetKeyDown(KeyCode.W) && isFalling == false)
{
rigidbody.velocity.y = jumpHeight;
}
isFalling = true;
}
function OnCollisionStay ()
{
isFalling = false;
}
I heard this was a arithmetic behavior problem in UnityScript. I am a very beginner at programming and do not really understand the code in UnityScript and this is the first game/project I am making in Unity3D. Help would be greatly appreciated. Thanks!
You would think that something as simple as jumping should be a no-brainer, but there are a couple of gotchas here:
It can happen that Update() is called twice without any physics updates in between. This means you don't receive OnColliderEnter nor OnColliderStay in between but you still reset isFalling, blocking the jump key.
The moment the ball hits you will receive OnColliderEnter, but not OnColliderStay. This won't happen until the next physics update. If you only listen to -stay you will be one update late.
If you press jump right at the time the ball is supposed to hit the ground then your key is already down when the first collider hit registers. You will be another update late.
Objects sink a little bit into the collider. When you jump, it might take a couple of updates before you clear the collider and you will receive OnColliderStay after you jumped.
I think that last point is the biggest problem here. To solve this you need to do a couple of things:
Use both OnColliderEnter and OnColliderStay. This will imrpove your timing with one update.
Reset isFalling in FixedUpdate instead of in Update. FixedUpdate is part of the physics loop, so it is called right before OnCollisionEnter and OnCollisionStay who will set it again immediately if needed.
Remember that you are trying to jump until you are actually in the air. This allows you to clear the collider with one button press.
The code below implements these points. If you want the timing to be even tighter you must queue the next jump while you are in the air.
#pragma strict
var rotationSpeed = 100;
var jumpHeight = 8;
private var isFalling = false;
private var tryingToJump = false;
function Update ()
{
//Handle ball rotation.
var rotation : float = Input.GetAxis ("Horizontal") * rotationSpeed;
rotation *= Time.deltaTime;
rigidbody.AddRelativeTorque (Vector3.back * rotation);
if (Input.GetKeyDown(KeyCode.W) && !isFalling) tryingToJump = true;
if (tryingToJump)
{
if (isFalling) tryingToJump = false;
else rigidbody.velocity.y = jumpHeight;
}
}
function FixedUpdate()
{
isFalling = true;
}
function OnCollisionStay()
{
isFalling = false;
}
function OnCollisionEnter()
{
isFalling = false;
}