So I'm currently working on a unity boss fight game where lasers are meant to fire from above on a certain time interval (let's say like 10 seconds) after the player shoots the boss for the first time. What happens is when the boss is hit multiple times, it spawns multiple different laser projectiles. I'm using InvokeRepeating to do this, which I don't think is the most efficient way, so if anyone has any ideas on this, help would be greatly appreciated. Here's the code (it's probably messy I'm pretty new to game development)
using UnityEngine;
public class startboss : MonoBehaviour
{
public bool fight = false;
public Sprite awake;
public bool player_dead = false;
public float spawntime = 10f;
public float spawndelay = 10f;
public bool spawn = false;
// Start is called before the first frame update
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.tag == "Bullet")
{
spawn = true;
fight = true;
if (fight == true)
{
gameObject.GetComponent<SpriteRenderer>().sprite = awake;
if (spawn == true)
{
InvokeRepeating("Shoot", spawntime, spawndelay);
}
}
}
}
public GameObject laserprefab;
public Transform laserpoint;
public Transform laserpoint2;
public float seconds = 10;
public Rigidbody2D rblaser;
// Start is called before the first frame update
// Update is called once per frame
// Start is called before the first frame update
void Shoot()
{
Instantiate(laserprefab, laserpoint.position, laserpoint.rotation);
Instantiate(laserprefab, laserpoint2.position, laserpoint2.rotation);
}
}
It looks like you are creating a new InvokeRepeating("Shoot", spawntime, spawndelay); every time you shoot the boss so it will start another instance of Shoot repeating. So if you shoot the boss 3 times you will have 3 repeating Shoots going on.
You could move the shooting outside of the collision and use a timer to count down instead of InvokeRepeating to have more control. If you need you can set the timer to 0 or stop / start the timer on collision.
//Declair timer and time
float fireTimer = 0.0f;
float fireTime = spawnTime;
void Update(){
//Add time to the timer.
fireTimer += Time.deltaTime;
//If timer = time then fire and reset the timer.
if(fireTimer == fireTime){
Shoot();
fireTimer = 0.0f;
}
}
Related
I am trying to make an endless 2D game. I want to use 3-4 spawners for my game. My problem is that I can't set a specific time for my spawners.
After game started
First spawner will start spawning after 5 seconds and stops after 15 seconds, and it never turns on again.
Second spawner will start spawning after 15 seconds and stop after 25 seconds, and it never turns on again.
Third spawner will start spawning after 25 seconds and stop after 40 seconds, and it never turns on again.
I would like to use the same spawner script for all of them. I think I need some public values.
Here is my spawner code :
using UnityEngine;
using System.Collections;
public class RandomSpawner : MonoBehaviour
{
bool isSpawning = false;
public float minTime = 5.0f;
public float maxTime = 15.0f;
public Transform[] spawnPoints;
public GameObject[] enemies;
IEnumerator SpawnObject(int index, float seconds)
{
Debug.Log("Waiting for " + seconds + " seconds");
int spawnPointIndex = Random.Range(0, spawnPoints.Length);
yield return new WaitForSeconds(seconds);
Instantiate(enemies[index], spawnPoints[spawnPointIndex].position, transform.rotation);
isSpawning = false;
}
void Update()
{
if (!isSpawning)
{
isSpawning = true;
int konumIndex = Random.Range(0, enemies.Length);
StartCoroutine(SpawnObject(konumIndex, Random.Range(minTime, maxTime)));
}
}
}
Use In-Built InvokeRepeating Method,You need to edit according to your Game.
https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html
One option I have used for this kind of thing is to test against Time.realtimeSinceStartup which gives you a the number of seconds since the game started:
if (Time.realtimeSinceStartup >= minTime && Time.realtimeSinceStartup <= maxTime) {
//spawn stuff
}
That might be enough, but one extra thing to mention is if the start of your game is not exactly the correct reference point (for example your scene with the spawner created might start long after the game starts if you have a main menu first), then you might need to store a reference time for when the spawner is...uh...spawned. eg:
public class RandomSpawner : MonoBehaviour {
float objectStartTime;
void Start() {
objectStartTime = Time.realtimeSinceStartup;
}
void Update() {
var timeSinceObjectStart = Time.realtimeSinceStartup - objectStartTime;
if (timeSinceObjectStart > minTime && timeSinceObjectStart < maxTime) {
//spawn stuff
}
}
}
Hope this helps!
I'm having this problem with my enemy shooting, you see I'm using raycasting to detected where my player is and once detected I want the enemy to shoot, so far I have accomplished that but however there's not delay between each Instantiated bullet!So the bullet is being constantly spawn rather than a delay in between each spawn. I've tried a whole lot of different solutions to fix this problem but nothing worked! I've tried IEnumerator, object pooling, creating a count down timer and invoke & invokerepeating but still my bullets are still being instantiated instantly without no delay. Does any one knows how to have a delay between each instantiated bullet?? Thank you!
This is my script:
public GameObject bulletPrefab;
public Transform bulletSpawn;
public Transform sightStart, sightEnd;
public bool spotted = false;
void Update()
{
RayCasting ();
Behaviours ();
}
void RayCasting()
{
Debug.DrawLine (sightStart.position, sightEnd.position, Color.red);
spotted = Physics2D.Linecast (sightStart.position, sightEnd.position, 1 << LayerMask.NameToLayer("Player"));
}
void Behaviours()
{
if (spotted == true) {
//Invoke("Fire", cooldownTimer);
Fire ();
} else if (spotted == false) {
//CancelInvoke ();
}
}
void Fire()
{
GameObject bullet = (GameObject)Instantiate (bulletPrefab);
//GameObject bullet = objectPool.GetPooledObject ();
bullet.transform.position = transform.position;
bullet.GetComponent<Rigidbody2D> ().velocity = bullet.transform.up * 14;
Destroy (bullet, 2.0f);
}
You need to throttle the rate at which bullets are spawned. You could base it on number of frames but that's a bad approach as fire rate will vary with game performance.
A better approach is to use a variable to track how much time must elapse before we can fire again. I'll call this "Time To Next Shot" (TTNS)
Decrement TTNS by the time that has elasped.
Check if TTNS is 0 or less
If so:
Set TTNS as appropriate for our desired rate of fire.
Fire a shot
Something like..
private float fireRate = 3f; // Bullets/second
private float timeToNextShot; // How much longer we have to wait.
// [Starts at zero so our first shot is instant]
void Fire() {
// Subtract the time elapsed (since the last frame) from TTNS .
timeToNextShot -= time.deltaTime;
if(timeToNextShot <= 0) {
// Reset the timer to next shot
timeToNextShot = 1/fireRate;
//.... Your code
GameObject bullet = (GameObject)Instantiate (bulletPrefab);
//GameObject bullet = objectPool.GetPooledObject ();
bullet.transform.position = transform.position;
bullet.GetComponent<Rigidbody2D> ().velocity = bullet.transform.up * 14;
Destroy (bullet, 2.0f);
}
}
This does assume that you only call Fire() once per frame (Otherwise the elapsed time will be subtracted from nextShot multiple times).
I opted to do it this way (subtracting slices from a time span) rather than defining an absolute time (wait until time.elapsedTime > x) as the resolution of elapsedTime decreases over time and that approach will result in glitches after a few hours of gameplay.
I am pretty fluent in using Unity, but regarding Mechanim and Animations I am not the too great at currently, so please don't give me too much of a hard time lol. So I have this boolean that is in my GameManager script:
public bool countDownDone = false;
This boolean gets set to true once my "3,2,1, GO!" Countdown Timer ends at the beginning of my game. Everything in my game starts after that boolean is set to true. Example:
using UnityEngine;
using System.Collections;
public class PlaneMover : MonoBehaviour {
private GameManagerScript GMS; // Reference to my GameManager Script
public float scrollSpeed;
public float tileSizeZAxis;
public float acceleration; //This has to be a negative number in the inspector. Since our plane is coming down.
private Vector3 startPosition;
void Start () {
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> (); //Finds the Script at the first frame
// Transform position of the quad
startPosition = transform.position;
}
void Update () {
if (GMS.countDownDone == true) //Everything starts once the countdown ends.
{
/* Every frame - time in the game times the assigned scroll speed
and never exceed the length of the tile that we assign */
float newPosition = Mathf.Repeat (Time.time * scrollSpeed, tileSizeZAxis);
// New position equal to the start position plus vector3forward times new position
transform.position = startPosition + Vector3.forward * newPosition; // was vector3.forward
scrollSpeed += Time.deltaTime * acceleration; // This makes the plane increase in speed over time with
// whatever our acceleration is set to.
}
}
}
I have this Crawling animation that plays at the very beginning of the game (Even before the Timer ends) and loops forever. My question is , how do I make that crawling animation also start once that boolean is set to "true"? Or do I just apply it to a CoRoutine and make it play after 3 seconds? I have done extensive research on whether to reference Animation or Animator and I got confused on that too. Any advice? If you need more pictures or details on my question, just let me know. Thank you! :)
NEW CODE BELOW
using UnityEngine;
using System.Collections;
public class Crawling : MonoBehaviour {
Animator animator;
private GameManagerScript GMS;
void Start ()
{
animator = GetComponent<Animator> ();
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> ();
}
void Update ()
{
if (GMS.countDownDone == true)
{
animator.Play("Main Character Crawling", 1);
}
}
}
I have this Crawling animation that plays at the very beginning of the
game (Even before the Timer ends) and loops forever. My question is ,
how do I make that crawling animation also start once that boolean is
set to "true"?
The solution is to play the animation from script.
Remove the currentAnimation.
Select the GameObject your PlaneMover script is attached to, attach Animation and Animator components to it. Make sure that the Animation's Play Automatically is unchecked.
public class PlaneMover : MonoBehaviour {
private GameManagerScript GMS; // Reference to my GameManager Script
public float scrollSpeed;
public float tileSizeZAxis;
public float acceleration; //This has to be a negative number in the inspector. Since our plane is coming down.
private Vector3 startPosition;
Animation animation;
public AnimationClip animationClip; //Assign from Editor
void Start () {
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> (); //Finds the Script at the first frame
// Transform position of the quad
startPosition = transform.position;
animation = GetComponent<Animation>();
//Add crawing Animation
animation.AddClip(animationClip, "Crawling");
//Add other animation clips here too if there are otheres
}
void Update ()
{
if (GMS.countDownDone) //Everything starts once the countdown ends.
{
/* Every frame - time in the game times the assigned scroll speed
and never exceed the length of the tile that we assign */
float newPosition = Mathf.Repeat (Time.time * scrollSpeed, tileSizeZAxis);
// New position equal to the start position plus vector3forward times new position
transform.position = startPosition + Vector3.forward * newPosition; // was vector3.forward
scrollSpeed += Time.deltaTime * acceleration; // This makes the plane increase in speed over time with
// whatever our acceleration is set to.
//Play Animation
animation.PlayQueued("Crawling", QueueMode.CompleteOthers);
}
}
} }
I solved the problem!!
This is my script:
using UnityEngine;
using System.Collections;
public class Crawling : MonoBehaviour {
public Animator animator;
private GameManagerScript GMS;
void Start ()
{
animator = GetComponent<Animator> ();
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> ();
}
void Update ()
{
if (GMS.countDownDone == true) {
animator.enabled = true;
}
else
{
animator.enabled = false;
}
}
}
All I do is just enable the Animator that I attach in the Inspector whenever "countDownDone" becomes "true", and for added safety, I added the "else" for it to be disabled. If someone notices a way for me to improve this script, please let me know. Thank you :)
Shorter version:
if (GMS.countDownDone)
animator.enabled = true;
else
animator.enabled = false;
I want to start an animation when my player enters a specific distance to an object, but my animation isn't starting. Any suggestion as to why it isn't starting?
Here is the code that I have so far:
using UnityEngine;
using System.Collections;
public class MoveTo : MonoBehaviour {
public Transform Player;
public Transform goal;
public Animator ani;
public Animator ani2;
// Use this for initialization
void Start ()
{
ani.Stop (); // this stop function is working accurate
ani2.Stop ();
}
void Update()
{
float dist = Vector3.Distance (Player.position, transform.position);
if (dist < 5)
{
ani.Play("Horse_Walk");// this is not working (Horse_Walk) is a state name
ani2.Play("Horse_Run");
pstart();
}
}
void pstart(){
NavMeshAgent agent=GetComponent<NavMeshAgent>();
agent.destination = goal.position;
}
}
There can be several reason including (1) wrong string (2) string name not matched and finally which seems more appropriate reason is that you playing animation again. you should need be call it with bool
bool isPlayAnim =true;
void Update()
{
float dist = Vector3.Distance (Player.position, transform.position);
if (dist < 5 && isPlayAnim)
{
isPlayAnim = false;//again true it on you specfic event
ani.Play("Horse_Walk");// this is not working (Horse_Walk) is a state name
ani2.Play("Horse_Run");
pstart();
}
}
isPlayAnim bool you can make and use to play animation only one time and again true it in specific event.
Or
Alternatively you need to use collider and its event to run this update code. I guess it is best way to do this job .
I'm working on a script, so I could detect when player is not moving for x seconds and load another scene accordingly.
If player started moving again after x seconds, then loading another scene should't be called.
I've tried using isSleeping function and delay it by including Coroutine with WaitForSeconds but it is still checking Rigidbody2D every frame. Is there any other way to check if Rigidbody2D hasn't moved for x seconds and only then load game over level, otherwise continue moving as before?
using UnityEngine;
using System.Collections;
public class PlayerStop : MonoBehaviour {
void Update() {
if (GetComponent<Rigidbody2D>().IsSleeping()) {
Application.LoadLevel(2);
}
}
}
In addition I have a script, which enables me to draw lines (with mouse) and stop player's movement, however lines disappear after x seconds. So for example if I'd set lines to disappear after 1 second, I would like to check if Rigidbody2D stopped moving for 2 seconds and only then load game over scene. Otherwise do nothing as Rigidbody2D will continue moving again after line disappears.
Try this
using UnityEngine;
using System.Collections;
public class PlayerStop : MonoBehaviour {
float delay = 3f;
float threshold = .01f;
void Update() {
if (GetComponent<Rigidbody2D>().velocity.magnitude < threshold * threshold)
StartCoRoutine("LoadTheLevel");
}
IEnumerator LoadTheLevel()
{
float elapsed = 0f;
while (GetComponent<Rigidbody2D>().velocity.magnitude < threshold * threshold)
{
elapsed += Time.deltaTime;
if(elapsed >= delay)
{
Application.LoadLevel(2);
yield break;
}
yield return null;
}
yield break;
}
}
You could try this...I'm not able to test this right now, so may need a little tweaking...
First some private variables:
private float _loadSceneTime;
private Vector3 _lastPlayerPosition;
private float _playerIdleDelay;
And in the Update method check if the player has moved:
private void Update()
{
// Is it time to load the next scene?
if(Time.time >= _loadSceneTime)
{
// Load Scene
}
else
{
// NOTE: GET PLAYERS POSITION...THIS ASSUMES THIS
// SCRIPT IS ON THE GAME OBJECT REPRESENTING THE PLAYER
Vector3 playerPosition = this.transform.position;
// Has the player moved?
if(playerPosition != _lastPlayerPosition)
{
// If the player has moved we will attempt to load
// the scene in x-seconds
_loadSceneTime = Time.time + _playerIdleDelay;
}
_lastPlayerPosition = playerPosition;
}
}