I have a need to lerp through 10 different materials attached to one of my game objects but for some reason, the code I have written doesn't work. I have spent the past hour trying to workout why and I'm pretty burnt out.
Could someone with fresher eyes please take a look and see if I'm doing some stupid?
public class LerpMaterials : MonoBehaviour
{
public List<Material> materials = new List<Material>();
public float lerpSpeed;
int currentMaterialNo;
Material currentMaterial;
Material targetMaterial;
bool lerpingMaterial;
float lerp;
void Start ()
{
if (materials.Count < 2) return;
currentMaterialNo = 0;
currentMaterial = materials[currentMaterialNo];
targetMaterial = materials[currentMaterialNo+1];
}
void Update ()
{
if (materials.Count < 2) return;
lerp += lerpSpeed;
renderer.material.Lerp(currentMaterial, targetMaterial, lerp);
if (lerp >= 1)
SwitchMaterial();
}
void SwitchMaterial()
{
if ( currentMaterialNo >= (materials.Count - 1) )
currentMaterialNo = 0;
else
currentMaterialNo++;
currentMaterial = materials[currentMaterialNo];
targetMaterial = materials[currentMaterialNo++];
lerp = 0;
}
}
My list holds every single material and my mesh renderer also holds the required materials as well. But nothing happens other than an instance of the first material appearing in the material renderer. No other movement.
You probably need to set your lerpSpeed.
If lerpSpeed = 0, then no change will happen.
If lerpSpeed > 1, then once per frame, it will change the material.
Since the first material is the only thing showing up and not looping through every material rapidly, lerpSpeed is probably 0. However, you will end up with a problem with this function in that it will change every frame or extremely rapidly. The issue being with how lerp is being incremented.
Instead of:
lerp += lerpSpeed;
Use this instead:
lerp += lerpSpeed * Time.deltaTime;
What this will do is instead of incrementing lerp by lerpSpeed once per frame (which is dependent on the computer's performance), instead, it will increment lerpSpeed by lerpSpeed every second, which will ensure a consistent effect across every machine.
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
Alright, so I'm making a game in Unity, and I tried to spawn in enemies randomly around a player. To control the rate of the spawning, I created a private bool spawnCooldown variable. Then, there was an if {} statement which controlled the rate of spawning. The original code is below:
private bool spawnCooldown;
private void Start
{
spawnCooldown = Time.time;
}
private void Update
{
if (spawnCooldown < Time.time)
{
Instantiate(original, position, rotation);
spawnCooldown = Time.time + 3f;
// Note that original, position and rotation are just placeholders
// and not the actual code.
}
}
What's wrong with this code? Currently, it's just instantiating every frame update, and it seems that it's not testing the if statement at all.
You need to add the seconds of cooldown you want on the spawner when you declare it too, E.G spawnCooldown = Time.time + coolDownPeriodInSeconds;
You also need to set spawnCooldown to be a float, which stores numbers.
Currently you have stored it as a bool, which only stores true or false values, and therefore cannot be compared to Time.time further in the code.
Lastly you are missing the closing } character in the if block
What is happening currently is you are doing the following:
Setting spawn Cooldown to be the current Time
For every frame after that, checking if the new current time is greater than the old time you set as spawnCooldown. Since it always is, the code will then run through the spawn script.
If you change it to
private float spawnCooldown;
void Start
{
spawnCooldown = Time.time +3f ;
}
void Update
{
if (spawnCooldown < Time.time)
{
Instantiate(original, position, rotation);
spawnCooldown = Time.time + 3f;
}
//Note that original, position and rotation are just placeholders and not the actual code.
}
Then it works fine.
Good luck with Unity!
Ok, so I've managed to fix the problems with StarshipladDev's answer. The problem was that the clones were cloning themselves, as I was instantiating the gameobject itself.
I am using Vector3.Lerp in unity game to simply move a gameobject from one position to other smoothly. Below is my code:
public class playermovement : MonoBehaviour {
public float timeTakenDuringLerp = 2f;
private bool _isLerping;
private Vector3 _startPosition;
private Vector3 _endPosition;
private float _timeStartedLerping;
void StartLerping(int i)
{
_isLerping = true;
_timeStartedLerping = Time.time ; // adding 1 to time.time here makes it wait for 1 sec before starting
//We set the start position to the current position, and the finish to 10 spaces in the 'forward' direction
_startPosition = transform.position;
_endPosition = new Vector3(transform.position.x + i,transform.position.y,transform.position.z);
}
void Update()
{
//When the user hits the spacebar, we start lerping
if(Input.GetKey(KeyCode.Space))
{
int i = 65;
StartLerping(i);
}
}
//We do the actual interpolation in FixedUpdate(), since we're dealing with a rigidbody
void FixedUpdate()
{
if(_isLerping)
{
//We want percentage = 0.0 when Time.time = _timeStartedLerping
//and percentage = 1.0 when Time.time = _timeStartedLerping + timeTakenDuringLerp
//In other words, we want to know what percentage of "timeTakenDuringLerp" the value
//"Time.time - _timeStartedLerping" is.
float timeSinceStarted = Time.time - _timeStartedLerping;
float percentageComplete = timeSinceStarted / timeTakenDuringLerp;
//Perform the actual lerping. Notice that the first two parameters will always be the same
//throughout a single lerp-processs (ie. they won't change until we hit the space-bar again
//to start another lerp)
transform.position = Vector3.Lerp (_startPosition, _endPosition, percentageComplete);
//When we've completed the lerp, we set _isLerping to false
if(percentageComplete >= 1.0f)
{
_isLerping = false;
}
}
}
}
The code works fine and the gameobject moves smoothly between two points. But it takes about 1 sec to reach destination. I want to make it move faster. I have tried decreasing the value of float timeTakenDuringLerp but the speed isn't affected. I have followed this tutorial and the explanation there also says to change timeTakenDuringLerp variable in order to change speed but its not working here.
Any Suggestions please?
H℮y, thanks for linking to my blog!
Decreasing timeTakenDuringLerp is the correct solution. That reduces the time it takes for the object to move from start to finish, which is another way of saying "it increases the speed".
If there is a specific speed you want the object to move at, you'll need to make timeTakenDuringLerp a variable rather than a constant, and set it to distance/speed. Or better yet, don't use Lerp at all, and instead set the object's velocity and let Unity's physics engine take care of it.
Multiplying percentageComplete by a constant, as suggested by #Thalthanas, is incorrect. That causes the lerping updates to continue occurring after the lerping has completed. It also makes the code hard to understand because timeTakenDuringLerp is no longer the time taken during the lerp.
I've double-checked with my code and it does indeed work, so the problem you are experiencing must be elsewhere. Or maybe you accidentally increased the time, which would decrease the speed?
The solution is to
multiply percentageComplete value with a speed value like,
transform.position = Vector3.Lerp (_startPosition, _endPosition, percentageComplete*speed);
So when you increase speed, it will go faster.
My game is an over-the-shoudler endless runner style game. I have the obstacles coming down the screen using constant force. My question is, how do I make this Constant Force Script make the obstacle go gradually faster as the game progresses and the player goes longer into the game? If you try out this script yourself (just attach it to a cube or a sphere), you'll notice how it starts off slow at first then literally 2 seconds later, it goes into Rocket Speed lol. ( I am working on an Object Pooler Script also, since I do not want to waste CPU with Instantiate and Destroy) Thank you to all who attempt and help me solve this! :)
Here is the Script I made:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(ConstantForce))]
public class AddConstantForce : MonoBehaviour
{
float speed = 1.0f;
Vector3 force;
void Awake()
{
//Another way to "Require" the component.
if(!GetComponent<ConstantForce>())
gameObject.AddComponent<ConstantForce>();
//
force = GetComponent<ConstantForce>().force;
GetComponent<Rigidbody>().useGravity = false;
}
void Update()
{
GetComponent<ConstantForce>().force = force;
force += -transform.forward * speed * Time.deltaTime;
/* Transform is negative because in my game, I have the
illusion of the objects coming down my mountain.
*/
}
}
Usually, to do stuff like this, you need 2 variables:
1. Time in which you want the speed to increase in. (For example, increase every 5 seconds) secondsToIncreaseIn.
2. Value to increase by.(For example, increment speed by 2 every time in #1 seconds) valueToIncreaseBy.
This way you have flexible ways to speed up or slow down how much your speed increments by changing either variable. With experiment setting secondsToIncreaseIn to 5 and valueToIncreaseBy to 0.0007f made the game last longer. You can increase valueToIncreaseBy to make it even last longer.You can change the startingSpeed variable if the starting speed is too slow or high.
private float speed = 0f;
private Vector3 force;
private ConstantForce cachedConstantForce;
private float counter = 0; //Dont change(Used for counting)
public float startingSpeed = 5f;//Start with 5 speed
public int secondsToIncreaseIn = 5; //Increase in every 5 seconds
public float valueToIncreaseBy = 0.0007f; //Increase by 0.0007 in secondsToIncreaseIn time/seconds
void Awake()
{
//Another way to "Require" the component.
cachedConstantForce = gameObject.GetComponent<ConstantForce>();
if (cachedConstantForce == null)
{
gameObject.AddComponent<ConstantForce>();
}
cachedConstantForce = GetComponent<ConstantForce>();
}
void Start()
{
force = cachedConstantForce.force;
GetComponent<Rigidbody>().useGravity = false;
speed = startingSpeed;
}
void Update()
{
cachedConstantForce.force = force;
force = -transform.forward * speed * Time.deltaTime;
/* Transform is negative because in my game, I have the
illusion of the objects coming down my mountain.
*/
if (counter < secondsToIncreaseIn)
{
counter += Time.deltaTime;
}
else
{
//Time has reached to Increase value
counter = 0; //Reset Timer
//Increase Spped
speed += valueToIncreaseBy;
Debug.Log("Increased Speed!");
}
}
Initially I was going to suggest use Time.time as factor (since Time.time is constantly increasing), but then, if you have to wait for some input for the run to start, Time.time could already be to big the the time that the run actually starts.
Instead I would suggest to keep a third variable that works as Time.time (keeps updating every frame by Time.deltaTime) and use that third variable to constantly keep increasing the applied force.
For example:
float speed = 1.0f;
Vector3 force;
float time = 0;
void Awake()
{
//Another way to "Require" the component.
if(!GetComponent<ConstantForce>())
gameObject.AddComponent<ConstantForce>();
//
force = GetComponent<ConstantForce>().force;
GetComponent<Rigidbody>().useGravity = false;
}
void Update()
{
GetComponent<ConstantForce>().force = force;
time += Time.deltaTime;
force += (-transform.forward * speed * Time.deltaTime) * time;
/* Transform is negative because in my game, I have the
illusion of the objects coming down my mountain.
*/
}
NOTE: You will probably have to apply a bigger value to speed since this operation will return quite a small value. Or you could add another variable that will increase the small value returned by the operation. Also consider that once you start testing the difficulty you might need to use a bigger or smaller value than Time.deltaTime to increase time, to either make the game harder or easier.
I'm making a project with Augmented Reality, using Unity and Vuforia extensions. I'm new to C#, but I was looking for a method similar to ARToolKit's getFrame(), and I'm really not finding anything.
My questions are:
Is it necessary that I can calculate the frame-rate that my scene is operating at?
Which scene object should i use to track the frame-rate?
Thats as simple as:
public float avgFrameRate;
public void Update()
{
avgFrameRate = Time.frameCount / Time.time;
}
Put this code in any MonoBehaviour and attatch it to any GameObject in the scene hierarchy.
Please note: this will only give you an average frame-rate. For a more current frame-rate, other answers have addressed effective ways of accomplishing that.
None of the answers here consider the fact that the timescale can be modified in Unity and if it is, all the above approaches will be incorrect. This is because Time.Delta time is influenced by the timescale.
As such, you need to use Time.unscaledDeltaTime:
int fps = 0;
void Update () {
fps = (int)(1f / Time.unscaledDeltaTime);
}
You should look at Time.smoothDeltaTime. This returns a smoothed Time.deltaTime value which you can use instead of having to smooth it yourself using one of the techniques mentioned in other answers.
You will want something like a timer that tracks the time, and how long it took to update the screen, and extrapolates from that how many frames are drawn in a second.
I am fairly rusty with Unity, but I believe something like 1/Time.deltaTime should give you what you want.
So you'd have something like
public void Update()
{
framerateThisFrame = 1/Time.deltaTime;
}
Next you would have to decide how often to change the displayed FPS, since framerateThisFrame can change a lot during every frame. You might want to change it every two seconds for example.
EDIT
An improvement you might want to make is something like storing the past n frames, and use an average to calculate the FPS, then display it. So you could end up with something like:
public int Granularity = 5; // how many frames to wait until you re-calculate the FPS
List<double> times;
int Counter = 5;
public void Start ()
{
times = new List<double>();
}
public void Update ()
{
if (counter <= 0)
{
CalcFPS ();
counter = Granularity;
}
times.Add (Time.deltaTime);
counter--;
}
public void CalcFPS ()
{
double sum = 0;
foreach (double F in times)
{
sum += F;
}
double average = sum / times.Count;
double fps = 1/average;
// update a GUIText or something
}
EDIT
You might even multiply the frame time by Time.timeScale, if you want to be consistent while you apply slow-down/time altering effects.
Since the framerate can vary constantly, it will change many times during a given second. I've used the following recommended approach to get the current framerate. Just put it in a new script and add it to a new, empty game object in your scene.
float deltaTime = 0f;
void Update() {
deltaTime += (Time.deltaTime - deltaTime) * .1f;
}
Source, including display method: http://wiki.unity3d.com/index.php?title=FramesPerSecond
IEnumerator FramesPerSecond()
{
while (true)
{
yield return new WaitForSeconds(1);
Debug.LogFormat("Fps {0}", Time.frameCount/Time.time);
}
}
private void Start()
{
StartCoroutine(FramesPerSecond());
}