Hello guys, hope the new year is being good to all of you.
I have a problem with my game. I am trying to fire a laser in my game. I have a list that grabs a collection of laser objects from an array. Each time I fire the laser, the laser stops because the list element value is incremented. I have tried to get my head around it, but I am banging my head against a brick wall. I would like any feedback on how I can get it to move, also move onto the next element (next laser in the list) and not use Rigidbody’s.
Here is my code.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
//REMEMBER TO COMPLETE COMMENTS
public class PlayerWeapons : Starship
{
//START OF VARIABLES
/**BOOL**/
[SerializeField] private bool isWeaponsReady;
/**FLOAT**/
[SerializeField] private float shootSpeed; //SET VARIABLE FOR SPEED OF EACH PLAYER LASER
[SerializeField] private float shootTimer; //SET TIMER TO TELL WHEN PLAYER CAN SHOOT NEXT LASER
/**INTEGER**/
[SerializeField] private int laserSpawnedAmount; //STORES AMOUNT OF LASERS FIRED
/**GAME OBJECT**/
[SerializeField] protected GameObject[] laserWeapon; //STORES CURRENT PLAYER LASER FROM PLAYER LASER LIST
[SerializeField] protected List<GameObject> playerLaser = new List<GameObject>(); //SETS LIST OF PLAYER LASER TO FIRE IN GAME
public void Update()
{
if (!isWeaponsReady)
{
SpawnWeapons();
}
StartCoroutine(MakeWeaponActive());
}
//FUNCTION TO SPAWN LASERS AND MISSILES IN GAME
public void SpawnWeapons()
{
for (int i = 0; i < 3; i++) //LOOP TO ADD PLAYER LINEAR LASER OBJECTS TO LIST
{
playerLaser = (GameObject)Instantiate(laserWeapon[0], gameObject.transform.position, Quaternion.identity); //INSTANTIATE A LIST OF PLAYER LINEAR LASER OBJECTS
playerLaser.transform.parent = gameObject.transform; //SETS PLAYER LASER AS A CHILD OBJECT OF PLAYER STARSHIP
playerLaser.SetActive(false); //SET PLAYER LINEAR LASER TO INACITVE (HIDDEN)
}
for (int j = 3; j <= 5; j++)
{
playerLaser[j] = (GameObject)Instantiate(laserWeapon[1], gameObject.transform.position, Quaternion.identity); //INSTANTIATE A LIST OF PLAYER SPIRAL LASER OBJECTS
playerLaser[j].transform.parent = gameObject.transform; //SETS PLAYER LASER AS A CHILD OBJECT OF PLAYER STARSHIP
playerLaser[j].SetActive(false); //SET PLAYER SPIRAL LASER TO INACITVE (HIDDEN)
}
isWeaponsReady = true;
}
public IEnumerator MakeWeaponActive( )
{
shootTimer -= Time.deltaTime;
if (Input.GetMouseButtonDown(0) && shootTimer < 0.00f && GameManager.Instance.GameReady != false) //IF PLAYER PRESSED FIRST MOUSE BUTTON AND LASER SHOOT TIMER IS BELOW OR EQUAL TO 0
{
IncrementLaserCount();
playerLaser[laserSpawnedAmount].SetActive(true); //SET PLAYER LASER TO ACTIVE (SEEN)
playerLaser[laserSpawnedAmount].transform.position = new Vector2(gameObject.transform.position.x, -2.98f);
//shootTimer = 1.00f;
}
playerLaser[laserSpawnedAmount].transform.Translate(new Vector2(0.00f, shootSpeed) * Time.deltaTime);
yield return null;
}
public void IncrementLaserCount()
{
if (laserSpawnedAmount >= 0 && laserSpawnedAmount <= 4 && shootTimer < 0.00f)
{
laserSpawnedAmount += 1;
}
else if (laserSpawnedAmount >= 5)
{
laserSpawnedAmount = 0;
}
}
}
Haven’t looked at your code all that much but as lordofduct said. code tags please. I did notice something that seemed odd… Shouldn’t the first if statement also have playerLaser|i| like the following?:
for (int i = 0; i < 3; i++) //LOOP TO ADD PLAYER LINEAR LASER OBJECTS TO LIST
{
//INSTANTIATE A LIST OF PLAYER LINEAR LASER OBJECTS
playerLaser[i] = (GameObject)Instantiate(laserWeapon[0], gameObject.transform.position, Quaternion.identity);
//SETS PLAYER LASER AS A CHILD OBJECT OF
playerLaser[i].transform.parent = gameObject.transform; PLAYER STARSHIP
//SET PLAYER LINEAR LASER TO INACITVE (HIDDEN)
playerLaser[i].SetActive(false);
}
for (int j = 3; j <= 5; j++)
{
//INSTANTIATE A LIST OF PLAYER SPIRAL LASER OBJECTS
playerLaser[j] = (GameObject)Instantiate(laserWeapon[1], gameObject.transform.position, Quaternion.identity);
//SETS PLAYER LASER AS A CHILD OBJECT OF PLAYER STARSHIP
playerLaser[j].transform.parent = gameObject.transform;
//SET PLAYER SPIRAL LASER TO INACITVE (HIDDEN)
playerLaser[j].SetActive(false);
}
There is no reason for you to use a Coroutine the way you do it currently. You start a new Coroutine every frame that runs for one frame, which is basically the same as calling a regular method in Update(). You may want to just start one Coroutine with an infinite while loop instead.
Also remember to fix what juicyz mentioned above.
Sorry about the presentation of the code and not having the code tags. I have reconfigured my post to show the the code in its proper format. My fault .
Although, if anyone knows how I can increment the array and still have the previous fired game object moving, please reply. Having headache this morning from trying to figure out a solution.
I think you’re trying to do too much in this one script. From what I can tell, it appears to be trying to both spawn (instantiate) these laser objects, and move them. Don’t do that.
Generally a weapon or player should just spawn the projectile, and then forget about it. The projectile itself should be responsible for moving forward and destroying itself (when it hits something, or goes off screen, or has simply lived for too long).
You can easily tell when you’re on the right track by running your game, and then manually grabbing the projectile prefab and dragging it into the Hierarchy tab. You should see the projectile do its thing (shoot off in its forward direction until it hits something or whatever). So, get that working first.
Now change this PlayerWeapons script so all it does is instantiate projectiles, placing and orienting them correctly, and then forgets about them. You don’t need an array to keep track of them, and you certainly don’t need to be calling Translate on them. Or using Coroutines. (Until you are an advanced Unity developer, you should probably stay away from Coroutines… they are a crutch that can easily lead to thorny problems unless you really know what you’re doing.)
Thank you for your post. I don’t like using Coroutines tbh, although, I see so many tutorials use them that I believe it is standard practice. Do they have a large overhead on performance? I know Instantiate does, which I am trying my best to learn about object pooling.
I wouldn’t say that. They are a crutch that makes writing a tutorial somewhat easier. But as an occasional tutorial writer myself, I think this is a mistake, because it takes a beginner a long time to really understand what the heck a Coroutine is and how it works, involving as it does fairly large doses of magic under the hood. So, sure, you’ve made your tutorial shorter, but at the cost of nobody understanding it.
In my own code, I almost never use them anyway. There’s (almost) nothing a Coroutine can do that an Update method can’t do, and I find the Update methods clearer and easier to maintain. For example, you start off thinking you just need a routine to spawn something every 3 seconds… but then, something happens and you want to spawn something right now, or stop spawning things, or whatever, and now you’re adding control flags and properties to “reach into” your Coroutine and change its behavior, and it’s no longer simpler or shorter than if you’d just used Update in the first place.
No, they don’t have any overhead on performance. In fact it’s possible that, in some cases, they are slightly more performant than Update… but probably not by much, if at all. I still wouldn’t recommend them.
You’re right about Instantiate, but there too, I wouldn’t bother with object pooling until you find you have a real need for it. In most games, spawning something like a typical projectile (which is one GameObject with maybe one or two scripts on it) is so quick it won’t affect the frame rate, unless you’re spawning by the bajillions.
(Of course if you profile your game and find that Instantiate is a bottleneck… definitely pool!)