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.
Related
So I'm creating a 2D top-down endless shooter game and need enemies to spawn faster as time goes on. The enemies health also scales with the player level. I'm using Unity 2020.1.4.
The problem I have is that at a certain point the framerate just drops cause of the amount of rigidbodies in the scene at the point in time.
Is there any way I can redo this or refine it to make it less resource intensive?
Code:
public class EnemySpawn : MonoBehaviour
{
public GameObject[] enemies, bossEnemy;
float bossTimer;
public float timeForBoss, bossTime, startTimeForBoss = 60f;
public float spawnRadius = 7f, time, defaultTime = 3f;
public float fasterSpawnIncrements, spawnIncTimer, spawnIncTimerHolder;
public bool timerDecreased = false;
// Start is called before the first frame update
void Start()
{
time = defaultTime;
spawnIncTimerHolder = spawnIncTimer; //Set timers for making enemies spawn faster over time
StartCoroutine(SpawnAnEnemy());
bossTimer = startTimeForBoss; //Set timers for making bosses spawn faster over time
timeForBoss = 60f;
}
void FixedUpdate()
{
spawnIncTimerHolder -= Time.deltaTime; //Decrease timer for enemies
while (spawnIncTimerHolder <= 0)
{
time *= fasterSpawnIncrements; //Decrease time between enemy spawns
spawnIncTimerHolder = spawnIncTimer;
}
bossTimer -= Time.deltaTime;
if (bossTimer <= 0)
{
StartCoroutine(SpawnABoss());
if (timeForBoss > 8)
{
timeForBoss -= 4f;
timerDecreased = true;
}
if (timeForBoss <= 8 && timeForBoss > 3)
{
timeForBoss -= 2f;
timerDecreased = true;
}
bossTimer = timeForBoss;
}
}
IEnumerator SpawnAnEnemy()
{
Vector2 spawnPos = GameObject.Find("Player").transform.position;
spawnPos += Random.insideUnitCircle.normalized * spawnRadius; //Set allowable spawn point area around the player
Instantiate(enemies[Random.Range(0, enemies.Length)], spawnPos, Quaternion.identity); //Spawn enemy
yield return new WaitForSeconds(time);
StartCoroutine(SpawnAnEnemy());
}
IEnumerator SpawnABoss()
{
Vector2 spawnPos = GameObject.Find("Player").transform.position;
spawnPos += Random.insideUnitCircle.normalized * spawnRadius; //Set allowable spawn point area for bosses around the player
Instantiate(bossEnemy[Random.Range(0, enemies.Length)], spawnPos, Quaternion.identity); //Spawn boss
yield return new WaitForSeconds(bossTime);
//StartCoroutine(SpawnABoss());
}
}
I know it's not the prettiest code but it works, kinda. I really need this optimised cause my computer is a potato and its getting more and more difficult to playtest every time I want to add a new feature.
Use Window->Profiler to see which steps/scripts actually take up most time.
Object Pooling. Basically, instead of "Instantiate" & "Destroy" you keep filling your pool with Instantiate, but disable instead of destroy. Then, instead of instantiate, you just enable objects and update position, reset health etc. This improves memory handling, garbage collection etc. You can look for a pooling solution or just use an Array/List and manage it yourself.
DOTS/ECS. Unity can handle a TON of entities when you use the Data oriented / entitiy component system. That is a bit different way to program and handle your data.
Unity supports Havoc Physics when using DOTS/ECS. That may help increase performance with your Rigidbodies.
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;
}
}
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.
So when my character gets hit by the enemies fire breath, I want to create the feel of the character being set on fire. So while the character is on fire I want him to lose a specific amount of health for a specific amount of time.
For example; lets say he is on fire for 3 seconds and I want to make him lose 30 health for being on fire, how would I evenly distribute losing 30 health for 3 seconds? I dont want the 30 damage to be applied instantly to the health, I want it to slowly tick away at the players health so that at the 3 second mark 30 damage has been dealt.
The game is being made with c#.
Thanks.
This is just like moving Gameobject over time or doing something over time. The only difference is that you have to use Mathf.Lerp instead of Vector3.Lerp. You also need to calculate the end value by subtracting the value you want to lose over time from the current value of the player's life. You pass this into the b or second parameter of the Mathf.Lerp function.
bool isRunning = false;
IEnumerator loseLifeOvertime(float currentLife, float lifeToLose, float duration)
{
//Make sure there is only one instance of this function running
if (isRunning)
{
yield break; ///exit if this is still running
}
isRunning = true;
float counter = 0;
//Get the current life of the player
float startLife = currentLife;
//Calculate how much to lose
float endLife = currentLife - lifeToLose;
//Stores the new player life
float newPlayerLife = currentLife;
while (counter < duration)
{
counter += Time.deltaTime;
newPlayerLife = Mathf.Lerp(startLife, endLife, counter / duration);
Debug.Log("Current Life: " + newPlayerLife);
yield return null;
}
//The latest life is stored in newPlayerLife variable
//yourLife = newPlayerLife; //????
isRunning = false;
}
Usage:
Let's say that player's life is 50 and we want to remove 2 from it within 3 seconds. The new player's life should be 48 after 3 seconds.
StartCoroutine(loseLifeOvertime(50, 2, 3));
Note that the player's life is stored in the newPlayerLife variable. At the end of the coroutine function, you will have to manually assign your player's life with the value from the newPlayerLife variable.
I suppose, what you are looking for is a Coroutine. Check out here and here for the documentation. It will allow you to do your custom health reducing actions separately from update function. Using coroutines you can make something happening by ticks, and you can determine how much time the tick is.
You could use couroutines. Something like this:
void OnHitByFire()
{
StartCoroutine(DoFireDamage(5f, 4, 10f));
}
IEnumerator DoFireDamage(float damageDuration, int damageCount, float damageAmount)
{
int currentCount = 0;
while (currentCount < damageCount)
{
HP -= damageAmount;
yield return new WaitForSeconds(damageDuration);
currentCount++;
}
}
So this is what I ended up doing. It causes the character on fire to lose 30 health and you can see the health ticking down instead of it happening over intervals.
IEnumerator OnFire()
{
bool burning = true;
float timer = 0;
while (burning)
{
yield return new WaitForSeconds(0.1f);
hp -= 1;
timer += 0.1f;
if (timer >= 3)
{
burning = false;
}
}
}
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.