Enabling/Disabling Script in C# failing to work - c#

So I've been developing a 2D platformer game in C# and Unity and as a part of the game I have been developing powerups. One of these in invincibility, so when the player collides with the game object they cannot be killed for a period of time. I am relatively new to Unity and C# and read that I can use '.enabled' to enable/disable an external script that is attached to the same object. However, when I activate the powerup the object is destroyed but if I collide with an enemy or object I still die. Can anyone see why this is happening.
Below is the script that I have developed.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InvincibilityPowerup : MonoBehaviour
{
public int Duration = 15;
void OnTriggerEnter2D(Collider2D coll)
{
if (coll.gameObject.tag == "Shield")
{
Destroy(GameObject.Find("Invincibility"));
StartCoroutine("Invincible");
}
}
IEnumerator Invincible()
{
Collision pIn = gameObject.GetComponent<Collision>();
pIn.enabled = false;
yield return new WaitForSeconds(Duration);
pIn.enabled = true;
}
}

1) GameObject.Find is completely unnecessary here. You already know which object is the invincibility powerup: its the one this script is attached to
2) Collision pIn = gameObject.GetComponent<Collision>(); both a) doesn't do what you want it to (you want to get the OTHER game object!) b) doesn't work anyway (Collision is not a component, Collider is)
3) you're destroying this before starting the coroutine, meaning your coroutine is being destroyed too.

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.

Make a GameObject indestructible

I am making a shooting game using Raycasts in Unity. I have a gameObject which is a cube (The ground for my game). That cube can be destroyed by using Destroy() method. How do I make it indestructible so that it doesn't get destroyed even after using the Destroy() method?
This my FPS gun script so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gun : MonoBehaviour
{
public float damage = 20f;
public float range = 150f;
//public GameObject ground;
public Camera fpsCamera;
void Start()
{
//DontDestroyOnLoad(ground);
}
void Update()
{
if (Input.GetButtonDown("Fire1")) {
Shoot();
}
}
void Shoot()
{
RaycastHit hitObject;
if (Physics.Raycast(fpsCamera.transform.position, fpsCamera.transform.forward, out hitObject, range) && hitObject.transform.name != "Ground") {
Destroy(hitObject.transform.gameObject);
}
}
It sounds like you want to sort which objects are destroyed somehow. The most simple way is of course to not call the Destroy method on that object in the first place.
However since you explain in the comments you wish to shoot and destroy some objects but not all. I would in your case make a script which either checks the tag, layer or calls a method in a script on each object.
For example, you try something similar to this:
ObjectProperties : MonoBehaviour{
public boolean CanBeDestroyed = true;
}
Add that script to the objects you shoot at.
And then when your raycast hits an object, use GetComponent on the object shot to try get ObjectProperties from that object. If the object hit has a ObjectProperties that isn't null, and that ObjectProperties has CanBeDestroyed set to be true. Then you may destroy the object.
The most simple way of achieving this, is using tags. Create a tag by highlighting the object. In the Inspector open the tags tab. Then create a tag and assign it to your object. The last thing you need to do is to check in code which tag the object you hit has.
if (Physics.Raycast(fpsCamera.transform.position, fpsCamera.transform.forward, out hitObject, range) && hitObject.transform.name != "Ground") {
switch (hitObject.tag)
{
case "immortal": //Nothing happens
break;
case "veryweak": //Objects with this tag get destroyed
Destroy(hitObject.transform.gameObject);
break;
default: //Objects that do not have a tag and do not match any of the above get destroyed
Destroy(hitObject.transform.gameObject);
break;
}
}
Hope that helps.

How to fix "Can't Play a Disabled Audio Source"?

I am trying to add a sound into my game that whenever the player moves over a certain space it plays a crunch sound. I have created the AudioSource file and a .OGG file for the sound.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpaceBlue : MonoBehaviour
{
public Transform spaceNext;
public AudioSource stepOnObject;
public AudioClip stepOnSound;
private void Start()
{
stepOnObject.clip = stepOnSound;
stepOnObject.enabled = true;
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == "Player")
{
stepOnObject.Play();
if (BoardScript.diceValue > 0)
{
BoardScript.diceValue -= 1;
Debug.Log("The dice are now " +BoardScript.diceValue);
other.transform.LookAt(spaceNext);
}
}
}
}
I have included the source and clip to my game object and i have tried it both with and without "Play on wake" selected.
Whenever the play walks over the player walks over the object i get a warning in the unity engine saying that the source is disabled.
Any help is appreciated :)
I had a similar problem and I fixed it by changing the location of the Play() call to after calling Destroy(GameObject). I would recommend trying moving the call to Play() to the end of the function, or trying Invoke("stepOnObject.Play", 0.5f); to ensure it gets called.
Otherwise, make sure its checkbox is ticked, and that the AudioSource actually has a AudioClip attached.
If you have any piece of code in some other script that Destroys this game object or makes SetActive false, then the best way to solve this problem will be to delay that piece of code by some time using a Coroutine.

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);
}
}
}

NullReferenceException & using methods without direct reference

using UnityEngine;
using System.Collections;
public class Gamemanager : MonoBehaviour {
public GameObject AttackButton; //reference to attack button game object
public GameObject RocketGO; // reference to rocket game object
public GameObject EnemySpawner;//reference to meteorite spawner gameobject
public GameObject navbuttons;
public GameObject Power1SpawnerGO; // reference to power1 spawner
public GameObject Enemy1bluespawner;
// Use this for initialization
public enum GamemanagerState
{
Opening ,
Gameplay ,
Gameover,
}
GamemanagerState GMState;
void Start ()
{
GMState = GamemanagerState.Opening;
}
void UpdateGameManagerState()
{
switch(GMState)
{
case GamemanagerState.Opening:
//set attack button to true
AttackButton.SetActive(true);
//hide game over
//set navigational buttons to false
navbuttons.SetActive(false);
break;
case GamemanagerState.Gameplay:
//set attack button false
AttackButton.SetActive(false);
//set navbuttons to true
navbuttons.SetActive(true);
//set playership to active
RocketGO.GetComponent<Rocketdamage>().Init();
//Start EnemySpawner
EnemySpawner.GetComponent<meteoritespawner>().startEnemySpawner();
Enemy1bluespawner.GetComponent<Enemy1Spawner>().startEnemy1blueSpawner(); // Enemy blue spawner
//Active Rocket bullete fire
RocketGO.GetComponent<Rocketshooting>().startBulleteFire();
//Activate Enemyblue1 bulletefire
Enemy1bluespawner.GetComponent<Enemyblue1shooting>().startEBulleteFire();
//Active power1 spawner
Power1SpawnerGO.GetComponent<PowerSpawner>().startPower1Spawner();
break;
case GamemanagerState.Gameover:
//stop enemy spawner
EnemySpawner.GetComponent<meteoritespawner>().StopEnemySpawning();
Enemy1bluespawner.GetComponent<Enemy1Spawner>().StopEnemy1blueSpawning();
//DeActive Rocket bullete fire
RocketGO.GetComponent<Rocketshooting>().StopBulleteFire();
//Stop power1 spawning
Power1SpawnerGO.GetComponent<PowerSpawner>().StopPower1Spawning();
//Deactive Enemyblue1 Bullete fire
Enemy1bluespawner.GetComponent<Enemyblue1shooting>().StopEBulleteFire();
//display gameover
//set game state to opening state after 8 sec
Invoke("changeToOpeningState" , 8f);
break;
}
}
public void setGameManagerState(GamemanagerState state)
{
GMState = state;
UpdateGameManagerState ();
}
//call this function when player call attack button
public void Startpaly()
{
GMState = GamemanagerState.Gameplay;
UpdateGameManagerState ();
}
public void changeToOpeningState()
{
setGameManagerState (GamemanagerState.Opening);
}
}
This is my script.......and everything is running fine! But the line number 67 and 89 i.e.
//line no 67
Enemy1bluespawner.GetComponent<Enemyblue1shooting>().startEBulleteFire();
and:
//line no 89
Enemy1bluespawner.GetComponent<Enemyblue1shooting>().StopEBulleteFire();
under UpdateGameManagerState() function.
are giving me NullreferenceException.
After so much of thinking, I have come up with this issue that may have been occurring!
Here Enemy1bluespawner is object(with which Spawning script is attached) to spawn enemy....and Enemyprefab is attached to Enemy1bluespawner...but however, I am able to call all the scripts/methods of Enemy PREFAB(Without direct reference of Enemy prefab - I don't know why?).
Everything working fine but line number 67 and 89 is giving me NullreferenceException.
Scenario - I have empty object - Enemy1bluespawner with which Enemyspawning script is attached - which spawn the enemy at particular interval of time.........
So, when I am calling this function :
Enemy1bluespawner.GetComponent<Enemyblue1shooting>().startEBulleteFire();
To start bullete fire of the enemy but now enemy is not on the screen it will come after 5 seconds
The enemy is not on the screen it comes after 5 seconds - so may be that's why Unity is throwing NullreferenceException(at runtime)
Questions:
How am I able to access Enemy prefab scripts/methods without direct reference thru Enemy1bluespawn Object?
Solution to remove NullreferenceException. When enemy is not on screen and I am calling methoda related to it.
I hope you guys are understanding it. Any little help will be great. Thank you! :) :)
You reference the Enemy1Spawner script, but never the Enemyblue1shooting. For what I'm understanding, your goal is to spawn enemies with the Enemy1Spawner script, and those spawned enemies are the objects that have the Enemyblue1shooting component (that will actually shoot).
If I'm right, you'll have to get the GameObject of the spawned enemy and then use the GetComponent method. What seems to be happening is a NullPointerException because the spawner doesn't have a Enemyblue1shooting component; the object that has it is it's child.
Try making a method in the Enemy1bluespawner script that returns the enemy at hand and, only then, using GetComponent<Enemyblue1shooting>() to get the right component. That'll do the trick.
Example:
public GameObject GetEnemy(int n)
{
return enemies.get(n); // Gets the n enemy in your array
}
...
Enemy1bluespawner.GetComponent<Enemy1Spawner>().startEnemy1blueSpawner(); // Enemy blue spawner
GameObject enemy = Enemy1bluespawner.GetComponent<Enemy1Spawner>().GetEnemy(0);
enemy.GetComponent<Enemyblue1shooting>().startEBulleteFire();
Don't forget to do some null validation. The perfect scenario would be returning the script itself, not a GameObject, but this way it may be a bit easier to see.
That might be also your question about calling prefab's methods. You don't call a prefab's method; you call the method of the instantiated prefab (the spawned enemy).
easiest but not the best is
if(Enemy1bluespawner.GetComponent<Enemyblue1shooting>()!=null)
Enemy1bluespawner.GetComponent<Enemyblue1shooting>().startEBulleteFire();

Categories