- Home /
Incorrect calling of method between scripts?
Hi all, I've built a game that changes between different types of bullets when colliding with a powerup. There are three separate game objects, one for each type of bullet. I have one script, PlayerShooting which is active all the time and contains the function for switching between the different bullet types, see the relevant parts below:
public enum GunType
{
standard,
super1,
super2
}
public GunType type;
void Start ()
{
type = GunType.standard;
EnableGuns (type);
}
void EnableGuns(GunType type)
{
if(type == GunType.standard)
{
StandardGun.SetActive(true);
SuperGun.SetActive(false);
SlowSuperGun.SetActive(false);
}
else if(type == GunType.super1)
{
StandardGun.SetActive(false);
SuperGun.SetActive(true);
SlowSuperGun.SetActive(false);
}
else if(type == GunType.super2)
{
StandardGun.SetActive(false);
SuperGun.SetActive(false);
SlowSuperGun.SetActive(true);
}
}
So when the game Starts, the type of gun is set to standard and EnableGuns is called. My issue is when I try and change the guntype from standard to super1 a second script attached to the powerup. I've tried doing this using the following:
playerShooting.type = PlayerShooting.GunType.super1;
playerShooting.EnableGuns(type);
This is set to trigger when the player collides with the powerup. I'm pretty sure there's an issue with how I'm calling the EnableGuns feature? The second line of the above code is giving me the error "PlayerShooting does not contain a definition for EnableGuns' but surely it does? Am I missing something? Any help would be really appreciated, thanks!
As requested, code attached as an update to the question instead of as separate screenshots...
Firstly, the BulletPickup script so you can see the context in which I'm trying to access the GunType function:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace CompleteProject
{
public class BulletPickup : MonoBehaviour {
private Renderer rend;
private BoxCollider coll;
private Light light;
private GameObject StandardGun;
private GameObject SuperGun;
private GameObject player;
private GunDeactivator gunDeactivator;
public PlayerShooting playerShooting;
void Start()
{
rend = GetComponent<Renderer> ();
coll = GetComponent<BoxCollider> ();
light = GetComponent<Light> ();
playerShooting = player.GetComponent<PlayerShooting>();
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
AudioSource audio = GetComponent<AudioSource>();
audio.Play();
rend.enabled = false;
coll.enabled = false;
light.enabled = false;
playerShooting.EnableGuns(PlayerShooting.GunType.super1);
StartCoroutine (DeactivateSuper ());
}
}
IEnumerator DeactivateSuper()
{
yield return new WaitForSeconds (10);
playerShooting.EnableGuns(PlayerShooting.GunType.standard);
}
}
}
using UnityEngine;
using UnitySampleAssets.CrossPlatformInput;
namespace CompleteProject
{
public class PlayerShooting : MonoBehaviour
{
public int damagePerShot = 20;
public float timeBetweenBullets = 0.15f;
public float range = 100f;
float timer;
Ray shootRay = new Ray();
RaycastHit shootHit;
int shootableMask;
ParticleSystem gunParticles;
LineRenderer gunLine;
AudioSource gunAudio;
Light gunLight;
public Light faceLight;
float effectsDisplayTime = 0.2f;
private GameObject StandardGun;
private GameObject SuperGun;
private GameObject SlowSuperGun;
public enum GunType
{
standard,
super1,
super2
}
void Start()
{
EnableGuns (GunType.standard);
}
public void EnableGuns(GunType type)
{
if(type == GunType.standard)
{
StandardGun.SetActive(true);
SuperGun.SetActive(false);
SlowSuperGun.SetActive(false);
}
else if(type == GunType.super1)
{
StandardGun.SetActive(false);
SuperGun.SetActive(true);
SlowSuperGun.SetActive(false);
}
else if(type == GunType.super2)
{
StandardGun.SetActive(false);
SuperGun.SetActive(false);
SlowSuperGun.SetActive(true);
}
}
I think this should be all you need to see what I'm doing now!
Answer by Bunny83 · Nov 11, 2017 at 10:58 AM
Your code is lacking important information. We don't know to which class which code belongs to.
Here:
playerShooting.type = PlayerShooting.GunType.super1;
playerShooting.EnableGuns(type);
You set the type field of the playerShooting script instance to "super1". However when you call the EnableGuns method you pass a (local?) variable named "type" which we don't see anywhere. Setting the "type" field of playerShooting has no effect as you don't use this field anywhere.
Also it's bad practise to name a member field "type" and name the parameter of your EnableGuns method also "type". It should be clear that the public GunType type;
variable has nothing to do with the type parameter here: void EnableGuns(GunType type)
edit
Now that we see more of your code we can go a step further ^^.
Are you sure you actually reference the (correct / any) PlayerShooting script?
We can see you do this in Start:
playerShooting = player.GetComponent<PlayerShooting>();
However "player" is a private variable of your script which doesn't seem to be initialized anywhere. Since it's private (and not serialized) it can't be set through the inspector. So this line most likely will throw a null reference exception.
Your "BulletPickup" script seems some sort of temporary power up. Is the object with this script dynamically created at runtime? If so do you actually setup the reference to the "PlayerShooting" script on the player?
Finally your "EnableGuns" method could be simplified to
public void EnableGuns(GunType type)
{
StandardGun.SetActive(type == GunType.standard);
SuperGun.SetActive(type == GunType.super1);
SlowSuperGun.SetActive(type == GunType.super2);
}
Do you get any sort of errors in the console? Either compiler errors or runtime errors?
Updated my question as requested in your comment, hopefully that should be a bit better than screenshots! Thanks for taking the time to help :)
I hadn't noticed that I hadn't referenced player, I've corrected that now. Yes, the BulletPickup script is designed to be temporary, after 10 seconds the gun should revert back to the standard bullet type. The object is saved as a prefab and is instantiated by random chance when an enemy is killed. I might be misinterpreting your question here, if so then sorry! When the prefab is instantiated, the BulletPickup script is attached to it, would the line playerShooting = player.GetComponent<PlayerShooting>();
set the reference to the PlayerShooting script? Finally, would the suggested revision to the EnableGuns method still disable the other two GameObjects? I only want one active at a time (otherwise the player shoots all three at once!). **Sorry, missed this bit. I'm not getting any runtime errors, besides 'Object reference not set to an instance of an object'. This is referring to the line in BulletPickup where I am trying to change the gun type: playerShooting.EnableGuns(PlayerShooting.GunType.super1);
Yes, the simplified version does exactly the same as your method. type == GunType.standard
evaluates only to "true" when type is "standard". That means for all others (type == GunType.super1 and type == GunType.super2) it will result in "false". So you deactivate all objects except the one where the type matches.
Well this runtime error clearly shows that the "playerShooting" variable doesn't reference anything. You don't seem to have the player or playerShooting reference at hand when you create your power up. So it's probably the easiest way to use this inside your OnTriggerEnter:
void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
AudioSource audio = GetComponent<AudioSource>();
audio.Play();
rend.enabled = false;
coll.enabled = false;
light.enabled = false;
var playerShooting = other.GetComponent<PlayerShooting>();
if (playerShooting != null)
{
playerShooting.EnableGuns(PlayerShooting.GunType.super1);
StartCoroutine (DeactivateSuper(playerShooting));
}
else
Debug.LogWarning("Player collided with 'BulletPickup' but it doesn't have a 'PlayerShooting' component");
}
}
IEnumerator DeactivateSuper(PlayerShooting playerShooting)
{
yield return new WaitForSeconds (10);
playerShooting.EnableGuns(PlayerShooting.GunType.standard);
}
Note that the BulletPickup now doesn't need any reference to the playerShooting component as it gets the reference from the object that is entering the trigger. I also pass this reference into your Deactivate coroutine.
This approach would be the best approach as it also works with multiple players. It gives the effect only the player that enters the trigger.
Note: If you see the warning message "Player collided with 'BulletPickup' but it doesn't have a 'PlayerShooting' component"
that means your PlayerShooting component was not on the same object as the collider. It might depend on how you setup the hierarchy of your player object. One solution could be to use this ins$$anonymous$$d:
var playerShooting = other.transform.root.GetComponentInChildren<PlayerShooting>();
That's cracked it!! Thank you so much, you've saved me ages in debugging time haha!
Answer by NoseKills · Nov 11, 2017 at 11:00 AM
Your void EnableGuns(GunType type)
method is not declared public so you can't call it from outside the class (if that's what you are doing. We don't see enough code to know). Just change it to public void EnableGuns(GunType type)
and playerShooting.EnableGuns(type);
should work.
On top of that you have some things in your code that might confuse yourself.
The void EnableGuns(GunType type)
method has the "GunType type" parameter that you compare in the ifs inside the method. On top of that you have a public GunType type;
that you are not really using for anything. You can just delete that row and do this in Start
void Start ()
{
EnableGuns (GunType.standard);
}
Since EnableGuns() uses the GunType it gets as a parameter, you don't need to set playerShooting.type = PlayerShooting.GunType.super1;
before calling it. Just do
playerShooting.EnableGuns(PlayerShooting.GunType.super1);
Totally missed that it's not public ^^. I guess i haven't read the last paragraph of the question...
Though the question title is misleading. "Incorrect calling " sounds like he has a runtime problem and not a compiler error.
Thanks for the response! apologies if I didn't provide enough code, I was trying to keep my question concise. I've made the suggested changes but it still isn't changing the type of bullets.
I've updated the question above to show the two scripts, BulletPickup is the script that is attached to the powerup item. This is the script that accesses the (now public) EnablesGuns function from within PlayerShooting. PlayerShooting is fairly long so I won't give the whole lot. Thanks again, I really appreciate the help :)
Your answer
Follow this Question
Related Questions
Can't click gameobject when over another trigger? 1 Answer
OnTriggerStay2D Breaks When Adding AnimationController 1 Answer
Controller Collider Script 1 Answer
Child object's collider (on a different layer) is interfering with parent Physics... 0 Answers
Can it be detect several colliders in the same gameObject? 0 Answers