Is it possible to smooth my Unity3D progress bar? - c#

I've created a simple GUI progress bar that starts at 0px and expands to 640px. It works great, but appears a little jumpy. I can live with it, but if I can make it appear smoother, that would be ideal.
Any suggestions?
IEnumerator Timer() {
DateTime start = DateTime.Now;
while (true) {
float tick = (float)DateTime.Now.Subtract(start).TotalSeconds;
Debug.Log("TimeInSeconds: " + (int)tick);
rawImageRectTransform.sizeDelta = new Vector2(tick / 60f * 640f, 10f);
if (tick > 60f) {
break;
}
yield return null;
}
}
UPDATE:
As per suggestions, I've tried the following and the effect is identical.
rawImageRectTransform.sizeDelta = Vector2.Lerp(new Vector2(0, 10f), new Vector2(640f, 10f), tick/60f);
I've also tried it this way:
rawImageRectTransform.sizeDelta = Vector2.Lerp(new Vector2(0, 10f), new Vector2(640f, 10f), Time.fixedTime/60f);
And the effect is the same, however the progress bar is no loger sync'd to the DateTime.TotalSeconds (ie. tick) value, so this isn't going to work.

Try using Vector3.lerp() or Vector2.lerp() in a FixedUpdate() it will make sure your requirement gets called fixed number of times per second still it might also work with co-routine without much visual irregulaties.
// for a 3d system
public static Vector3 Lerp(Vector3 from, Vector3 to, float t);
// for a 2d system
public static Vector2 Lerp(Vector2 from, Vector2 to, float t);

Related

How to do vector addition in Unity

This is my first from scratch game project. I'm trying to make a pinball game but I don't want to just "watch a video on how to make a pinball game". I want to run into the problems and learn how to tackle them as they come.
So far, attaching script to a sprite was issue #1 but I've kinda worked that out. Issue #2 was creating variables and having them translate to real object values. After multiple hours of trial and error I eventually just copied someone elses script that had the most basic setup possible, then broke it and rebuilt it to what I have below with the addition of void Update.
My question is mostly to gather a better understanding but also about a new problem of mine.
Issue #3 is currently when I click play, it moves the object only once. I thought void update is supposed to call every frame?
I would also like to know why when I do transform.position, why can't I do transform.position += (value 1, value 2)? From what I've come up with from experimenting, the only way to alter transform.position is to do = new Vector everytime which I don't fully understand... Another way of wording this part of the question would be: Is there a shorter way of writing a vector transformation or is this the only way the change can be written?
Below is the code. I appreciate any answers even if it's simply directing on the right path to find the information I want.
public float width, height, xSpeed, ySpeed, xPosition, yPosition;
public Vector2 position, scale;
void Start() {
// Initialise the variables
width = 0.5f;
height = 0.5f;
xSpeed = 0;
ySpeed = -1f;
xPosition = 0;
yPosition = 3.5f;
// set the scaling
Vector2 scale = new Vector2(width, height);
transform.localScale = scale;
// set the position
transform.position = new Vector2(xPosition, yPosition);
}
void Update() {
transform.position = new Vector2(xPosition + xSpeed,
yPosition + ySpeed);
}
First I would recommend you changing the title of the question to something that is a bit more on point, so that people have an idea what programming concept your question is about! :) I would recommend something like "How to do vector addition in Unity".
I will go through your questions one-by-one:
Yes, the Update-Function is called every frame! With each call of Update() you set your position to exactly the same value again and again. That is why it is not moving. Neither the xPosition/yPosition nor the xSpeed/ySpeed variables are changing after they have been defined in Start(), so your Update Function will always set your position to (0, 2.5, 0).
You can do Vector addition! But in order to do that, you need to properly write it in your code, by which I mean you need to make a vector out of the values you want to add and only then you can add them to the position vector! So if you wanted to add the xSpeed/ySpeed values ontop of your position, it would look like that:
transform.position += new Vector2(xSpeed, ySpeed);
I hope that helps!
With the help of Jayne, this is what the code ended up turning into, hope this helps anyone else who just wants a straightforward way to alter a vector:
public float width, height, xSpeed, ySpeed, xPosition, yPosition;
public Vector2 position, scale;
void Start() {
// Initialise the variables
width = 0.5f;
height = 0.5f;
xSpeed = 0;
ySpeed = -0.01f;
xPosition = 0;
yPosition = 3.5f;
// set the scaling
Vector2 scale = new Vector2(width, height);
transform.localScale = scale;
// set the position
transform.position = new Vector2(xPosition, yPosition);
}
void Update() {
// Move the pinball
xPosition += xSpeed;
yPosition += ySpeed;
transform.position = new Vector2(xPosition, yPosition);
}

Having trouble on a Scripting Exercise for Unity and deleting objects (So close!!)

EDIT: I have updated the code, but it still doesn't quite work properly.
I am almost done with this project but am stuck on the last part.
Basically, the parameters for what I'm working on is as follows:
Instantiate one cube prefab every frame into the scene.
The cubes need to be named as “Cube1”, “Cube2”… based on the order they are generated.
The cubes need to be generated at a random locations within 1 unit from the origin [0,0,0].
Each cube should have a random color.
The cube size (localScale) shrink 10% in each frame.
When the cube’s scale is less than 10% of its original scale, the cube is destroyed.
I am stuck on six. The cubes are created, colored and named, and shrink over time, but will not always destroy themselves after they shrink enough.
I've gotten them to delete SOME of the cubes, but others will now just stop shrinking and stay in the scene indefinitely.
if this helps, I noticed that after it deletes a certain element (cube0 for example)it will reuse that name again and then the list of objects gets all messed up looking and things stop deleting.
My code so far:
using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeCreator : MonoBehaviour
{
Vector3 origin;
private List<GameObject> cubeList;
public float targetScale = 0.1f;
public float shrinkSpeed = 0.1f;
GameObject newCube;
// Start is called before the first frame update
private void Awake()
{
cubeList = new List<GameObject>();
}
void Start()
{
origin = new Vector3(0, 0, 0);
}
// Update is called once per frame
void Update()
{
AddNewCube();
for (int i = 0; i < cubeList.Count; i++)
{
cubeList[i].name = "cube" + i;
cubeList[i].transform.localScale = Vector3.Lerp(cubeList[i].transform.localScale, new Vector3(targetScale, targetScale, targetScale), Time.deltaTime * shrinkSpeed);
if (cubeList[i].transform.localScale.x <= targetScale + 0.5f)
{
cubeList.RemoveAt(i);
RemoveCube(newCube);
print("cube " + i + " removed");
}
}
}
private void SetRandCubeState(GameObject cube)
{
float randScale = Random.Range(0.8f, 1.0f);
Vector3 randomScaler = new Vector3(randScale, randScale, randScale);
cube.transform.localScale = randomScaler;
Vector3 randomPosition = new Vector3(Random.Range(origin.x - 1.0f, origin.x + 1.0f), Random.Range(origin.y - 1.0f, origin.y + 1.0f), Random.Range(-1.0f,1.0f));
cube.transform.localPosition = randomPosition;
Renderer render = cube.GetComponent<Renderer>();
render.material.SetColor("_Color", Random.ColorHSV());
}
private void AddNewCube()
{
GameObject newCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cubeList.Add(newCube);
SetRandCubeState(newCube);
}
private void RemoveCube(GameObject oldCube)
{
Destroy(oldCube);
}
}
Note that the tasks says nothing about a random initial scale. It further says the cubes should destroy after reaching < 10% of the original scale. So if you use random initial scale then you would also need to store the original scale somewhere.
It also says the cube should shrink 10% each frame not based on seconds. So how I understand it the first frame it goes 1 -> 0.9, the second frame it goes 0.9 -> 0.81 etc so either the task is bad formulated or you are doing something else ^^
Lerp takes a factor between 0 and 1 and linear interpolates the start and end value accordingly. You are using it wrong ^^
Given an e.g. 60 frames per second you are all the time interpolating between the current scale and 0.1 with a (more or less constant) factor of about 0.0017 .. your cubes will barely scale down and get slower and slower the closer you get to the target. Definitely not scaling down 10 % per frame ;) You are also lerping against the value 0.1 instead of 0. So your threshold might never be reached at all or only very very slow.
Also the cubes should be named accordingly to the order they are created in -> you shouldn't iterate through the list and rename them.
// Store existing cube references together with their minimum scale
// before getting destroyed
private Dictionary<Transform, float> cubes = new Dictionary<Transform, float>();
// Count created objects for correct naming
private int cubeSpawnCounter;
void Update()
{
AddNewCube();
foreach(var kvp in cubes)
{
// Scale down 10% per frame
kvp.Key.localScale *= 0.9f;
// Check if reached below threshold of 10% of original scale
if (kvp.Key.localScale.x <= kvp.Value)
{
RemoveCube(kvp.Value);
}
}
}
private void SetRandCubeState(GameObject cube)
{
// Multiply the vector that saves you a lot of typing ;)
cube.transform.localScale = Vector3.one * Random.Range(0.8f, 1.0f);
// storing the origin is redundant
cube.localPosition = new Vector3(Random.Range(-1.0f, 1.0f), Random.Range(-1.0f, 1.0f), Random.Range(-1.0f,1.0f));
// Only go via the Shader properties if really necessary
var render = cube.GetComponent<Renderer>();
render.material.color = Random.ColorHSV());
}
private void AddNewCube()
{
var newCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
SetRandCubeState(newCube);
// Count the created cubes and set the name ONCE
cubeSpawnCounter++;
newCube.name = $"Cube{cubeSpawnCounter}";
// store the destroy threshold for each cube (10% of original scale)
cubes[newCube.transform] = randomScale * 0.1f;
}
private void RemoveCube(Transform oldCube)
{
// Remove from the Dictionary
cubes.RemoveKey(oldCube);
print($"{oldCube.name} removed");
Destroy(oldCube.gameObject);
}
It appears that the value never reaches .1 due to the lerp. You could try adding a float to the conditional check (I used a large number, but using .01f could work depending on your use case)
if(cubeList[i].transform.localScale.x <= targetScale + .3f)
{
cubeList.RemoveAt(i);
RemoveCube(newCube);
print("cube " + i + " removed");
}

How do I create an object to move between two positions in unity, using C#, and why is my code not working?

I have this piece of code right here that should make the block object move between the startPos and endPos objects, but something is wrong about it and I don't know what.
void FixedUpdate()
{
if (block.transform.position == startPos.transform.position)
{
check = false;
}
if(block.transform.position == endPos.transform.position)
{
check = true;
}
if (check == false)
{
block.transform.position = Vector3.Lerp(block.transform.position, endPos.transform.position, .03f);
}
if (check == true)
{
block.transform.position = Vector3.Lerp(block.transform.position, startPos.transform.position, .03f);
}
}
At some point the block will reach endPos, and then on its way back to startPos it will stop, because the functions will be executed simultaneously. But how is this possible because my if's right there should not allow this to happen?
In general you should always be using
Update &rightarrow; called every frame
instead of
FixedUpdate &rightarrow; called in certain realtime intervals
except you are dealing with Physics somehow (which doesn't seem to be the case here). Also see the Update and FixedUpdate Tutorial
The issue with Vector3.Lerp is that it doesn't behave as you expect.
I guess you like that it starts fast and then becomes "smooth" ... but actually this might be your problem.
It never reaches the target position really. It just gets closer and closer and slower and slower ...
... until at some moment the == with a precision of 0.00001f eventually becomes true.
So it might seem that it stopped but actually it might still be moving just really really slow.
For the following two alternatives you have to decide a bit what you want to control:
Option: The speed
If you want to have a linear velocity for the object you should rather use
// adjust via the Inspector
[SerializeField] private float moveSpeedInUnityUnitPerSecond = 1f;
// you should use Update here in general
void Update()
{
if (block.transform.position == startPos.transform.position)
{
check = false;
}
// always use else in cases where only on condition can be
// true at the same time anyway
else if(block.transform.position == endPos.transform.position)
{
check = true;
}
block.transform.position = Vector3.MoveTowards(block.transform.position, check ? startPos.transform.position : endPos.transform.position, Time.deltaTime * moveSpeed);
}
Option: The duration
If you rather want a smooth movement but control the duration it takes to reach the target you should use a Lerp but with a factor depending on the time like
// adjust via the Inspector
[SerializeField] private float moveDurationInSeconds = 1f;
private float passedTime;
// you should use Update here in general
void Update()
{
// prevent overshooting
passedTime += Mathf.Min(moveDurationInSeconds - passedTime, Time.deltaTime);
if(passedTime >= moveDurationInSeconds)
{
check = !check;
passedTime = 0;
}
var lerpFactor = passedTime / moveDurationInSeconds;
// and now add ease-in and ease-out
var smoothedLerpFactor = Mathf.SmoothStep(0, 1, lerpFactor);
var fromPosition = check ? endPos.transform.position : startPos.transform.position;
var toPosition = check ? startPos.transform.position : endPos.transform.position;
block.transform.position = Vector3.Lerp(fromPosition, toPosition, smoothedLerpFactor);
}
For this you could also use a Coroutine which usually is a bit easier to interpret and maintain:
// adjust via the Inspector
[SerializeField] private float moveDurationInSeconds = 1f;
// yes you see correctly one can directly use the Start
// as a Coroutine
private IEnumerator Start()
{
var fromPosition = startPos.transform.position;
var toPosition = endPos.transform.position;
// looks strange but as long as you yield somewhere inside
// the loop it simply means repeat the sequence forever
// just like the Update method
while(true)
{
var passedTime = 0f;
while(passedTime < moveDurationInSeconds)
{
var lerpFactor = passedTime / moveDurationInSeconds;
// and now add ease-in and ease-out
var smoothedLerpFactor = Mathf.SmoothStep(0, 1, lerpFactor);
block.transform.position = Vector3.Lerp(fromPosition, toPosition, smoothedLerpFactor);
passedTime += Mathf.Min(moveDurationInSeconds - passedTime, Time.deltaTime);
// reads like: "pause" here, render this frame and continue
// from here in the next frame
yield return null;
}
// once reached flip the positions
var temp = fromPosition;
fromPosition = toPosition;
toPosition = temp;
}
}
in both cases you could still add more flexibility and instead of simply using the moveDurationInSeconds use
var fixedDuration = moveDurationInSeconds * Vector3.Distance(fromPosition, toPosition);
this way the movement takes shorter if the positions are closer together and longer if they are further apart. This comes pretty close to the Lerp you used before regarding to the smoothness of motion but you can control very good how long the movement will take.
The third argument of Vector3.Lerp is a percentage of the distance between the first two arguments. When you pass in 0.03f, you're getting 3% closer to your end position but never actually getting exactly there (You can prove this by logging the block's position and the target's position and you'll see they're never perfectly equal).
Instead of checking if your block's position with the == operator (which works to an accuracy of 0.00001f) to the target position, you could simply check if it's close enough with Vector3.Distance:
if(Vector3.Distance(block.transform.position, endPos.transform.position) < 0.1f) {
You can make your "close enough threshold" (0.1f in the example) a named variable for easy adjustment until it feels right.
For starters, Update() loops every frame. FixedUpdate() depends on the number of frames per second set in the time setting. So, put your code into Void Update() instead.
If you refer to Vector3.MoveTowards documentation, you may find a much better approach to your problem.
The script below should do the trick. The target variable should be set to the position of your end point. Speed is how fast your object should move.
Inside Update(), the step variable is used to determine how far the object should move based off its speed and the time elapsed since it last moved. Finally, the last line changes the position of the object and records the new position.
public Transform target;
public float speed;
void Update() {
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
}

Handling mouse for first person game in monogame

I am trying to make a first-person game on Monogame and so far all I have come with or found on the internet didn't meet my standards.
this is how I am currently handling the mouse:
private void HandleMouse()
{
Vector2 mouseDifference;
mouseNow = Mouse.GetState();
if (mouseNow.X != mouseDefaultPos.X || mouseNow.Y != mouseDefaultPos.Y)
{
mouseDifference.X = mouseDefaultPos.X - mouseNow.X;
mouseDifference.Y = mouseDefaultPos.Y - mouseNow.Y;
leftrightRot += mouseSens * mouseDifference.X;
updownRot += mouseSens * mouseDifference.Y;
Mouse.SetPosition((int)mouseDefaultPos.X, (int)mouseDefaultPos.Y);
UpdateViewMatrix();
}
}
private void UpdateViewMatrix()
{
Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
viewMatrix = Matrix.CreateLookAt(new Vector3(0,0,0), cameraRotatedTarget, cameraRotatedUpVector);
}
My problem is that while this solution works, it is extremely inconsistent when it comes to how far the camera should rotate.
for example, happens when I make circles with the mouse and see that sometimes the mouse randomly jumps more than expected.
My main assumptions inconsistencies in fps that cause the time between frames to change, thus affecting the distance the mouse can move within that time.
I don't know if this is the reason or if my assumption can even cause this, but I would like to find a way to get consistent mouse movement.
I can provide a video of the problem if needed.
Thank you in advance.
The mouse in Windows is updated 240 times per second. The game loop runs at 60 frames per second. The discrepancy can lead to large mouse deltas. The solution is to limit the change in the mouse delta:
Vector2 mouseDifference;
const float MAXDELTA = 6; // Set to the appropriate value.
mouseNow = Mouse.GetState();
if (mouseNow.X != mouseDefaultPos.X || mouseNow.Y != mouseDefaultPos.Y)
{
mouseDifference.X = Math.Min(MAXDELTA, mouseDefaultPos.X - mouseNow.X);
mouseDifference.Y = Math.Min(MAXDELTA, mouseDefaultPos.Y - mouseNow.Y);
leftrightRot += mouseSens * mouseDifference.X;
updownRot += mouseSens * mouseDifference.Y;
Mouse.SetPosition((int)mouseDefaultPos.X, (int)mouseDefaultPos.Y);
UpdateViewMatrix();
}
This is an old thread, but I figured I would share a solution. The solution that's worked for me to use the GameTime object to relate how much the player should rotate by. In other words, rotate by (delta * rotationSpeed * GameTime.ElapsedTime.TotalSeconds) so that each rotation is relative to how much time has passed since the last frame. This protects it against frame drops, which I've found has been the problem for me.

How To: Make a Rotating Lazer Offset In Unity3D?

I'm attempting to create an effect similar to the one found in this video:
https://www.youtube.com/watch?v=1i0da_Pg5Jc
However, with verbatim code it does not function as demonstrated. I'm not sure if it's changes in unity or failure on my part but I'm using the same shader, and probably have watched this video like.. Oh Idk, 2501 times.. and I can't does it.
The best I can do is back and forth, Time.time does not work for me.
The code suggested..
Vector2 m_Offset = new Vector2(0, Time.time);
Doesn't do anything.
What I'm rolling with right now is..
void ProcessTextureShift()
{
m_MaterialOffsetValue += m_OffsetShiftValue * Time.deltaTime;
if (m_MaterialOffsetValue >= m_MaterialOffsetLimit)
ResetMaterialOffset();
m_WeaponBeam.material.
mainTextureOffset = new Vector2(0, m_MaterialOffsetValue);
}
That produces an effect from start to finish but I was trying to accomplish the rotating/spinning of the rainbow in the video.
float m_MaterialOffsetLimit = 1.0f;
float m_MaterialOffsetValue = 0.0f;
float m_OffsetShiftValue = 2.0f;

Categories

Resources