Game does not take input from keyboard consistantly - c#

I am new to Game dev and started with the 7-hour free course on youtube. I followed the course very carefully and stumbled against this problem. As i have learn so far this is how to get the 2D game object to jump when i press Space:
void PlayerJump()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
MyBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}
But when i compiled and run my game, the game object did not jump right away when i pressed Space. It took about 5,6 times to make it jump once, and about the same amount each time later. I'm not sure if this is an Input problem. I swear I did not miss anything in the course until this.
I looked for mistake i might have in my code and it's clean. I expect to make it jump right when i press Space

First I would like to demonstrate that the code from the tutorial does indeed miss valid space bar presses.
// Update is called once per frame
void Update()
{
PlayerMoveKeyboard();
AnimatePlayer();
if (Input.GetButtonDown("Jump"))
{
Debug.Log("pressedUpdate" + isGrounded);
}
}
private void FixedUpdate()
{
PlayerJump();
}
void PlayerJump()
{
if (Input.GetButtonDown("Jump"))
{
Debug.Log("pressedFixed" + isGrounded);
}
if (Input.GetButtonDown("Jump") && isGrounded)
{
Debug.Log("jump");
isGrounded = false;
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}
If you compile this and play around for a bit you can get the following output in the log:
As you can see the update loop has detected the button press 6 times and only on the last one the fixed update loop detected the input and executed the jump.
Something you need to know is that Input.GetButtonDown is only true for the frame that it is pressed down. By default Unity runs in 60 fps and therefore the input system is checked approximately every 0.017 seconds. When you press the space bar the input system learns about that on the next frame. For the next 0.017 seconds the button is considered to be pressed down.
Parallel to this FixedUpdate is called every 0.02 seconds (default time step), which is a little bit more than the duration of a frame.
Consider the following scenario:
Unity checks the input in the FixedUpdate and detects that space wasn't pressed yet
runs the frame related code and the input system detects that space is pressed down
0.017 seconds pass and it's time for the next frame where the input system decides that the button is no longer pressed down, but rather held down
since 0.02 seconds have passed now Unity quickly goes back to check on the FixedUpdate, queries the input system once more, which reports that the button wasn't pressed down
Depending on your timing pressing the button this can happen a number of times in a row, or not happen at all.
The lazy solution would be so simply decrease the physics time step to be smaller than the frame rate step. You can go to the Project Settings in the Time menu and set the Fixed Timestep to something like 0.01. Problem with this approach is that you make physics more expensive and don't actually address the issue, since it will crawl back if you run the game in 120 fps for example.
Another easy solution is to simply move the the PlayerJump function into the Update loop. As the guy in the tutorial explained it is technically not correct to do this, since you should keep all physics related stuff in FixedUpdate, but you won't notice the collision related issues in such a simple game.
Finlay there is the "proper" way to resolve your problem by implementing a lenience system with a bit more advanced code. Instead of the jump button only counting as pressed down for one frame you can make it so it is considered pressed down for an arbitrary long amount of time.
private bool jump;
[SerializeField] private float lenience = 0.2f;
// Update is called once per frame
void Update()
{
PlayerMoveKeyboard();
AnimatePlayer();
JumpInput();
}
private void FixedUpdate()
{
PlayerJump();
}
void JumpInput()
{
if (Input.GetButtonDown("Jump"))
{
CancelInvoke("Lenience");
jump = true;
Invoke("Lenience", lenience);
}
}
private void Lenience()
{
jump = false;
}
private void CancelLenience()
{
CancelInvoke("Lenience");
jump = false;
}
void PlayerJump()
{
if (jump && isGrounded)
{
CancelLenience();
isGrounded = false;
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}

Related

How to handle FixedUpdate() and Input in Unity?

As far as I know, in Unity, physics related things should be done in FixedUpdate() method.
And FixedUpdate() is called at fix timestamp and Update() is not. Let's asuume I have a gameobject with RigidBody2D component attached and get the user input in Update(). User can move the object with left and right arrow. See the fllowing figure,
user input happen at frame 2 and 3 and FixedUpdate() is not called. During frame 2
user pressed right arrow and say it move the object 1 unit and object is currently located at
(0, 0). The object position should be now at (1, 0). But nothing happen because FixedUpdate()
wasn't called. And again in frame 3 FixedUpdate() is not called and user press the left arrow.
So, it move the object 1 unit left and now the position is (0, 0). When we reached frame 4
FixedUpdate() is called and if I put the input data in a variable at previous frame, frame 3,
current object position will be (0, 0). So, in this scenario our gameobject never reached
the position (1, 0) but it's supposed to be. Probably it's not the only problem. There might
exist many scenario like called FixedUpdate() 2 times within one frame and called FixedUpdate()
only every 3 or 4 frames and so on. So, should I ignore such case because I think it's not too obivious to user. If I shouldn't, how can I handle it? Do I need to queue all the input data and do all the movement when the FixedUpdate() is called? What I tested is store movement data in a variable and if movement occurred then put true to a flag. If the flag is true the object will be moved inside FixedUpdate(). But the problem is it can handle only one input. If FixedUpdate() is not called the next movement will never be happen.
void FixedUpdate()
{
if(moved)
{
Vector2 movePos = rb2d.position + new Vector2(x, 0);
rb2d.MovePosition(movePos);
moved = false;
}
}
void Update()
{
if(!moved)
{
x = Input.GetAxisRaw("Horizontal");
if (x != 0) moved = true;
}
}
FixedUpdate isn't guaranteed to run on a fixed schedule because Unity is designed to deal with lag. Instead, whenever Update is called, Unity checks if any FixedUpdates should have happened between the previous Update and this one. If so, multiple FixedUpdates may run in sequence. If not, no FixedUpdates will run. So the FixedUpdate timing is actually linked to the Update timing, but not very predictably.
Generally, I recommend staying away from FixedUpdate and instead handling movement indirectly through the physics system in Update, as anything that needs to happen in FixedUpdate will get aggregated before it needs to be run. For example, if you change a rigidbody's velocity multiple times in Update, then FixedUpdate should still work as expected.
For single event inputs (GetKeyDown) it might be sometimes important to split the behavior and get the Input within Update but apply it in FixedUpdate (again this depends a lot on your use case still).
However, for continues input like GetKey or your GetAxisRaw it doesn't matter where you handle it (again depending on your exact use case of course). MovePosition is recommended to be called in FixedUpdate so in your case you could simply do
private void FixedUpdate()
{
rb2d.MovePosition(rb2d.position + Vector2.right * Input.GetAxisRaw("Horizontal");
}
What I mean by "it depends on your use case" is that you could achieve the same thing using rather the velocity in which case it wouldn't matter at all whether you do it within Update or FixedUpdate:
//private void Update ()
private void FixedUpdate()
{
// This is even better since you keep the Y velocity unchanged
// => Gravity will still work while it wouldn't using MovePosition!
var velocity = rb2d.velocity;
velocity.x = Input.GetAxisRaw("Horizontal");
rb2d.velocity = velocity;
}
for both Update or FixedUpdate the movement will be the same. If you use Update some frames might just have no effect as the velocity is applied in the next FixedUpdate call and in the meantime interpolated using the settings on your Rigidbody.

Unity3D C# - speed issues with `Input.GetMouseButton(0)` [SOLVED]

I've been struggling with the movement of my player since its movement was based on updating its world position with time frames. Using Update or FixedUpdate didn't change anything.
Since, I setted up another movement which involved a lot of changes in my code, this time based on speed (* Time.delaTime) and Update make my player moving faster when the mouse button is kept down, but FixedUpdate fix the problem and whatever if I just clic or keep the mouse down my player keep the same speed.
Code:
void FixedUpdate()
{
if(Input.GetMouseButton(0))
{
Vector3 target = GetMouseWorldPosition();
transform.position = Vector3.MoveTowards(transform.position, target , speed * Time.deltaTime);
Good luck and have fun :)
Issue 1
As you are already using Coroutine, you can use yield return new WaitForSeconds (1) instead of yield return null. you can adjust the number to set the wait duration between steps. Don't purposely slow down your CPU to achieve anything about timing. And don't mess up with Fixed Update time it's for physics simulation.
Issue 2
change it to yield return new WaitForSeconds (Input.GetMouseButton(0)?.5f:1);, so it waits shorter time if mouse button is held.
Issue 3
if(pathfinding==null || grid=null){
...(do random movement)
}else{
...(do pathfinding movement)
}
EDIT 1
if you don't want teleporting, you need to write transition yourself.
Vector3 oldPos = transform.position;
Vector3 newPos = nodeWaypoint;
float time = 0;
while(time<1){
yield return null;
time +=Time.deltaTime;
transform.position = Vector3.Lerp(oldPos,newPos,time);
}
Or just use DoTween
but TBH, your coding is quite problematic. it will keep doing pathfinding and calling UpdatePosition when the player holding click.

Background jitter with v-sync off in standalone build (high fps/300+ fps)

So the last 2 months I had been reworking my PlayerController to be rigidbody based for the sake of having more interactions with rigidbodies i.e. pushing objects. This went all pretty well, until I started testing my game in a standalone build. I immediately noticed strange jitter/stutter in the background (the player runs smooth) when v-sync is turned off (with v-sync is turned off my computer runs the game at 300fps+). At first I thought it had something to do with Cinemachine, I'm using CinemachineFreeLook, but to be honest as of now I'm unsure if Cinemachine is what's causing the jitter/stutter. Something worth noting is that the jitter/stutter is not constant, while the fps seems constant the jitter/stutter might go away for like 20 seconds and come back after. I have obviously done alot of research the past week and have been reading plenty of posts about FixedUpdate vs. Update, but unfortunately without any luck.
I tried to add as much information as possible to this post to give a good understanding of my project, but first the stuff I already tried;
Changing the Fixed Timestep from 0,02 to 0,01. (This kind of works,
but I would like to prevent changing the Fixed Timestep)
Turning on interpolate/extrapolate. (Now there is jitter with both
v-sync turned on and off)
Changing the CinemachineBrain Update Method to FixedUpdate/LateUpdate
instead of SmartUpdate.
Deconstructing the scene and removing some heavy on performance stuff
like Volumetric Lighting and Planar Reflections.
Creating a really barebones PlayerController with only the code to
move the player.
Removing the library folder of my project. (Read this somewhere
online, but no luck)
Turning of my Xbox One controller, to make sure there is only one
input in use.
Swithed the building from Windowed Fullscreen to Exclusive
Fullscreen.
Turned off one monitor while testing.
Move all my code to FixedUpdate.
Added these 3 scripts and changed the Script Execution Order
(Couldn't get this to work)
Put the input logic in FixedUpdate.
Below you can see both my movement scripts, one is part of my actual PlayerController, while the other was the one I wrote for the barebones Player.
void FixedUpdate() {
_isGrounded = Physics.CheckSphere(_groundChecker.position, GroundDistance, Ground, QueryTriggerInteraction.Ignore);
_inputs = Vector3.zero;
_inputs.x = Input.GetAxis("Horizontal");
_inputs.z = Input.GetAxis("Vertical");
_inputs.Normalize();
if (_inputs != Vector3.zero)
transform.forward = _inputs;
_body.MovePosition(_body.position + _inputs * Speed * Time.fixedDeltaTime);
}
This is the script I wrote for the barebones player controller.
void FixedUpdate() {
if (pushControllerScr._pushBlocker) {
input = Vector3.zero;
} else {
input = new Vector3(InputManager.MainHorizontal(), 0, InputManager.MainVertical());
}
RunAxis();
inputDir = input.normalized;
inputDir.Normalize();
// Makes the player able to move, while not landing or being dead
if (!landState && !climbManagerScr.isClimbing) {
Move(inputDir, running);
}
}
void Move(Vector3 inputDir, bool running) {
if (inputDir != Vector3.zero) {
float targetRotation = Mathf.Atan2(inputDir.x, inputDir.z) * Mathf.Rad2Deg + cameraT.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));
}
float targetSpeed = ((running) ? runSpeed : movementSpeed) * inputDir.magnitude;
stateInfoBaseLayer = _anim.GetCurrentAnimatorStateInfo(0);
if (stateInfoBaseLayer.IsTag("Climbing") || stateInfoBaseLayer.IsTag("Standing")) {
currentSpeed = 0f;
} else {
currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
}
if (stateInfoBaseLayer.IsTag("AirIdle") && currentSpeed > walkSpeed) {
currentSpeed = walkSpeed;
}
velocity = transform.forward * currentSpeed + Vector3.up * _rb.velocity.y;
if (_capsuleCol.enabled) {
_rb.MovePosition(_rb.position + velocity * Time.deltaTime);
}
}
This is what I actually use in my PlayerController Script
Below you can see some footage of the jitter/stutter. It was quite hard to capture it, since using any recording software like OBS dropped the fps in my game to a point where the jitter/stutter was disappearing. Sorry for the phone recording, but this was honestly the only way.
Footage of my game showcasing the jitter/stutter especially noticable around the yellow ship.
And finally here's an album with some relevant settings in the inspector. Imgur Album
Looking at your recording, it looks as if the player is smooth and everything else.
From my experience 99% of the time means the jitter comes from the camera not being synced up correctly to the player.
It seems you have done the right things, and usually combining the following should work fine:
CinemachineBrain should update on FixedUpdate
Use interpolate/exrapolate on your player's Rigidbody (we chose extrapolate)
Apply forces/changes to physics objects solely in FixedUpdate
If you do all 3 together, this should work smoothly regardless of vsync, frame rate, fixed delta time, etc
Obviously your specific project might have something else that's messing it up; but I've seen this issue countless times and usually these combined steps should solve it.

Unity - Run a loop like sonic without falling down

I am making this 2D platform game for a freelance, and I have to do a sonic mechanic, I have to make the character run through a full loop without falling down, I realised that to do this first I would have to rotate the character conform he runs through the loop, but the second part is where I am stuck.
So, basically, how can I make the character run the loop without falling down.
private void OnCollisionEnter2D(Collision2D coll)
{
Vector3 collRotation = coll.transform.rotation.eulerAngles;
if (coll.gameObject.tag == "Ground")
{
//in this part i rotate the player as he runs through the loop
transform.eulerAngles = new Vector3(transform.eulerAngles.x, transform.eulerAngles.y, collRotation.z);
//this is the part that i am stuck, trying to figure out how to make the character stay in the loop without falling, i tried collision detection,
//i tried raycasting but nothing seems to work
if (IsGrounded)
{
GetComponent<Rigidbody2D>().AddForce(Vector2.down, ForceMode2D.Impulse);
}
}
}
The trick that is most commonly used for remakes and similar is getting the speed of the player character on entry.
// Feel free to adjust this to whatever works for your project.
const float minimumAttachSpeed = 2f;
// This should be your characters current movement speed
float currentSpeed;
// You need a Rigidbody in this example, or you can just disable
// any custom gravity solution you may have created
Rigidbody2D rb;
If the speed of the character is excelling the minimum attachment speed, you can then allow them to follow a predefined path at that speed.
bool LoopAttachmentCheck()
{
return minimumAttachSpeed <= currentSpeed;
}
And now you can check to see if you're moving fast enough! I assume for this example you are using a Rigidbody2D...
(this check should only be run when you're either entering or currently on the loop)
void Update()
{
if( LoopAttachmentCheck() )
{
// We enable this to prevent the character from falling
rb.isKinematic = true;
// Here you write the code that allows the character to move
// in a circle, e.g. via a bezier curve, at the currentSpeed.
}
else
{
rb.isKinematic = false;
}
}
It's up to you to implement the actual rotational behaviour. If I were you, a good way to do this would (assuming it is a perfect circle) use RotateAround from the center of the circle.
If you have more complex shapes, you can use waypoints for movement, and just iterate through them at your speed.
The moment you fail to keep up speed, your character will fall off (so if a player decided to stop running for example) and the Rigibody2D will become kinematic.
Hope this helps!

how do i do something every second after Collision

I am currently working on a football game(American) and im working on a tackle mechanic. Now just having 2 things bumping into each-other did not work because the character would continue to move after being forced sideways on the ground. To make the character stop after making contact with the other rigid body, I thought the best way would be to use Time.Timescale = 0; . However the problem with this is the fact that the 2 rigid body's then just go through each-other.to solve this I think the best way would be to set time scale to 0 after 1 second of collision. How can I do this?
Feedback is always appreciated ;)
to invoke a method delayed within unity presuming it is a monobehaviour.
the Invoke method apears to be what you are after
http://docs.unity3d.com/ScriptReference/MonoBehaviour.Invoke.html
however please be aware that Time.Timescale effects everything and is not local and would more than likely not get the effect you are after. setting the Velocity of the gameObject to zero should get the desired outcome.
var rb = GetComponent<Rigidbody>();
rb.velocity = Vector3.zero;
Time.Timescale would effect your whole game.
Actually Time.Timsescale is
The scale at which the time is passing. This can be used for slow motion effects.
When timeScale is 1.0 the time is passing as fast as realtime. When timeScale is 0.5 the time is passing 2x slower than realtime.
When timeScale is set to zero the game is basically paused if all your functions are frame rate independent.
From Unity Documentation
Well, what you can do is,
In script attached to Rigidbody's GameObject you can implement OnCollisionEnter.
Rigidbody _rb;
void Start()
{
_rb = GetComponent<Rigidbody>();
}
void OnCollisionEnter(Collision col){
_rb.velocity = Vector3.zero;
}
It will stop your player even after collision with ground. :)
So you can further modify collision condition like if body strikes to some specific object, you can detect it by tag or some other properties.
void OnCollisionEnter(Collision col){
if (col.gameObject.tag == "TAG_OF_SPECIFIC_OBJECT")
_rb.velocity = Vector3.zero;
}

Categories