Unity C# Player going too fast with Rigidbody2D.AddForce - c#

So I'm trying to make a game in Unity 5. It's a 2D game and I want my character to automatically go forward. But my problem is that since I use Rigidbody2D.AddForce, it keeps adding force every frame and I don't want that. I want my speed to be at 5.0f, not more or less. Is there a way to set a limit or keep a constant speed?
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour {
public Rigidbody2D player;
public bool grounded = false;
private bool hasJumped = false;
public float movementspeed = 5.0f;
public float jumpforce = 450.0f;
// Use this for initialization
void Start () {
player = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
player.AddForce(transform.right * movementspeed);
// (JumpScript)
}
void FixedUpdate(){
// (JumpScript)
}
}

I am very suprised at the answers here, they are way off. Your problem is that you are adding force in the update, which is called every frame. That means that every frame of the game the object will gain 5 more force and speed up infinitely.
If you want the object to get 5 force and then stay at that speed, just put the add force in the start instead of the update. Like this:
// Use this for initialization
void Start () {
player = GetComponent<Rigidbody2D>();
player.AddForce(transform.right * movementspeed);
}
// Update is called once per frame
void Update () {
// (JumpScript)
}

You could just set the Rigidbody.velocity to 5.
However, sooner or later knowing some physics will come very handy. The absolute minimum is knowing Newton's laws.
In your case, if you are absolutely dedicated to using AddForce() you could use ForceMode.Impulse as the 3rd parameter to give your object an instant push (instead of gradual acceleration). This way you would have to call AddForce() only once, not repeatedly. You can calculate the exact amount of force you need by the formula for Kinetic energy:
Force = 0.5f * Mass * Velocity^2

I'm writing a 2D game like yours in unity too. And I use an if to check the velocity and use another variable for the force like this:
public float acel = 50, speed = 5;
Rigidbody2D rbody;
void FixedUpdate () {
// As you want to move right, we don't have to calculate the absolute value
if (rbody.velocity.x < speed)
rbody.AddForce(Vector2.right*acel);
}
The speed won't always be 5 but maybe that isn't important though.

If you want to use Physics to move your player at a constant speed then you need to apply force at the beginning until the speed you want is reached, only then change force to 0. In order for this to work as expected I would also add a Physic Material to the collider of the character to make sure that no friction is slowing down the character.
P.S: If you are creating some sort of infinite runner game I would consider moving and reusing the environment rather than the player.

Related

How can I reduce my character from sliding?

using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed;
public float jump;
private Rigidbody2D rb;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
rb.position += new Vector2(Input.GetAxis("Horizontal"), 0) * Time.deltaTime * speed;
if(Mathf.Abs(rb.velocity.y) < 0.001f && Input.GetKeyDown(KeyCode.W))
{
rb.AddForce(new Vector2(0, jump), ForceMode2D.Impulse);
}
}
So I have this code for my player movement. I am wondering how can I reduce my character from sliding that much. I don't want to stop instantly after I release the key.
You could add counter-movement to make the movement to feel more responsive, or you could change the friction by adding a physics material. Counter-movement makes the player stop by adding a force opposite to the wanted direction of the movement. It will stop the player from sliding too much. Another approach is to add a physics material and up the friction a bit. This will make the player stop faster. I hope you find this helpful!
Inside of the Input Manager, Edit->Project Settings->Input Manager, there is a property called gravity.
Gravity: Speed in units per second that the axis falls toward neutral when no input is present.
Decreasing this value will cause the input to fall quicker, resulting in less/no sliding.
You can debug your input value to confirm this. You should notice a ramp up from 0 to 1/-1 when you first hold the horizontal input. Once you let go of the input, you should see the value fall back down to 0.
var inputHorz = Input.GetAxis("Horizontal");
Debug.Log(inputHorz);
Lower the value until it feel correct. This can be changed while you are playing the game, but you will need to paste that value back in after pressing stop.

Player unable to move after being teleported/transported Unity3d

I've been trying to fix this one bug in my code for over 7 hours now, upon being teleported, the movement controls cease to function, the mouse works fine, you can look around, but you can't move around.
I wanted to set up some simple code that would teleport the player to a "checkpoint" upon achieving a negative or null y level. I was doing this for a parkour based game, if the player fell off the platform they would have to start over, but after teleporting, it becomes impossible to move as I'm sure I have already said. My code is pretty simple:
public class Main : MonoBehaviour
{
float Fall;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 Checkpoint = new Vector3 (0,3,0);
GameObject Player = GameObject.FindGameObjectWithTag("Player");
Fall = GameObject.FindGameObjectWithTag("Player").transform.position.y;
if (Fall<-4)
{
Player.transform.position = Checkpoint;
}
}
}
You would think that this would just simply change the coordinates of the player, but I think this might be screwing with the FPSController script.
I am using Unity3d, with Standard Assets imported, All of the code is in C#.
Instead of checking the Y value of your character, I would instead place a death collider under the map. Make this a trigger and if the player touches this trigger, then teleport them back. Nothing with your code should screw with the FPS controller so it might be something else. I would also highly recommend not using a FindGameObjectWithTag in the Update() method as it is extremely expensive to use this every frame, especially twice. If you would rather keep the Update() Y component of the position check, please rewrite the code to something like this:
public class Main : MonoBehaviour
{
// assign this object of your player in the inspector - it stores the reference to reuse
// instead of grabbing it every frame
[SerializeField] private Transform playerTransform = null;
// make this a variable as it is not changing - might as well make this const too
private Vector3 checkpoint = new Vector3(0, 3, 0);
// constant value of what to check for in the Y
private const int FALL_Y_MARKER = -4;
// Update is called once per frame
void Update()
{
if (playerTransform.position.y < FALL_Y_MARKER)
{
playerTransform.position = checkpoint;
}
}
}
With your current code, there should be nothing breaking your input/movement, but with that said, we can not see your input/movement code. All the above snippet does is check if the Y component of the player objects position is below a certain value, and if it is, it sets the position to a new vector. Can you post a bit more movement code or somewhere else it can go wrong that you think is the issue?

How to cycle a coroutine every 10 seconds to accelerate a scrolling background?

I'm an absolute newbie in Unity, and I'm trying to create a 2D game that requires a scrolling background that accelerates every 10 seconds. I'm having trouble getting the code to work
I've tried to set up a Coroutine, but it seems to call the function every frame, instead of every 10 seconds
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScrollingBackground : MonoBehaviour
{
public float bgSpeed = 5;
public Renderer bgRend;
public float increment = 2f;
private void Start()
{
StartCoroutine(Accelerate());
}
void Update()
{
bgRend.material.mainTextureOffset += new Vector2(0f, bgSpeed * Time.deltaTime);
StartCoroutine(Accelerate());
}
private IEnumerator Accelerate()
{
while (true)
{
bgSpeed = increment * Time.deltaTime;
yield return new WaitForSeconds(10f);
Debug.Log("Getting Faster!");
Debug.Log("OnCoroutine: " + (int)Time.time);
}
}
}
Not only is the background speed going very slowly (only up to around 0.3 and stuck), I can't seem to make this work. Thank you for you help !
First, you should only start your coroutine once, such as in Start - instead, you're starting it every frame, so the speed is going to get stuck.
Second, is your texture scaled at all? This may affect how it looks when you're adjusting the offset directly, and thus why it appears to be stuck at 0.3 even though your increment is 2f here.
Finally, note that deltaTime is the time since the last frame, so using to adjust your speed rather than adding a constant is going to produce strange results dependent on performance. It's useful when applying the speed to movement, but not the acceleration based on this game logic.
It is calling your function every frame because you start the coroutine inside of Update. See Update scripting reference.

Unity - How do I increase a value while a key is held down?

public int power;
// Start is called before the first frame update
void Start()
{
player = GameObject.Find("Whyareyoulikethis");
while (Input.GetKey(KeyCode.Space))
{
power = power + 10;
}
// Places the ball at the player's current position.
transform.Translate(-player.transform.forward);
rb = GetComponent<Rigidbody>();
rb.AddForce(-player.transform.forward * power);
}
What this is meant to do is while the space key is held down, power will increase by 10. Unfortunately, this does absolutely nothing. When the ball is spawned, it simply just drops down with no force added whatsoever. I have also tried GetKeyUp and GetKeyDown as opposed to Getkey, but they made no difference to the final result. I have also tried this in an if statement under void Update(), but the same happened. As stupid as it was, I also tried it in its while statement under void Update() and crashed the engine as expected.
That while loop blocks your game until it is done. So as soon as you enter it you will never come out since the Input is not updated inside of your while loop.
Also it makes no sense in Start which is only called once when your GameObject is initialized and the space key won't be pressed there.
Move the check for Input.GetKey it to Update which is called every frame.
Than Cid's comment is correct and this will increase the power quite fast and frame dependent. You probably want to increase rather with a frame-independent 60/second so rather use Time.deltaTime
in this case power should be a float instead
Than it depends where the rest should be executed but I guess e.g. at button up
public float power;
private void Start()
{
player = GameObject.Find("Whyareyoulikethis");
rb = GetComponent<Rigidbody>();
}
private void Update()
{
if(Input.GetKey(KeyCode.Space))
{
power += 10 * Time.deltaTime;
}
if(Input.GetKeyUp(KeyCode.Space))
{
// Places the ball at the player's current position
transform.position = player.transform.position;
// you don't want to translate it but set it to the players position here
// rather than using addforce you seem to simply want to set a certain velocity
// though I don't understand why you shoot backwards...
rb.velocity = -player.transform.forward * power;
}
}

Slow collision detection at low frame rates

I'm experiencing an odd issue with my collision detection. I'm using the Update method to move the player (I don't want to use FixedUpdate because that creates an undesired weird movement). The fixed timestep is set at the default 0.02 (I tried playing with time setting but that didn't work either) . I set the collision detection of the rigidbodies of both objects to "continuous dynamic". Also, I set the target frame rate to 300 and that didn't change anything...
When the framerate is low or the device itself is slow, the collision detection doesn't always work. The player can easily fall through the object it's supposed to collide with, though sometimes it doesn't.
Please tell me what I can do to fix this because I've published a game and many users are reporting this (serious) bug. Thank you for your support.
This is what is supposed to happen:
This is what actually happens:
(as you can see, the cube gets out of the wall and to the other side)
I move the player when the user releases the mouse button:
Script 1:
public Script2 Jumper;
public float TimeToJump;
public void Update()
{
if (Input.GetMouseButtonUp(0))
{
StartCoroutine (Delay (1f/50f)); //Don't mind the time.
}
}
IEnumerator Delay(float waitTime)
{
yield return new WaitForSeconds (waitTime);
if (Jumper != null)
{
Jumper.SetVelocityToJump (gameObject, TimeToJump);
}
}
Script 2 attached to player (cube):
public class Script2 : MonoBehaviour {
GameObject target;
private float timeToJump;
public bool isJumping = false;
public void SetVelocityToJump(GameObject goToJumpTo, float timeToJump)
{
StartCoroutine(jumpAndFollow(goToJumpTo, timeToJump));
this.timeToJump = timeToJump;
this.target = goToJumpTo;
}
private IEnumerator jumpAndFollow(GameObject goToJumpTo, float timeToJump)
{
var startPosition = transform.position;
var targetTransform = goToJumpTo.transform;
var lastTargetPosition = targetTransform.position;
var initialVelocity = getInitialVelocity(lastTargetPosition - startPosition, timeToJump);
var progress = 0f;
while (progress < timeToJump)
{
progress += Time.deltaTime;
if (targetTransform.position != lastTargetPosition)
{
lastTargetPosition = targetTransform.position;
initialVelocity = getInitialVelocity(lastTargetPosition - startPosition, timeToJump);
}
float percentage = progress * 100 / timeToJump;
GetComponent<Rigidbody>().isKinematic = percentage < 100.0f;
transform.position = startPosition + (progress * initialVelocity) + (0.5f * Mathf.Pow(progress, 2) * _gravity);
yield return null;
}
OnFinishJump (goToJumpTo, timeToJump);
}
private void OnFinishJump(GameObject target, float timeToJump)
{
if (stillJumping)
{
this.isJumping = false;
}
}
private Vector3 getInitialVelocity(Vector3 toTarget, float timeToJump)
{
return (toTarget - (0.5f * Mathf.Pow(timeToJump, 2) * _gravity)) / timeToJump;
}
}
The target of the cube is a child of the bigger cube (the wall).
If you require clarification, please leave a comment below. I might give the link to my game if you need more details.
Quote from here (found thanks to #Logman): "The problem exists even if you use continuous dynamic collision detection because fast moving objects can move so fast that they are too far apart from itself from one frame to the next immediate frame. It's like they teleported and no collision detection would ever be triggered because no collision existed, from each frame perspective, and thus from all calculations processed."
In my case, the cube is not going fast, but you get the concept.
There are several issues with your code.
You are asking a Coroutine to yield for 1/50th of a second. The minimum time a yield must occur for is one frame. If Time.deltaTime > 0.02f this is already one of the problems.
You are using Coroutines and yield return null to compute physics calculations. Essentially, you're computing physics in Update(), which is only called once per frame (null is equivalent to new WaitForEndOfFrame(): as mentioned in (1), a running Coroutine cannot be yielding between frames). Under low frame-rate, the amount of motion an object undertook between two frames might exceed the collision range of the target trigger. Assuming linear, non-accelerating motion: ∆S = v∆t where v = velocity, ∆S is movement to cover in the current frame, ∆t is Time.deltaTime. As you can see, ∆S scales proportionally with ∆t.
You have GetComponent<T>() calls inside loops. Always avoid doing this: store a reference as a member variable instead (initialise it in Start()).
My suggestion for the quickest working hack would be to not worry too much about "being clean", and instead create subroutines that you call from FixedUpdate(), and (create and) use member bools to conditionally test which subroutine to "execute" and which to "skip". You can also use member bools or enums as triggers to switch between various "states".
A better solution would be to let Unity handle the kinematics and you instead work with rigidbody mutators (and not transform.positions), but that may be totally unnecessary for an arcade situation, which yours might be. In that case stick to the hack above.
If you really want to control kinematics by hand, use an engine like SFML. A Particle System tutorial would be a good place to start.
It's your float percentage, among other things.
"If isKinematic is enabled, Forces, collisions or joints will not affect the rigidbody anymore."
That's from the isKinematic page of Unity's documentation. You're setting it to true when progress hits 100. So at lower framerates, there'll be a sudden jump due to Time.deltaTime steps being a lot higher, progress is suddenly >= 100, isKinematic is set to true and the player is no longer affected by collisions.
I think you're going to have to rethink a lot of the code here and do some heavy optimisations. But the other posters have laid those out already, so I don't need to.
EDIT: Misunderstood the initial question, thought that it meant you were trying to detect collisions but your code wasn't always detecting them. Didn't realise it actually meant getting the collisions to occur in the first place.

Categories