I unlimited the frames per second in my game by doing.
graphics.SynchronizeWithVerticalRetrace = false;
IsFixedTimeStep = false;
But now my sprite/player moves WAY faster then it did before. I don't know why it does this, and I am not sure how to fix it.
if (keyboard.IsKeyDown(Keys.W) || keyboard.IsKeyDown(Keys.Up))
{
position.Y -= spd;
}
if (keyboard.IsKeyDown(Keys.A) || keyboard.IsKeyDown(Keys.Left))
{
position.X -= spd;
}
if (keyboard.IsKeyDown(Keys.S) || keyboard.IsKeyDown(Keys.Down))
{
position.Y += spd;
}
if (keyboard.IsKeyDown(Keys.D) || keyboard.IsKeyDown(Keys.Right))
{
position.X += spd;
}
That is currently how I am getting the sprite to move. spd = 4 at the moment. It worked perfectly fine, but now it seems as if it is moving like 2000 times faster. Just taping one of the keys takes him off screen.
Any and all help will be appreciated.
The game loop in XNA is based around update and draw. Fixed time step also refers to update, so by setting that to false, you are telling update to be called as often as possible. As your code is in the update function, its being called more than the default, fixed 60 times per second.
rather than just using spd, change it to
spd * (gameTime.ElapsedGameTime.Milliseconds / 16);
That will change it so that the spd is scaled by the elapsed time. The 16 is the number of milliseconds an update takes at 60 fps (approx) which is what your spd value is currently working at.
EDIT: For not part of Game.cs:
Have an update on the class you are interested in moving (Im going to call it Ship, but it could be anything you want)
class Ship
{
public void Update(GameTime gameTime)
{
...
position.Y += spd * (gameTime.ElapsedGameTime.Milliseconds / 16);
...
}
..
}
Then in the Game.cs file:
public override Update(GameTime gameTime)
{
myShip.Update(gameTime);
...
}
myShip being the variable for the class of the sprite you want to move. Note update is no longer overriding a base method, so the call to base.Update is also gone
When you turn off a fixed timestep, you need to take the time delta into the calculation to have any control over movement. For example:
if (keyboard.IsKeyDown(Keys.W) || keyboard.IsKeyDown(Keys.Up))
{
position.Y -= spd * gameTime.ElapsedGameTime.TotalSeconds;
}
...
In this case, you would set spd to the distance per second, not per frame.
Related
I'm trying to make a camera track two targets in a 2D game on unity but I can't quite get it to work.
This is the code I currently have, but it's changing the rotation in transform instead of position. the ortho size is changing but it is not properly tracking the center point between the characters. is there anyway I can fix this?
using System.Collections.Generic;
using UnityEngine;
public class CameraZoom : MonoBehaviour
{
private Camera cameraRef;
private GameObject[] playerPos;
void Start()
{
cameraRef = GetComponent<Camera>();
playerPos = GameObject.FindGameObjectsWithTag("Player");
Debug.Log(playerPos[0].transform.position);
Debug.Log(playerPos[1].transform.position);
Debug.Log(cameraRef.tag);
StartCoroutine(ZoomInOut());
}
// Update is called once per frame
void Update()
{
}
IEnumerator ZoomInOut()
{
while (true)
{
if (playerPos[0] != null && playerPos[1] != null)
{
Vector3 lookPoint = Vector3.Lerp(playerPos[0].transform.position, playerPos[1].transform.position, 0.5f);
cameraRef.transform.LookAt(lookPoint);
float distance = Vector3.Distance(playerPos[0].transform.position, playerPos[1].transform.position);
if (distance > (cameraRef.orthographicSize * 2))
{
cameraRef.orthographicSize += 0.05f;
// if (distance < (cameraRef.orthographicSize * 2))
//{
// cameraRef.orthographicSize -= 0.1f;
//}
}
else
if (distance < (cameraRef.orthographicSize))
{
cameraRef.orthographicSize -= 0.05f;
}
yield return new WaitForSeconds(0.02f);
}
}
}
}
Well cameraRef.transform.LookAt does exactly what you described
Rotates the transform so the forward vector points at worldPosition.
Since your game is 2D anyway what you rather want to do is set the camera to exactly the same XY position
var lookPoint = (playerPos[0].transform.position, playerPos[1].transform.position) * 0.5f;
//Maintain the Z depth position
lookPoint.z = cameraRef.transform.position.z;
cameraRef.transform.position = lookPoint;
Also instead of
yield return new WaitForSeconds(0.02f);
rather use
yield return null;
The latter makes it run every frame. But anyway assuming 60FPS a frame takes about 0.017seconds so waiting0.02` will most probably mean you wait two frames since this value is not the exact time but rather the minimum time to wait/skip to the next frame.
And move this OUT of the if block!!
If one of your objects is missing => endless loop -> Freeze/Crash of Unity and your app! You definitely want to wait one frame for every iteration of your while loop!
Actually you could as well do the thing in Update without the loop at all :)
And then instead of
cameraRef.orthographicSize -= 0.05f;
I would rather either use
// Take differences in the frame rate into account (jitter)
cameraRef.orthographicSize -= 3 * Time.deltaTime;
or directly interpolate
// Smooth interpolate -> if the target and current value are far apart
// this zooms faster and then becomes slower when already very close
// Adjust that "5" according to your needs
cameraRef.orthographicSize = Mathf.Lerp(cameraRef.orthographicSize, distance, 5 * Time.deltaTime);
without using your conditions at all.
Typed on the phone but I hope the idea gets clear
I am developing a VR application in Unity and I am struggling to develop a smooth UI scroll using my VR controller's joystick. So far what I have looks like this...
private void Update()
{
float joyStickDirection = globals.menuInteraction_Scroll.GetAxis(SteamVR_Input_Sources.Any).y; // this is either 1 for up or -1 for down
if (joyStickDirection != 0)
{
float multiplier = joyStickDirection * 5f;
scrollRect.verticalNormalizedPosition = scrollRect.verticalNormalizedPosition + (multiplier * Time.deltaTime);
}
}
...this works, but has two problems. Firstly, it scrolls at different speeds depending how big the scrolling container is. Secondly, the scrolling is not very smooth, as it is clearly just skipping varying gaps between 0 and 1.
I think I know what's wrong but I don't have enough experience working inside Update() to figure out the correct approach. Can anyone advise?
Actually you don't necessarily go through the ScrollRect component itself.
I usually would simply do
public class ScrollExample : MonoBehaviour
{
public float speed = 5f;
public Transform ScrollContent;
void Update()
{
// this is either 1 for up or -1 for down
var joyStickDirection = globals.menuInteraction_Scroll.GetAxis(SteamVR_Input_Sources.Any).y;
if (joyStickDirection != 0)
{
var multiplier = joyStickDirection * speed;
// You want to invert the direction since scrolling down actually means
// moving the content up
ScrollContent.position -= Vector3.up * multiplier * Time.deltaTime;
}
}
}
The ScrollRect then updates and handles the rest itself. The speed is in Units/seconds or in a Screenspace Overlay canvas in pixels per seconds regardless of how big the content is.
Usually you would want to adjust the elasticity of the ScrollRect or simply set the Movement Type to Clamped right away.
So I have a bunch of Coroutines running/working fine in my game.
However, one of them is for a power meter, and here's the issue...
You press the button, the power meter uses the coroutine to start filling up the power meter. (represented by the width of a red panel)
The goal is to make the meter fill up fast enough, as to where you can't just get "full power" every time. However, it also has to be smooth, and look nice.
The problem I'm facing is that my coroutine is maxed out on speed, and can't go any faster, and the meter is moving too slowly. So to "speed up" the meter, I have to use larger width increasing increments on the red panel.
It works, but, this causes 2 problems.
It doesn't look smooth. (It looks choppy and jittery)
It misses a whole bunch of values/possibilities. (e.g. The bar has to increase by a unit of "50" to make it fast enough to be difficult. But that's dumb, because that makes only 12 possibilities when stopping the meter. And there could be up to 600!)
Has anyone faced this before? If so, what's the best approach to making a fast, but smooth power meter, that can still hit a full range of value possibilities?
Thanks,
Ben
Here is my Code:
public static IEnumerator StartPitchMeter(Player.Guy guy, float duration)
{
var dur = 0.01f;
do
{
yield return new WaitForSeconds(dur);
PlayerControls.PitchMeter += 50f;
if (PlayerControls.PitchMeter > 600) PlayerControls.PitchMeter = 0f;
PlayerControls.pitchMeter.sizeDelta = new Vector2(PlayerControls.PitchMeter, 50);
} while (!PlayerControls.stopPitch);
}
If you're using a coroutine to update something for graphical purposes, you want to update on every available render frame (same as how often Update methods are called). So, instead of waiting for a set amount of time to pass, use yield return null and then move the meter according to Time.deltaTime, the time elapsed since the last frame in seconds. In this case, you can multiply Time.deltaTime by how many units per second you would like the meter to fill:
public static IEnumerator StartPitchMeter(Player.Guy guy, float duration)
{
do
{
yield return null;
// Fill 50 units per second.
float fillThisFrame = Time.deltaTime * 50f;
PlayerControls.PitchMeter += fillThisFrame;
if (PlayerControls.PitchMeter > 600) PlayerControls.PitchMeter = 0f;
PlayerControls.pitchMeter.sizeDelta = new Vector2(PlayerControls.PitchMeter, 50);
// Not sure what the 50 is doing here so it might be this instead:
// PlayerControls.pitchMeter.sizeDelta = new Vector2(PlayerControls.PitchMeter, fillThisFrame);
} while (!PlayerControls.stopPitch);
}
I've trying to create a scrolling background with 3 background, but everything When the 3rd start to come out. It creates a giant blue screen(Default background of the game) infront of the 3rd and after the 3RD it doesn't show any background. I have no idea how fix this, and I already know it something simple. I've got the 3RD one to work, but .
Code that I'm trying to use.
public void Update(GameTime gameTime)
{
bgPos0.Y += speed;
bgPos1.Y += speed;
bgPos2.Y += speed;
if (bgPos0.Y >= 950)
{
bgPos1.Y = -950;
if (bgPos1.Y >= 950) // Doesn't go fully down.
{
bgPos2.Y = -950;
if (bgPos2.Y >= 950)
{
bgPos0.Y = 0; //(Try to change it to -950 still doesn't work. I guest it due to that bgPos0 is set to 0, 0)
}
}
}
}
And the Vector2 code for the pos are
bgPos0 = new Vector2(0, 0);
bgPos1 = new Vector2(0, -950);
bgPos2 = new Vector2(0, -1900); // Could be the large -1900 number that destroying the code. To make it not work.
So how do I fix this? I wish I could fix it right now, but I can't for some reason.
I don't think you want to have the if-statements nested. The code you posted constantly moves the second background to -950 for as long as the first is past 950. Since the first is constantly moved back to -950 it shouldn't ever manage to move past 950, and so it never gets into the last one. I think what you probably want to do is something more like:
public void Update(GameTime gameTime)
{
bgPos0.Y += speed;
if(bgPos0.Y > 950) {
bgPos0.Y = -950;
}
bgPos1.Y += speed;
if(bgPos1.Y > 950) {
bgPos1.Y = -950;
}
bgPos2.Y += speed;
if(bgPos1.Y > 950) {
bgPos1.Y = -950;
}
}
[EDIT]: As an aside, the number in question isn't nearly large enough to cause problems. XNA's Vector2 class stores the x and y components as floats, and the maximum value for a float in C# is somewhere around 3.4e38 or so, and accurate to 7 digits according to MSDN.
When the user presses a button fwdi is set to 1 which starts this if statement. It seems logical to me that if I reduce fwdi by 0.1 every update and move the player 0.1 every update then everything should be done by the 1 second mark however the movement is much quicker than 1 second. I have an animation that also plays which takes 1 second and they don't match up.
EDIT
void Update ()
{
if (fwdi > 0.0f)
{
fwdi = fwdi - 1.0f * Time.deltaTime;
amountToMove.y = 1.0f * Time.deltaTime;
transform.Translate (amountToMove);
if (fwdi == 0.0f)
{
amountToMove.y = 0.0f;
fwdi = 0.0f;
}
}
}
All you should need to do in compensate for frametime (IE: at 60fps, update is called 60times per second)
void Update ()
{
if (fwdi > 0)
{
amountToMove.y = 0.1f*Time.deltaTime;
transform.Translate (amountToMove);
fwdi = fwdi - amountToMove.y;
//This code bellow looks redundant, and probably unnecessary
if (fwdi == 0)
{
amountToMove.y = 0;
fwdi = 0f;
}
}
}
Also consider putting movement on FixedUpdate. Fixed update works similarly to update with a few key differents:
-Fixedupdate has priority over the normal update function,
-unity default call cap for fixed update is 20 times per second
It's commonly suggested that you put, lightweight processes that benefit from predictable update intervals on fixed update, like movement and physics.
Note:: its still a good idea to multiply everything in FixedUpdate by Time.fixedDeltaTime, just in case Fixedupdate fails to reach its 20 tick per second count.
Time.timeScale
Frame rate
Time.deltaTime
Now I know almost nothing about Unity, so this may not be close to an answer. Normally you would make things in games frame rate independent by calculating a factor, say if you wan't to move 1 distance in one second, you calculated time passed and apply a multiplication. E.g. "timepassed-factor * amounttomove"
From what I can gather you need to use Time.deltaTime which would give you the actual time passed between frames, where as timeScale really just is for slow motion etc...
So... amountToMove.y = 1f * Time.deltaTime * ???;