C# (Galaga Style Project) - Limit Projectile Prefab Clones based on Hierarchy - c#

I am trying to practice creating a clone of Galaga following the same ruleset as the original. I am currently stuck trying to attempt a limit on the amount of cloned prefabs that can be in the scene at any one time, in the same way that Galaga's projectiles are limited to 2 on screen at any time. I want to make it so the player can shoot up to two projectiles, which destroy after 2 seconds or when they collide (this part is functioning), followed by not being able to shoot if two projectile clones are active and not yet destroyed in the hierarchy (Not working as I can instantiate projectiles over the limit of 2).
I have combed through Google for about 3 hours with no solutions that have worked for me, at least in the ways that I had attempted to implement them.
Thank y'all so much for the help!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerController : MonoBehaviour
{
public float moveSpeed = 1.0f;
public playerProjectile projectile;
public Transform launchOffset;
public int maxBullets = 0;
private GameObject cloneProjectile;
public Rigidbody2D player;
// Start is called before the first frame update
void Start()
{
player = this.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
MovePlayer();
PlayerShoot();
}
public void MovePlayer()
{
player.velocity = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")) * moveSpeed;
}
public void PlayerShoot()
{
if (Input.GetKeyDown(KeyCode.Z))
{
var cloneProjectile = Instantiate(projectile, launchOffset.position, launchOffset.rotation);
maxBullets++;
if (maxBullets >= 3)
{
Destroy(cloneProjectile, 0.1f);
maxBullets --;
return;
}
}
}
}

You could change the logic up a bit. An instance of the playerController class is active as long as the game is active, so it will know and retain the value of 'maxBullets' until you die or exit the program.
So instead, every time you click "z", the first thing you should do is run the check. If the current amount of live projectiles equals the maximum, have the logic 'return' and exit out of the method.

Related

Unity Transform not updating position realtime

I am trying to create a simple scriptable object for my shoot ability. I have that aspect working, but as I try to set my Transform to my player, it does not update the shoot position. I am very new to C#, and this script isnt complete. I still need to add the functionality to destroy the created objects. Any help would be greatly appreciated. I suspect I need to add an update function but im am not certain how to do this.
using UnityEngine.InputSystem;
using UnityEngine.AI;
using UnityEngine;
namespace EO.ARPGInput
{
[CreateAssetMenu]
public class Shoot : Ability
{
public Transform projectileSpawnPoint;
public GameObject projectilePrefab;
public float bulletSpeed = 10;
public float bulletLife = 3;
public override void Activate(GameObject parent)
{
var projectile = Instantiate(projectilePrefab, projectileSpawnPoint.position, projectileSpawnPoint.rotation);
projectile.GetComponent<Rigidbody>().velocity = projectileSpawnPoint.forward * bulletSpeed;
Destroy(projectile, bulletLife);
void OnCollisionEnter(Collision collision)
{
Destroy(projectile);
}
}
}
}
I'm still new to Unity and coding also, so take my advice with a load of salt :P.
It may be best to have a transform on your character (say just past the barrel of the player's gun) that you can put as the projectileSpawnPoint. In your code the projectileSpawnPoint is never set. Your first line of code in the "Activate" method should be something like:
public override void Activate(GameObject parent)
{
projectileSpawnPoint = playerGunBarrelTransform.transform.position;
var projectile = Instantiate(projectilePrefab, projectileSpawnPoint.position, projectileSpawnPoint.rotation);
projectile.GetComponent<Rigidbody>().velocity = projectileSpawnPoint.forward * bulletSpeed;
Destroy(projectile, bulletLife);
For destroying the projectile afterward you can keep it as you have it in OnCollision. howeer, with bullets in particular, since they tend to be instantiated A LOT and then destroyed afterward it would be best to use an object pooler for them to instantiate several of them on start and then disable and enable them as needed so you can resuse them instead of making new ones every time.
you have to create a new script that derives from Monobehaviour for your projectiles. attach that script to the projectile prefab and place the OnCollisionEnter method in that script. now your projectiles should get destroyed when touching another collider. make sure that there is a rigidbody component attached to the projectile.

I need help for spawning enemies in unity 2d

I have problem spawning enemies in unity 2d. Enemies clones themselves too quickly and it lags. Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
public Transform[] spawnPoints;
public GameObject enemy;
public float spawnTime = 5f;
public float spawnDelay = 3f;
// Use this for initialization
void Start () {
InvokeRepeating ("addEnemy", spawnDelay, spawnTime);
}
void addEnemy() {
int spawnPointIndex = Random.Range(0, spawnPoints.Length);
Instantiate (enemy, spawnPoints[spawnPointIndex].position,
spawnPoints[spawnPointIndex].rotation);
}
}
Oh, I am currently making a 2D game where I have to spawn enemies and here is what code I used, edited for you of course:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
public GameObject enemyPrefab;
public float interval = 100;
private float counter = 0;
// Update is called once per frame
void FixedUpdate()
{
counter += 1;
if(counter >= interval){
counter = 0;
Instantiate(enemyPrefab, transform.position,transform.rotation);
}
}
}
Simply create a new game object, put this script it, add the enemy prefab in the Game Object variable and you are set. You can edit the interval between each enemy with the interval variable. Good luck with your project :)
I'm assuming they are spawning more than every 3 seconds? Do you have multiple 'EnemySpawner' scripts attached to objects in the scene?
edit sounds like you have an ‘EnemySpawner’ script in your prefab, remove it from prefab and put it on a separate GameObject in your scene.
You should not as in the accepted answer use FixedUpdate for this! It uses 100 fixed update steps .. but who wants to calculate how long this actually takes?
By default fixed update step is 0.02s so 100 means 2 real-time seconds - but what if you change the fixed update step for any reason?
I would rather do it using a simple timer in Update:
public class EnemySpawner : MonoBehaviour
{
public GameObject enemyPrefab;
// In seconds
[SerializeField]private float interval = 2f;
private float timer = 0f;
// Update is called once per frame
void Update()
{
timer += Time.deltaTime;
if(timer >= interval){
timer = 0f;
Instantiate(enemyPrefab, transform.position,transform.rotation);
}
}
}
Actually what you had should also work! Depends of course also on the values you set via the Inspector.
BUT What I can see from the image you posted is: In enemy you referenced the object itself!
=> The next time there are 2 enemy instances including this spawner
=> The next time there are 4 enemy instances including this spawner
=> The next time there are 8 enemy instances ...
=> etc
Therefore they are called Enemy[Clone] then Enemy[Clone][Clone] then Enemy[Clone][Clone][Clone] etc so
Not sure if this was intended but I would rather put the spawner script on one single object in the scene and rather reference a prefab without an additional spawner script.

Enemy movement to only follow player when in range using navmesh

I am very new at C# (around 1 week in), so I have been following a lot of tutorials. I am at the moment trying to edit a piece of code (unity tutorial) to only have the enemy follow the player when in a certain range of the player (10 foot), but unfortunately without changing the code completely, I cannot find a solution. At the moment the enemy will only follow the player when the player is alive (which i also want).
I've tried googling what I want, but I don't want to change the code from what is it too much. I am very new at c# and and currently learning bit by bit, other methods I have found require me changing the code a lot. As far as I understand, the main focus of the code below is for the enemy to be completely controlled by the Nav Mesh Agent, is it possible for me to keep the same technique?
using UnityEngine;
using System.Collections;
public class EnemyMovement : MonoBehaviour
{
Transform player;
PlayerHealth playerHealth;
EnemyHealth enemyHealth;
UnityEngine.AI.NavMeshAgent nav;
void Awake()
{
// references
player = GameObject.FindGameObjectWithTag("Player").transform;
playerHealth = player.GetComponent<PlayerHealth>();
enemyHealth = GetComponent<EnemyHealth>();
nav = GetComponent<UnityEngine.AI.NavMeshAgent>();
}
void Update()
{
if (enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)
{
nav.SetDestination(player.position);
transform.LookAt(player);
}
else
{
nav.enabled = false;
}
}
}
If possible, I would like to add 2 if functions. 1 being the check if the player is alive, and the other to check if the player is within distance. I don't know much about raycast at the moment, so will this be the next step for me to learn to get this to work how i want?
Thankyou for your time.
Dean.
You can calculate distance by doing:
float distance = Vector3.Distance(player.transform.position,transform.position);
You can do a check if it's no larger than some amount with:
bool playerIsCloseEnough = distance <= amount;
And you can check if the player is alive with:
bool playerIsAlive = playerHealth.currentHealth > 0;
Then, you can do things if they're both true with:
if (playerIsAlive && playerIsCloseEnough)
{
// ...
}
And as you mentioned in the comment, you'll need to set nav.enabled=true; in void Awake or void Start to make sure the nav mesh is turned on :)

Score Count not working on a prefab

This is semi complicated of a question but I'll do my best to explain it:
I am making this mobile game in which you have to shoot four cubes. I'm trying to make it so when the cubes are shot by a bullet, they're destroyed and a UI text says 1/4, to 4/4 whenever a cube is shot. But it's being really weird and only counts to 1/4 even when all four cubes are shot and destroyed. I put these two scripts on the bullets (I made two separate scripts to see if that would do anything, it didn't)
And to give a better idea of what I'm talking about, here's a screenshot of the game itself.
I've been using Unity for about 6 days, so I apologize for anything I say that's noob-ish.
EDIT
So I combined the two scripts onto an empty gameobject and here's the new script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GameManagerScript : MonoBehaviour {
public GameObject cubes;
public Text countText;
public int cubeCount;
public Transform target;
// Use this for initialization
void Start () {
}
void OnTriggerEnter(Collider other)
{
cubes = other.gameObject;
}
// Update is called once per frame
void Update () {
cubes.transform.position = Vector3.MoveTowards(transform.position, target.position, 1f * Time.deltaTime);
if (cubes.gameObject.tag == "BULLET")
{
cubeCount = cubeCount + 1;
countText.text = cubeCount + "/4";
cubes.SetActive(false);
}
}
}
ANOTHER EDIT
I tried everything, so is there a way to detect when all the children in a parent on the Hierarchy are destroyed? Instead of counting up? This can give a better idea:
So I want to be able to detect when Cube, Cube1, Cube2, and Cube3 have all been destroyed.
The answer is pretty simple: Since every individual bullet has that script, each bullet has its own score.
For something like a score you want a single spot to store it, e.g. a script on an empty gameobject that serves as game controller. Just access that in the collision and increase the score (maybe have a look on singletons here).
You can combine those two scripts and actually it might be better to not have this on the bullet, but on the target because there are probably less of them which will save you some performance. (And it does more sense from a logical point of view.)
Edit:
I assume you create the bullets using Instantiate with a prefab. A prefab (= blueprint) is not actually in the game (only objects that are in the scene/hierarchy are in the game). Every use of Instantiate will create a new instance of that prefab with it's own version of components. A singleton is a thing that can only exist once, but also and that is why I mention it here, you can access it without something like Find. It is some sort of static. And an empty gameobject is just an object without visuals. You can easily create one in unity (rightclick > create empty). They are typically used as container and scriptholders.
Edit:
What you want is:
An empty gameobject with a script which holds the score.
A script that detects the collision using OnTriggerEnter and this script will either be on the bullets or on the targets.
Now, this is just a very quick example and can be optimized, but I hope this will give you an idea.
The script for the score, to be placed on an empty gameobject:
public class ScoreManager : MonoBehaviour
{
public Text scoreText; // the text object that displays the score, populate e.g. via inspector
private int score;
public void IncrementScore()
{
score++;
scoreText.text = score.ToString();
}
}
The collision script as bullet version:
public class Bullet : MonoBehaviour
{
private ScoreManager scoreManager;
private void Start()
{
scoreManager = GameObject.FindWithTag("GameManager").GetComponent<ScoreManager>(); // give the score manager empty gameobject that tag
}
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Target") == true)
{
// update score
scoreManager.IncrementScore();
// handle target, in this example it's just destroyed
Destroy(other.gameObject);
}
}
}

Unity: Enemy Spawn / Health System

I'm working at an enemy spawn system. This is my code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class EnemyManager : MonoBehaviour
{
public GameObject shark; // prefab von Shark
public GameObject instanceShark; // globale Instanzvariable von Shark
public Transform[] spawnPoints; // An array of the spawn points this enemy can spawn from.
public float spawnTime = 3f; // How long between each spawn.
public int maximumSharks = 2;
private int currentSharks;
public int healthShark; // current Health von Shark
public int startinghealthShark = 200;
public float sinkSpeed = 2.5f;
bool isDead;
void Start ()
{
healthShark = startinghealthShark;
currentSharks = 0;
}
void Update ()
{
if (currentSharks <= maximumSharks) {
InvokeRepeating ("Spawn", spawnTime, spawnTime);
}
Debug.Log (currentSharks);
}
void Spawn ()
{
// Find a random index between zero and one less than the number of spawn points.
int spawnPointIndex = Random.Range (0, spawnPoints.Length);
// Create an instance of the enemy prefab at the randomly selected spawn point's position and rotation.
instanceShark = Instantiate (shark, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation) as GameObject;
currentSharks++;
if (currentSharks >= maximumSharks) {
CancelInvoke("Spawn");
}
}
public void AddDamageToShark (int neuDamageWert) // Addiere zu damage. Public function, können andre scripts auch aufrufen
{
// If the enemy is dead...
if(isDead)
// ... no need to take damage so exit the function.
return;
healthShark -= neuDamageWert;
if (healthShark <= 0) { //tot
Death ();
}
Debug.Log (healthShark);
}
void Death ()
{
// The enemy is dead.
isDead = true;
currentSharks--;
Destroy (instanceShark);
Debug.Log ("dead?");
return;
}
What I want: Spawn enemies as long as maximum amount isn't reached (this part works so far) and destroy the enemy that has been shot and respawn another one (doesn't work).
This codes creates 2 sharks as enemies at the moment. The problem is when I damage one, only the last created instance gets destroyed even though I shot at the first shark. Also the health of the other instance and new spawning instance isn't affected at all.
I'd appreciate any advices, I spent ages with this code and it seems like I need some help regarding the instances - health logic.
Thank you very much
Split your spawn manager behaviour and your enemy behaviour and use interfaces to organize your code in a more SOLID approach. Turn your objects responsible for just one scope (Now your SpawnManager is current responsible for Enemy behaviours/responsabilities)
SpawnManager should be a singleton object with just one responsibility "to manage enemy spawn" with a maximunEnemyCount and a currentEnemyCount. You can spawn always when your currentEnemyCount < maximunEnemyCount.
OnTakeDamage() and OnDeath() should be interfaces of your enemy behaviour (in a separated script from SpawnManager, lets assume EnemyBehaviour script) and your destroy should target just the instance of itself Destroy(this.gameObject)
Remember to notify your SpawnManager when an enemy is dead on your OnDeath method to adjust enemyCurrentCount.
I think this is a more elegant way to do this task and less susceptible to bugs on managing enemy instances.
edited:
linked singleton references that I forgot before
you'll need a unique gameObject for manager that should know how much enemies can live and how many are living, nothing more about the enemies (enemy's health is a responsibility of enemy object in this case).
each enemy need to know when he/she dies and so notify the manager to decrement its counter.

Categories