I have this code which is loaded when you finish a game level. It should freeze the time, scale the level completed message, wait 5 seconds and then load next level and unfreeze time. The problem is that the WaitForSeconds is not working. It just loads the next level without waiting. What am I doing wrong?
IEnumerator WaitingFunction(){
yield return new WaitForSeconds(5);
}
void OnTriggerEnter ( Collider col )
{
if (col.tag == "Player")
{
Time.timeScale = 0f;
lvlcomplete.transform.localScale = new Vector3 (1,1,1);
StartCoroutine(WaitingFunction());
Time.timeScale = 1f;
lvlcomplete.transform.localScale = new Vector3 (0,0,0);
Application.LoadLevel(Application.loadedLevel+1);
player.transform.position = new Vector3(0f, 0.3519309f, -2.381933f);
}
You start a coroutine that waits 5 seconds without doing anything while the OnTriggerEnter code continues to load the level instantly.
You should move the code that should run after the wait time has passed inside the coroutine:
IEnumerator WaitingFunction(){
yield return new WaitForSeconds(5);
Time.timeScale = 1f;
lvlcomplete.transform.localScale = new Vector3 (0,0,0);
Application.LoadLevel(Application.loadedLevel+1);
player.transform.position = new Vector3(0f, 0.3519309f, -2.381933f);
}
Like LearnCocos2D said you have to move the code. Further on to get this working with timeScale = 0 you simply need to let this wait for the corresponding number of frames:
IEnumerator WaitingFunction(){
int noOfFramesToWait = (int)(seconds * Application.targetFrameRate);
for (int i = 0; i < noOfFramesToWait; i++) {
yield return null;
}
Time.timeScale = 1f;
// do my stuff
}
If you have a couple of situations like pausing, additive level loading, game over, fading in and out,.. the next step can be get this more generic:
void OnTriggerEnter ( Collider col ) {
if (col.tag == "Player") {
Time.timeScale = 0f;
StartCoroutine (WaitForNFrames (5f, LoadNewLevel));
}
}
public void LoadNewLevel () {
Time.timeScale = 1f;
// do my stuff
}
IEnumerator WaitForNFrames (float seconds, System.Action executeWhenFinished) {
int noOfFramesToWait = (int)(seconds * Application.targetFrameRate);
for (int i = 0; i < noOfFramesToWait; i++) {
yield return null;
}
executeWhenFinished ();
}
Another small thing, maybe you have this already: You reference the player object after calling LoadLevel. Ensure that it is not destroyed when the new level is loaded by using DontDestroyOnLoad.
Try restructuring your code as follows
WaitingFunction(){
yield WaitForSeconds (5);
}
void OnTriggerEnter ( Collider col )
{
if (col.tag == "Player")
{
Time.timeScale = 0f;
lvlcomplete.transform.localScale = new Vector3 (1,1,1);
StartCoroutine(WaitingFunction());
Time.timeScale = 1f;
lvlcomplete.transform.localScale = new Vector3 (0,0,0);
Application.LoadLevel(Application.loadedLevel+1);
player.transform.position = new Vector3(0f, 0.3519309f, -2.381933f);
}
Related
I am very new to c# and I've come across a problem with my enemy spawner. I am making an endless top-down shooter, and after 20 enemies have been spawned and then destroyed, the enemies stop appearing. My score counter continues to increase though, which leads me to believe they're somehow being destroyed.
Here is the enemy spawn controller:
void Update()
{
if (!spawningObject && GameController.EnemyCount < spawnSettings[0].maxObjects)
{
spawningObject = true;
float pick = Random.value * totalWeight;
int chosenIndex = 0;
float cumulativeWeight = enemySpawnables[0].weight;
while(pick > cumulativeWeight && chosenIndex < enemySpawnables.Count - 1)
{
chosenIndex++;
cumulativeWeight += enemySpawnables[chosenIndex].weight;
}
StartCoroutine(SpawnObject(enemySpawnables[chosenIndex].type, Random.Range(spawnSettings[0].minWait / GameController.DifficultyMultiplier, spawnSettings[0].maxWait / GameController.DifficultyMultiplier)));
Spawn();
}
}
private IEnumerator SpawnObject(string type, float time)
{
yield return new WaitForSeconds(time);
randomSpawnPoint = Random.Range(0, enemySpawners.Length);
randomEnemy = Random.Range(0, enemy.Length);
enemyPool.SpawnObject(enemySpawners[randomSpawnPoint].position, transform.rotation);
spawningObject = false;
GameController.EnemyCount++;
}
And here is the enemy controller (attached to enemy prefab):
public void Start()
{
myRB = GetComponent<Rigidbody>();
player = FindObjectOfType<PlayerController>();
}
private void OnEnable()
{
player = FindObjectOfType<PlayerController>();
}
void Update()
{
if (health <= 0)
{
Die();
}
}
void FixedUpdate()
{
transform.LookAt(player.transform.position);
myRB.velocity = (transform.forward * moveSpeed);
//Falling
if(myRB.velocity.y < 0)
{
myRB.velocity += Vector3.up * Physics.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
}
}
public void Die()
{
print("Enemy" + this.gameObject.name + " has died!");
EnemySpawn.instance.enemyPool.ReturnObject(this.gameObject);
player.GetComponent<PlayerController>().points += pointsToGive;
ScoreController.scoreValue += 1;
}
The only thing that should affect enemy health is the bullet. Here is the part of the bullet controller that affects enemy health:
public void OnTriggerEnter(Collider other)
{
if(other.tag == "Enemy")
{
triggeringEnemy = other.gameObject;
triggeringEnemy.GetComponent<EnemyController>().health -= damage;
PlayerController.instance.bulletPool.ReturnObject(gameObject);
}
}
The issue always happens after 20 enemies have been spawned/destroyed and 20 points have been achieved. Before then, enemy movement is normal and everything seems to be working as it should. It could very well be something very simple that I'm missing, but I'd appreciate any help!
When you do engage logic mistake good way to find it is Visual Studio debugging.
Here is good official article about whole process - https://docs.unity3d.com/Manual/ManagedCodeDebugging.html.
I would place breakpoint right at start of Update method to see that EnemyCount increases over time and then you will understand that you forgot to do decrement it.
I'm trying to make my first game, but I have a problem with the speed control:
Whenever I hold down on the button, everything slows down. I'm using time.deltatime which I know is global but I can't find any fixes.
void Start ()
{
self = GetComponent<Rigidbody2D>();
InvokeRepeating("SwitchDirections", switchTime, switchTime * 2);
StartCoroutine(SpawnBallLoop());
StartCoroutine(Count());
}
IEnumerator SpawnBallLoop ()
{
while (gameOver == false)
{
yield return new WaitForSecondsRealtime(2f);
SpawnBall();
}
}
void SpawnBall ()
{
Instantiate(ball, new Vector3(Random.Range(-2.18f, 2.18f), 4.6f, 0f), Quaternion.identity);
}
void UpdateClock ()
{
seconds += 1;
clock.text = "Time: " + seconds;
}
void SwitchDirections ()
{
moveSpeed *= -1;
}
void FixedUpdate ()
{
self.velocity = new Vector2(moveSpeed, 0f);
if (Input.GetMouseButton(0))
{
Time.timeScale = 0.5f;
}
else
{
Time.timeScale = 1f;
}
}
IEnumerator Count ()
{
while (gameOver == false)
{
yield return new WaitForSecondsRealtime(1);
UpdateClock();
}
}
public void OnCollisionEnter2D(Collision2D collision)
{
if (collision.transform.tag == "Ball")
{
GetComponent<SpriteRenderer>().enabled = false;
transform.GetChild(0).gameObject.SetActive(true);
}
}
Time.timeScale = 0.5f;
TimeScale is the scale at which time passes. This can be used for slow motion effects.
I suggest you to read this : here
Time.timeScale will slow down every game object in your scene. If you want only one game object to slow down use reduce movespeed of that one gameobject.
I have a problem, I am trying to make the enemy shoot at the player every 5 seconds and right now it is just shooting lots of bullets constantly in a line.
This is my code, I would really appreciate some help!
void ShootAtPlayer()
{
StartCoroutine(bulletshooting());
IEnumerator bulletshooting()
{
shooting = true;
if (shooting == true)
{
GameObject tempBullet = Instantiate(enemyBullet, eyes.gameObject.transform.position, eyes.gameObject.transform.rotation) as GameObject; //shoots from enemies eyes
Rigidbody tempRigidBodyBullet = tempBullet.GetComponent<Rigidbody>();
tempRigidBodyBullet.AddForce(tempRigidBodyBullet.transform.forward * enemyBulletSpeed);
Destroy(tempBullet, 0.1f);
yield return new WaitForSeconds(5f);
}
shooting = false;
}
From your comment
shootAtPlayer is called in the update when the enemy is close enough to the player
The issue is: You are starting a new Coroutine every frame!
In your case I wouldn't use a Coroutine at all since you already have a code that is executed every frame. (Because otherwise you would need some code to also stop the running routine)
Rather use a simple timer:
[SerializeField] private float cooldown = 5;
private float cooldownTimer;
void ShootAtPlayer()
{
cooldownTimer -= Time.deltaTime;
if(cooldownTimer > 0) return;
cooldownTimer = cooldown;
GameObject tempBullet = Instantiate(enemyBullet, eyes.gameObject.transform.position, eyes.gameObject.transform.rotation) as GameObject; //shoots from enemies eyes
Rigidbody tempRigidBodyBullet = tempBullet.GetComponent<Rigidbody>();
tempRigidBodyBullet.AddForce(tempRigidBodyBullet.transform.forward * enemyBulletSpeed);
Destroy(tempBullet, 0.1f);
}
If you really want to use a Coroutine for that it would probably look like
private bool isShooting;
void ShootAtPlayer()
{
// Only allow a new routine if there is none running already
if(isShooting) return;
StartCoroutine (shootRoutine());
}
private IEnumerator shootRoutine()
{
// Just in case avoid concurrent routines
if(isShooting) yield break;
isShooting = true;
GameObject tempBullet = Instantiate(enemyBullet, eyes.gameObject.transform.position, eyes.gameObject.transform.rotation) as GameObject; //shoots from enemies eyes
Rigidbody tempRigidBodyBullet = tempBullet.GetComponent<Rigidbody>();
tempRigidBodyBullet.AddForce(tempRigidBodyBullet.transform.forward * enemyBulletSpeed);
Destroy(tempBullet, 0.1f);
yield return new WaitForSeconds (5f);
isShooting = false;
}
i did some code making an enemy search if the player exists or not and if the player exists go-to player to kill him the code is working fine but every moment the enemy move the speed keep increased and i do not want the speed increased at all so how to make it moving at the same starting speed without any increase
this is my code
void Update () {
if (target == null) {
if (!searchingForThePlayer) {
searchingForThePlayer = true;
StartCoroutine (searchForPlayer ());
}
return;
}
if (Vector3.Distance (target.position, transform.position) < 20) {
transform.position = Vector2.MoveTowards (transform.position, target.position, 1f * Time.deltaTime);
if (target.position.x < transform.position.x && !facingRight)
Flip ();
if (target.position.x > transform.position.x && facingRight)
Flip ();
} else {
}
StartCoroutine (UpdatePath ());
}
IEnumerator searchForPlayer () {
GameObject sRusult = GameObject.FindGameObjectWithTag ("Player");
if (sRusult == null) {
yield return new WaitForSeconds (0.5f);
StartCoroutine (searchForPlayer ());
} else {
target = sRusult.transform;
searchingForThePlayer = false;
StartCoroutine (UpdatePath ());
yield return null;
}
}
IEnumerator UpdatePath () {
if (target == null) {
if (!searchingForThePlayer) {
searchingForThePlayer = true;
StartCoroutine (searchForPlayer ());
}
yield return null;
} else {
if (Vector3.Distance (target.position, transform.position) < 20) {
transform.position = Vector2.MoveTowards (transform.position, target.position, 1f * Time.deltaTime);
if (target.position.x < transform.position.x && !facingRight)
Flip ();
if (target.position.x > transform.position.x && facingRight)
Flip ();
} else {
}
}
// Start a new path to the target position, return the result to the OnPathComplete method
yield return new WaitForSeconds (1f / 2f);
StartCoroutine (UpdatePath ());
}
void Flip () {
Vector3 scale = transform.localScale;
scale.x *= -1;
transform.localScale = scale;
facingRight = !facingRight;
}
Every frame, in your Update() method, you start the UpdatePath() coroutine. Then, in UpdatePath(), you start another UpdatePath() coroutine. In no situation do you don't start it, so this ensures that UpdatePath() keeps running forever and ever.
Since you also keep starting a new one in Update(), this means that you keep piling on more and more coroutines, which means the more the game runs, the more UpdatePath() gets called every frame.
in other words, your object's speed isn't technically increasing, it's just the number of time MoveTowards() is called that does, which does have the same end result.
As for the fix, I'd recommend restructuring your code. For example, I find it highly suspicious that Update() and UpdatePath() are near identical copies of each other. I also find it weird that UpdatePath() starts itself at the end of its run.
I wanted to use a coroutine to make the player wait a little before shooting a new bullet but it never gets past the yield. Here's the code
protected override void Start () {
base.Start ();
animator = GetComponent<Animator> ();
score = GameManager.instance.playerScore;
playerLives = GameManager.instance.playerLife;
}
void Update(){
int horizontal = (int)Input.GetAxisRaw("Horizontal");
AttemptMove (horizontal, 0);
if (Input.GetKeyDown ("space")) {
if(canShoot){
Vector3 shootPosition = transform.position + new Vector3 (0, 0.5f, 0);
Instantiate (bullet, shootPosition, Quaternion.identity);
StartCoroutine(Shoot());
}
}
}
IEnumerator Shoot(){
Debug.Log("Start");
canShoot=false;
yield return new WaitForSeconds (shootingTimeDelay);
canShoot=true;
Debug.Log("End");
}
shootingTimeDelay is set to 1.1f. I am not destroying my gameObject anywhere and it works properly in other scripts in my project.
It never prints End. I don't get what is wrong
I would say don't use a coroutine for something like this.
Trying doing this and see if you get better restults
private float time = 0;
public float fireTime = 1.1f;
private void Update()
{
time += Time.deltaTime;
if(Input.GetKeyDown("space") && time >= fireTime)
{
Vector3 shootPosition = transform.position + new Vector3 (0, 0.5f, 0);
Instantiate (bullet, shootPosition, Quaternion.identity);
time = 0;
}
}
Ok i found the bug. I had a method in its superclass that called StopAllCoroutines but for some reason i never tought of it until now. Changed that to StopCoroutine("name of my coroutine") and now it works perfect :)