In Unity3D my enemy is not taking damage upon colliding with my projectile explosion.
Although this is not the case as it the health variable is unaffected upon colliding with my projectile explosion.
My Enemy and Barrel classes inherit from Entity which handles the taking of damage (subtracting the damage variable from the health variable). Although only the barrel class is working as intended.
The tags are 100% correct and I would prefer to continue using inheritance so please no suggestions to change the method in which my classes take damage.
the class that Enemy and Barrel inherit from
using UnityEngine;
using System.Collections;
public class Entity : MonoBehaviour {
public float health = 25;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public virtual void takeDamage(float dmg){
health -= dmg;
if (health <= 0){
Destroy(this.gameObject);
}
}
}
Enemy class
using UnityEngine;
using System.Collections;
public class Enemy : Entity {
private NavMeshAgent agent;
public GameObject target;
// Use this for initialization
void Start () {
agent = GetComponent<NavMeshAgent> ();
}
// Update is called once per frame
void Update () {
agent.SetDestination (target.transform.position);
}
}
Barrel class
using UnityEngine;
using System.Collections;
public class Barrel : Entity {
private Transform myTransform;
//Effects
public GameObject barrelExplosion;
public GameObject explosionDamage;
public GameObject explosionSound;
// Use this for initialization
void Start () {
myTransform = this.transform;
}
// Update is called once per frame
void Update () {
}
public override void takeDamage(float dmg){
health -= dmg;
if (health <= 0){
Instantiate(barrelExplosion, myTransform.position, myTransform.rotation);
Instantiate(explosionSound, myTransform.position, myTransform.rotation);
Instantiate(explosionDamage, myTransform.position, myTransform.rotation);
Destroy(this.gameObject);
}
}
}
ExplosionAOE the class that sends the damage
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ExplosionAOE : MonoBehaviour {
public float damage = 100.0f;
public float lifeTime = 0.05f;
private float lifeTimeDuration;
public List<GameObject> damageTargets = new List<GameObject>();
public float radius = 15.0f;
GameManager gameManager;
void Start() {
gameManager = GameObject.FindGameObjectWithTag("GameManager").GetComponent<GameManager>();
//Destroy (this.gameObject, lifeTime);
lifeTimeDuration = Time.time + lifeTime;
transform.GetComponent<SphereCollider>().radius = radius;
}
void Update() {
//Explosion finishes, damage targets and remove AOE field
if (Time.time > lifeTimeDuration) {
foreach (GameObject target in damageTargets) {
if (target != null) {
//Calculate damage based on proximity to centre of explosion
float thisDamage = ((radius - Vector3.Distance(target.transform.position, transform.position)) / radius) * damage;
print(thisDamage);
target.GetComponent<Entity>().takeDamage(thisDamage);
//target.SendMessage("takeDamage", damage); //<< This is not good code. Let's fix this!
}
}
Destroy(this.gameObject);
}
}
void OnTriggerEnter(Collider otherObject) {
if (otherObject.gameObject.tag == "Enemy") {
damageTargets.Add(otherObject.gameObject);
}
if (otherObject.gameObject.tag == "Player") {
Vector3 jumpVector = (otherObject.transform.position - transform.position).normalized;
jumpVector *= 25;
otherObject.GetComponent<CharacterMotor>().SetVelocity(jumpVector);
}
}
}
Sorry this is a bit of a lengthy one and EVERYTHING is tagged correctly so that is not the issue, thanks.
Problem 1.
Use "Debug.Log" everywhere
void OnTriggerEnter(Collider otherObject) {
Debug.Log("in trig");
Debug.Log("otherObject.gameObject.tag is " + otherObject.gameObject.tag);
if (otherObject.gameObject.tag == "Enemy") {
Debug.Log("a");
damageTargets.Add(otherObject.gameObject);
}
if (otherObject.gameObject.tag == "Player") {
Debug.Log("b");
Vector3 jumpVector = (otherObject.transform.position -
transform.position).normalized;
jumpVector *= 25;
otherObject.GetComponent<CharacterMotor>().SetVelocity(jumpVector);
}
}
In particular, in Entity and Enemy.
Questions such as this one are instantly answered by tracking with Debug.Log.
Problem 2.
It's a PITA getting the relationships between triggers, rigidbody, etc.
It's very likely that's a problem here.
http://docs.unity3d.com/Manual/CollidersOverview.html
Go down to the annoying "trigger action matrix" and work from there.
Problem 3.
As a rule, never use the "tags" feature in Unity. (They only added tags to help "hello world" tutorials.)
In practice you use layers everywhere and always:
(Layers are particularly essential in shooting games: every single category needs a layer.)
Problem 4.
The code shown is definitely looking good. Here's some example code not unlike yours for tips.
Trivial example, note the breakaway code (the returns) inside the OnTrigger, you should do that).
Also,
use extentions
everywhere and always in Unity. Quick tutorial
it's the #1 tip if you actually want to work professionally.
public class Enemy:BaseFrite
{
public tk2dSpriteAnimator animMain;
public string usualAnimName;
[System.NonSerialized] public Enemies boss;
[Header("For this particular enemy class...")]
public float typeSpeedFactor;
public int typeStrength;
public int value;
// could be changed at any time during existence of an item!
[System.NonSerialized] public FourLimits offscreen; // must be set by our boss
[System.NonSerialized] public int hitCount; // that's ATOMIC through all integers
[System.NonSerialized] public int strength; // just as atomic!
[System.NonSerialized] public float beginsOnRight;
private bool inPlay; // ie, not still in runup
void Awake()
{
boss = Gp.enemies;
}
..........
protected virtual void Prepare() // write it for this type of sprite
{
ChangeClipTo(bn);
// so, for the most basic enemy, you just do that.
// for other enemy, that will be custom (example, swap damage sprites, etc)
}
void OnTriggerEnter2D(Collider2D c)
{
// we can ONLY touch either Biff or a projectile. to wit: layerBiff, layerPeeps
GameObject cgo = c.gameObject;
if ( gameObject.layer != Grid.layerEnemies ) // if we are not enemy layer....
{
Debug.Log("SOME BIZARRE PROBLEM!!!");
return;
}
if (cgo.layer == Grid.layerBiff) // we ran in to Biff
{
Gp.billy.BiffBashed();
// if I am an enemy, I DO NOT get hurt by biff smashing in to me.
return;
}
if (cgo.layer == Grid.layerPeeps) // we ran in to a Peep
{
Projectile p = c.GetComponent<Projectile>();
if (p == null)
{
Debug.Log("WOE!!! " +cgo.name);
return;
}
int damageNow = p.damage;
Hit(damageNow);
return;
}
Debug.Log("Weirded");
}
public void _stepHit()
{
if ( transform.position.x > beginsOnRight ) return;
++hitCount;
--strength;
ChangeAnimationsBasedOnHitCountIncrease();
// derived classes write that one.
if (strength==0) // enemy done for!
{
Gp.coins.CreateCoinBunch(value, transform.position);
FinalEffect();
if ( Gp.superTest.on )
{
Gp.superTest.EnemyGottedInSuperTest(gameObject);
boss.Done(this);
return;
}
Grid.pops.GotEnemy(Gp.run.RunDistance); // basically re meters/achvmts
EnemyDestroyedTypeSpecificStatsEtc(); // basically re achvments
Gp.run.runLevel.EnemyGotted(); // basically run/level stats
boss.Done(this); // basically removes it
}
}
protected virtual void EnemyDestroyedTypeSpecificStatsEtc()
{
// you would use this in derives, to mark/etc class specifics
// most typically to alert achievements system if the enemy type needs to.
}
private void _bashSound()
{
if (Gp.biff.ExplodishWeapon)
Grid.sfx.Play("Hit_Enemy_Explosive_A", "Hit_Enemy_Explosive_B");
else
Grid.sfx.Play("Hit_Enemy_Non_Explosive_A", "Hit_Enemy_Non_Explosive_B");
}
public void Hit(int n) // note that hitCount is atomic - hence strength, too
{
for (int i=1; i<=n; ++i) _stepHit();
if (strength > 0) // biff hit the enemy, but enemy is still going.
_bashSound();
}
protected virtual void ChangeAnimationsBasedOnHitCountIncrease()
{
// you may prefer to look at either "strength" or "hitCount"
}
protected virtual void FinalEffect()
{
// so, for most derived it is this standard explosion...
Gp.explosions.MakeExplosion("explosionC", transform.position);
}
public void Update()
{
if (!holdMovement) Movement();
if (offscreen.Outside(transform))
{
if (inPlay)
{
boss.Done(this);
return;
}
}
else
{
inPlay = true;
}
}
protected virtual void Movement()
{
transform.Translate( -Time.deltaTime * mpsNow * typeSpeedFactor, 0f, 0f, Space.Self );
}
......
/*
(frite - flying sprite)
The very base for enemies, projectiles etc.
*/
using UnityEngine;
using System.Collections;
public class BaseFrite:MonoBehaviour
{
[System.NonSerialized] public float mpsNow;
// must be set by the boss (of the derive) at creation of the derive instance!
private bool _paused;
public bool Paused
{
set {
if (_paused == value) return;
_paused = value;
holdMovement = _paused==true;
if (_paused) OnGamePause();
else OnGameUnpause();
}
get { return _paused; }
}
protected bool holdMovement;
protected virtual void OnGamePause()
{
}
protected virtual void OnGameUnpause()
{
}
protected string bn;
public void SetClipName(string clipBaseName)
{
bn = clipBaseName;
}
}
Is more easy if in ExplosionAOE/OnTriggerEnter function you call the takeDamage function:
scriptCall = otherObject.GetComponent(EnemyScript);
scriptCall.takeDamage(damage);
Related
Why is this connecting all the health bars of my enemies together, even though their actual health is decreasing at its specified rate?
public class FillHealth : MonoBehaviour
{
Image HealthBar;
private NormalMonster normalMonster;
// Start is called before the first frame update
void Start()
{
HealthBar = GetComponent<Image>();
normalMonster = GameObject.FindGameObjectWithTag("Normal Monster").GetComponent<NormalMonster>();
}
// Update is called once per frame
void Update()
{
UpdateHealthLeft();
}
public void UpdateHealthLeft()
{
if (normalMonster.healthLeft > 0)
{
HealthBar.fillAmount = normalMonster.healthLeft / normalMonster.setHealth;
}
}
}
This is the script that is being referenced in FillHealth. As far as I understand it, since the variable isn't static, then the values should not be shared. It should find fill the health bar for each individual enemy.
public class NormalMonster : MonoBehaviour
{
private float _normalSpeed = 2f;
private float _BaseHealth = 20f;
private float _HealthModifier;
public float setHealth;
public float healthLeft;
// Start is called before the first frame update
void Start()
{
UpdateHealth();
}
// Update is called once per frame
void Update()
{
NormMonMov();
}
public void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Arrows")
{
healthLeft -= Arrows.Damage;
Destroy(other.gameObject);
if (healthLeft <= 0f)
{
Destroy(this.gameObject);
EarnedGold.earnedGold += 7;
Spawn_Manager.enemyCount--;
}
}
}
public void UpdateHealth()
{
if (StageMode.StageLvl > 5)
{
_HealthModifier = (StageMode.StageLvl * 0.01f) * _BaseHealth;
setHealth = Mathf.Round(_BaseHealth + _HealthModifier);
}
else
{
setHealth = _BaseHealth;
}
healthLeft = setHealth;
}
public void NormMonMov()
{
transform.Translate(Vector3.left * _normalSpeed * Time.deltaTime);
transform.position = new Vector3(Mathf.Clamp(transform.position.x, -7.0f, 10), transform.position.y, 0);
}
}
Any help would be greatly appreciated for this guy who just start playing with unity this weekend.
I believe the issue is with normalMonster = GameObject.FindGameObjectWithTag("Normal Monster").GetComponent<NormalMonster>();
If you have two Monsters
Both have two scripts attached, FillHealth and NormalMonster
Both the "FillHealth" scripts look for the FIRST gameobject in the scene that has a script with tag NormalMonster so both monsters are pointing to the exact same NormalMonster script (the first in the list)
Change "GameObject" capital G to "gameObject" lower case g
Still not the best way to code this, but that may work I think
Instead of getting the image, get the rect transform, like this
public RectTransform healthBar;
and change the length with:
healthBar.sizeDelta = new Vector2(normalMonster.healthLeft,healthBar.sizeDelta.y);
I am facing a problem in Unity3D. I have the same health script attached to both the player and the enemy. I want to show the game-over message when the player dies but the issue is that the game over message appears for both the player and enemy when they die.
My code looks like is that:
public class CharacterStats : MonoBehaviour
{
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
health = Mathf.Clamp (health, 0, 100);
}
public void damage(float damage)
{
health -= damage;
if(health<=0)
{
Die();
Application.LoadLevel(gameover);
}
}
void Die()
{
characterController.enabled = false;
if (scriptstodisable.Length == 0)
return;
foreach (MonoBehaviour scripts in scriptstodisable)
scripts.enabled = false;
if (ragdollmanger != null)
ragdollmanger.Raggdoll();
}
}
As you are using 1 script for both player and enemy. You should have different classes for both and implement an interface or derive from a base class to implement health:
public class Character : MonoBehaviour
{
public float Health;
public virtual void Damage(float damageValue)
{
Health -= damageValue;
}
public virtual void Die()
{
}
}
public Enemy : Character
{
public override void Die()
{
// do enemy related stuff
}
}
public Player : Character
{
public override void Die()
{
// do player related stuff.
// like game over screen
}
}
Hope this helps :)
You could use a bool to check whether CharacterStats is attached to the player, for example by adding a tag called ”Player” to the player game object and checking if gameObject.tag == “Player”, or you could equivalently name the game object ”Player” and check gameObject.name if you so wish.
You could then run the function for the game over message only if the game object is a player (isPlayer is true).
public class CharacterStats : MonoBehaviour
{
bool isPlayer = false;
// Use this for initialization
void Start ()
{
if(gameObject.tag == “Player”)
{
isPlayer = true;
}
}
// Update is called once per frame
void Update ()
{
health = Mathf.Clamp (health, 0, 100);
}
public void damage(float damage)
{
health -= damage;
if(health<=0)
{
if(isPlayer)
{
// Do Player-only stuff
}
// Do Stuff for both players and enemies
}
}
}
I am making a game like 2 cars. And I have written code to create a instantiater. It is a car game and there are 4 lanes. Let me show you a picture of my game and yeah this is solely for practise.
Player should avoid square objects and eat circle objects but sometimes 2 square objects get spawned in a same lane making impossible for player to win. I have written this script to control that but I failed. Please help me. At least have a check to my DetectSameLaneFunction(). And tell me what I am doing wrong.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour {
public GameObject[] objects;
public float delaytime = 2f; // this is separate for each prefab with which script is attaches
public float spawnrate = 1f; // this is separate for each prefab with which script is attaches
public static int lastgameobjectindex;
public static GameObject lastgameobject;
public static GameObject SecondLastGameObject;
private float loadingtime;
private GameObject go; // just a temporary variable
public static List<GameObject> spawnobjects = new List<GameObject>();
// Use this for initialization
void Start () {
loadingtime = delaytime;
}
// Update is called once per frame
void Update () {
if (Time.time > loadingtime)
{
float randomness = spawnrate * Time.deltaTime;
if ( randomness < Random.value)
{
Spawners();
}
NextLoadTime();
}
}
private void Spawners()
{
int spawnnumber = Random.Range(0, 2);
GameObject go = Instantiate(objects[spawnnumber]) as GameObject;
go.transform.position = this.transform.position;
spawnobjects.Add(go);
Debug.Log(spawnobjects[spawnobjects.Count-1]);
DetectSameLaneObjects();
/* if (spawnobjects.Count > 4)
{
spawnobjects.RemoveAt(0);
}*/
}
private void DetectSameLaneObjects()
{
if (spawnobjects.Count > 3)
{
lastgameobject = spawnobjects[spawnobjects.Count - 1];
SecondLastGameObject = spawnobjects[spawnobjects.Count - 2];
lastgameobjectindex = spawnobjects.Count - 1;
if (SecondLastGameObject.transform.position.x != lastgameobject.transform.position.x
)
{
if (Mathf.Abs(lastgameobject.transform.position.x- SecondLastGameObject.transform.position.x) < 2.3f)
{
Debug.Log("Destroy function getting called");
Destroy(spawnobjects[lastgameobjectindex]);
spawnobjects.RemoveAt(lastgameobjectindex);
}
}
}
}
void OnDrawGizmos()
{
Gizmos.DrawWireSphere(this.transform.position, 0.6f);
}
void NextLoadTime()
{
loadingtime = Time.time + delaytime;
}
}
The issue I am having is with a dropped item i pick up adding ammo to a gun.
Built a Gun class with all the methods and variables.
Built a Rifle class derived from the Gun class
The Rifle works perfect No Issues
I now am adding a "PickUp" system where x amount of enemies drop a pickup.
This is the script on the item to pick up
public class AddARAmmo : MonoBehaviour
{
private Rifle rifle;
private void Awake()
{
rifle = FindObjectOfType<Rifle>();
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == string.Format("Player"))
{
rifle.AddAmmo(30);
Destroy(gameObject);
}
}
}
The rifle and gun scripts are kind of long but here is the relevant stuff from the Gun base class is public abstract class ......
public int bulletsInStock;
public void AddAmmo(int ammoToAdd)
{
bulletsInStock += ammoToAdd;
UpdateAmmo();// updates on screen Ammo
}
......
Then in the Rifle Class
public override void Modifiers() // This is where the guns starts are stored
{
bulletSpeed = 2777f;
bulletsInStock = 200;
bulletsInMag = 30;
bulletPoolSize = 40;
desiredRPS = 15;
muzzleFlashPoolSize = 10;
}
I am getting an Object Reference Not Set To An Instance
The Rifle script is on the rifle in the game hierarchy so it should find it.
Does anyone see anything wrong?
Here is the full Gun script
public abstract class Gun : MonoBehaviour
{
[SerializeField] protected GameObject muzzleFlash;// spawns on barrelEnd
[SerializeField] protected Transform muzzleFlashFolder;
[SerializeField] protected Transform bulletFolder;// is the parent of bullets
[SerializeField] protected Transform barrelEnd;// Gameobject at the end of barrel
[SerializeField] protected Rigidbody bullet; // The bullet Prefab
[SerializeField] protected Text ammo; // OSD
[SerializeField] protected Text weaponType; // OSD
[HideInInspector] protected float bulletSpeed;
[HideInInspector] public int bulletsInStock;
[HideInInspector] protected int bulletsInMag;
[HideInInspector] protected float desiredRPS;// Rounds Per Second
[HideInInspector] protected List<Rigidbody> poolOfBullets; // Make pool for bullets
[HideInInspector] protected int bulletPoolSize; // The size off the buletpool 10 works really well
[HideInInspector] protected List<GameObject> muzzleFlashPool;// pool for muzzleflash
[HideInInspector] protected int muzzleFlashPoolSize; // size of the muzzle pool
[HideInInspector] protected int bulletsLeft; // In mag
[HideInInspector] protected bool isReloading = false;
[HideInInspector] protected float timeLeft;// for fire speed
[HideInInspector] protected float fireSpeedTimer;
[HideInInspector] protected Weapons weaponsScript;
[HideInInspector] protected PlayerController playerController;
protected void FixedUpdateStuff()
{
if (playerController.canMove && Input.GetAxisRaw(string.Format("Fire1")) > 0)
{
FireSpeedControl();// call the fire timer controller
}
if (playerController.canMove && Input.GetAxisRaw(string.Format("Fire1")) == 0)
{
timeLeft = 0f;
}
if (playerController.canMove && Input.GetKeyDown(KeyCode.R) && !isReloading)
{
Reload();
}
UpdateAmmoOnInput();
}
protected void UpdateStuff()
{
if (gameObject.activeInHierarchy)// when a gun become active it updates OSD
{
UpdateWeaponType();// With its Name
}
}
protected void RPSFinder()// finds the Rounds Per Second the gun will fire
{
fireSpeedTimer = (100 / desiredRPS) / 100;
timeLeft = fireSpeedTimer;
}
protected void Fire()// Instatiates a clone of the desired bullet and fires it at bulletSpeed
{
if (!Empty())
{
Rigidbody bulletClones = GetPooledBullet();
if (bulletClones != null)
{
bulletClones.transform.SetPositionAndRotation(barrelEnd.position, barrelEnd.rotation);
bulletClones.gameObject.SetActive(true);
}
GameObject muzzleFlashClone = GetMuzzleFlash();
if (muzzleFlashClone != null)
{
muzzleFlashClone.transform.position = barrelEnd.position;
muzzleFlashClone.gameObject.SetActive(true);
}
bulletClones.AddForce(-bulletClones.transform.up * bulletSpeed * .304f); //add the force in FPS * .304 = MPS
bulletsLeft--;// the holder to know how many bullets are left in the magazine
isReloading = false;// Gun cannot reload unless it has been fired
UpdateAmmo();// Updates the on screen ammo count and the stock usage
return;
}
}
protected void Reload()
{// this removes full magazine from the stock and the stock can still go negitive FIX FIX FIX FIX FIX FIX FIX
if (bulletsInStock > 0)
{
isReloading = true;
bulletsInStock -= bulletsInMag;
bulletsLeft = bulletsInMag;
UpdateAmmo();
}
}
protected bool Empty()// Checks the magazine to see if there are bullets in it
{
if (bulletsLeft == 0)
return true;
else
return false;
}
protected void FireSpeedControl()// controls the RPS fired by the gun Controled by Update() Input
{
if (timeLeft > 0f)
{
timeLeft -= Time.deltaTime;
}
else if (timeLeft <= 0f)
{
Fire();
timeLeft = fireSpeedTimer;
}
}
protected Rigidbody GetPooledBullet()// retrieve a preInstatiated bullet from the pool to use when shooting
{
for (int i = 0; i < poolOfBullets.Count; i++)
{
if (!poolOfBullets[i].gameObject.activeInHierarchy)
{
return poolOfBullets[i];
}
}
return null;
}
protected GameObject GetMuzzleFlash()
{
for (int i = 0; i < muzzleFlashPool.Count; i++)
{
if (!muzzleFlashPool[i].gameObject.activeInHierarchy)
{
return muzzleFlashPool[i];
}
}
return null;
}
protected void UpdateAmmo()// Update the on screen ammo information
{
ammo.text = bulletsLeft + string.Format( " : ") + bulletsInStock;
}
protected abstract void UpdateWeaponType();
protected void UpdateAmmoOnInput()
{
if (weaponsScript.updateAmmo)
{
UpdateAmmo();
weaponsScript.updateAmmo = false;
}
}
public abstract void Modifiers();
protected void StartStuff()
{
Modifiers();// Call first to store indvidual gun stats
playerController = FindObjectOfType<PlayerController>();
weaponsScript = FindObjectOfType<Weapons>();
poolOfBullets = new List<Rigidbody>();
for (int i = 0; i < bulletPoolSize; i++)
{
Rigidbody bulletClone = (Rigidbody)Instantiate(bullet);
bulletClone.gameObject.SetActive(false);// Builds the Inspector list
poolOfBullets.Add(bulletClone); //and populates the elements with clones
bulletClone.transform.parent = bulletFolder.transform;
}
muzzleFlashPool = new List<GameObject>();
for (int i = 0; i < muzzleFlashPoolSize; i++)
{
GameObject muzzleFlashClone = (GameObject)Instantiate(muzzleFlash);
muzzleFlashClone.gameObject.SetActive(false);
muzzleFlashPool.Add(muzzleFlashClone);
muzzleFlashClone.transform.parent = muzzleFlashFolder.transform;
}
bulletsLeft = bulletsInMag;
ammo.text = string.Format( " 0 : 0 ");
RPSFinder();// Run last to set the RPS of the gun
}
public void AddAmmo(int ammoToAdd)
{
bulletsInStock += ammoToAdd;
UpdateAmmo();
}
}
}
and here is the full Rifle script
public class Rifle : Gun
{
//All variables are stored in the "Gun" Script
//Copy this onto guns
void Start()
{
StartStuff();
}
private void FixedUpdate()
{
FixedUpdateStuff();
}
void Update()
{
UpdateStuff();
}
public override void Modifiers() // This is where the guns starts are stored
{
bulletSpeed = 2777f;
bulletsInStock = 200;
bulletsInMag = 30;
bulletPoolSize = 40;
desiredRPS = 15;
muzzleFlashPoolSize = 10;
}
protected override void UpdateWeaponType()
{
weaponType.text = string.Format("Assault Rifle");
}
}
There are three reasons why FindObjectOfType may return null:
Let's say the script name to find is Rifle:
And FindObjectOfType<Rifle>() is returning null.
1.The GameObject the Rifle script is attached to is in-active. You must make sure that the GameObject is active.
2.The Rifle is not attached to any GameObject at-all. Make sure that the Rifle script is attached to a GameObject..
It doesn't matter if the script is enabled or disabled, it should find it as long as the GameObject it is attached to is active. See #1.
3.When loading new scene:
When you trigger scene loading with functions such as SceneManager.LoadScene
and Application.LoadLevel, FindObjectOfType will not be able to find anything until the scene loading it done.
See here for how to check when scene has finished loading.
If you still have problems which you shouldn't, simply find the GameObject then get the Rifle component from it. Assuming that the name of the GameObject is "Rifle" too.
GameObject.Find("Rifle").GetComponent<Rifle>();
I figured out how to fix this for anyone having a similar issue.
The issue was when the enemy dropped the item, the Awake() method ran. When it ran the gun was inactive in the scene so FindObjectOfType did not find the reference script because as mentioned above, It has to be active in the scene to be found.
So what I did was create a Holder script i called EnemyDrops and this script calls the findobjectoftypes for the guns. That way the call is done on the initial game start .
I then changed the pickup to find the EnemyDrops script(which is on en empty game object) and send the call to it.
EnemyDrops Script
private Handgun handgun;
private void Awake()
{
handgun = FindObjectOfType<Handgun>();
}
public void AddHandgunAmmo(int x)
{
handgun.AddAmmo(x);
}
and the new pickup script
public class AddHandgunAmmo : MonoBehaviour
{
private EnemyDrops enemyDrops;
private void Awake()
{
enemyDrops = FindObjectOfType<EnemyDrops>();
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == string.Format("Player"))
{
enemyDrops.AddHandgunAmmo(50);
Destroy(gameObject);
}
}
}
As you can see it all still works with passing values through the methods just had to have a "middle man" to relay the information. But this works just fine
Thanks for everyones feedback and I hope this helps someone
I am new at Unity. I have successfully made a player can shoot a bullet to the enemy depends on the current wave, and I make where the enemy have it is own life depends on the current wave multiply with certain value. If the enemy life is 0, the enemy dies. But, the problem is: for example there are 2 enemies on the scene and the enemy life is 5 and the player start shooting the first enemy until the enemy life decreases to 2, when the player start shooting the second enemy, the enemy life is not 5 anymore, but it is 2 until one of the 2 cubes is dead and the enemy life reset to 5.
How can I solve this problem?
Here is the bullet script:
public class BulletManager : MonoBehaviour
{
private ScoreManager scoreManager;
private PlayerController playerController;
private EnemyManager enemyManager;
public int bulletPower;
private void Start()
{
scoreManager = GameObject.Find("Game Manager").GetComponent<ScoreManager>();
playerController = GameObject.Find("Character").GetComponent<PlayerController>();
enemyManager = GameObject.Find("Game Manager").GetComponent<EnemyManager>();
bulletPower = playerController.currentWave;
}
private void OnTriggerEnter(Collider col)
{
if (col.gameObject.tag == "Enemy")
{
enemyManager.enemyLife -= bulletPower;
if (enemyManager.enemyLife <= 0)
{
scoreManager.Points += (2 * playerController.currentWave);
enemyManager.enemyLife = playerController.currentWave * 5;
Destroy(col.gameObject);
}
}
}
}
And here is the enemy script:
public class EnemyManager : MonoBehaviour
{
private ScoreManager scoreManager;
private SoundManager soundManager;
private PlayerController playerController;
public int enemyLife;
private void Start()
{
scoreManager = GameObject.Find("Game Manager").GetComponent<ScoreManager>();
soundManager = GameObject.Find("Game Manager").GetComponent<SoundManager>();
playerController = GameObject.Find("Character").GetComponent<PlayerController>();
enemyLife = playerController.currentWave * 5;
}
private void Update()
{
if (playerController.life <= 0)
{
Invoke("Restart", 1);
}
}
private void OnTriggerEnter(Collider col)
{
if (col.gameObject.tag == "Bullet")
{
soundManager.PlaySound("Enemy Dead");
Destroy(col.gameObject);
}
else if (col.gameObject.tag == "Destroyer")
{
playerController.life--;
}
}
private void Restart()
{
scoreManager.SendToHighScore();
Application.LoadLevel(0);
}
}
The problem is that all of your enemies use the EnemyManager class to manage their health. What your code does is count the amount of hits on all enemies and then destroys the last one that was hit. My suggestion is to add an EnemyStats class to manage stats specific to each enemy (name of the enemy, the amount of damage they can do, weapon graphics, sound effects, the skys the limit). I hate to give you the answer right off the bat (or at least a basic one) since this is a good learning opportunity but here is a basic script that should do what you need and the changes you need to make to your BulletManager script.
EnemyStats.cs (Attach to the enemies game object)
public class EnemyStats : MonoBehaviour
{
public int currentHealth;
public int maxHealth;
public float increasePerWave;
public int bulletPower;
// use a negative value for healing
public void ReduceHealth(int damage)
{
currentHealth -= damage;
}
public bool IsDead()
{
return currentHealth <= 0;
}
public int GetDamage()
{
return bulletPower;
}
/// Restores enemy to their max health for the current wave. Call this after creating an spawning.
public void RestoreHealth(int wave)
{
currentHealth = (int)(maxHealth * increasePerWave * (wave + 1));
}
}
BulletManager update
private void OnTriggerEnter(Collider col)
{
if (col.gameObject.tag == "Enemy")
{
EnemyStats stats = col.GetComponent<EnemyStats>();
if (stats)
{
stats.ReduceHealth(bulletPower);
if (stats.IsDead())
{
scoreManager.Points += (2 * playerController.currentWave);
Destroy(col.gameObject);
}
}
}
}