Inheriting variables
Hi there,
I have a base class called Turret.cs and several child classes with specific requirements. All I want to do is, in the Update() of each child script, read a public bool from the parent class it inherits from. The child script recognises the variable of its parent (hovering over the reference in the child script shows public bool facing, no red, squiggly lines either) but at runtime it doesn't seem to actually read the variable.
Here is the base class:
using UnityEngine;
using System.Collections;
public class Turret : MonoBehaviour
{
public float rateOfFire;
public int damage;
public float rateOfTurn;
public int health;
public bool facing;
public Transform tmpLookRotation;
public Transform gunPointTransform;
public LayerMask playerLayer;
private GameObject player;
private Transform tmpRotation;
private float tmpLerp;
// Use this for initialization
void Start ()
{
player = GameObject.FindGameObjectWithTag("Player");
}
// Update is called once per frame
void Update ()
{
//if lincecast to player, start turning at rateOfTurn
Debug.DrawRay(gunPointTransform.position, transform.forward * 10000.0f);
Ray ray = new Ray(gunPointTransform.position, transform.forward);
RaycastHit hit;
RaycastHit linecastHit;
tmpRotation = transform;
tmpLookRotation.LookAt(player.transform.position);
if(Physics.Linecast(transform.position, player.transform.position, out linecastHit) && linecastHit.transform == player.transform)
{
transform.rotation = Quaternion.Lerp(transform.rotation, tmpLookRotation.rotation, rateOfTurn);
}
//is turret facing player?
if(Physics.Raycast(ray, out hit, Mathf.Infinity, playerLayer))
{
facing = true;
}
}
}
And here's the child class:
using UnityEngine;
using System.Collections;
public class TurretMiniGun : Turret
{
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if(facing)
{
print("Facing");
}
}
So the TurretMiniGun class inherits from Turret and the "facing" bool in the base class is public. I've looked at the Unity tutorials about inheritance and this should work but I'm obviously missing something. One thing that's not made clear in the tutorial is whether the base class needs to be attached to the object. I've tried having the base class attached and merely in the project hierarchy but neither work and if I don't attach the base script the turret doesn't turn to track the player at all.
FYI There are many clones of each type of turret, instantiated by a LevelMaster class, ie many mini-gun turrets in the scene at once.
Any help would be great.
Quick clarification; the Turret script handles movement and common variables to all the types of turrets (mini-gun, grenade, laser etc) but each child handles specific behaviour for each turret. Line render for laser, simply deducting health of the player with mini-gun and lobbing a projectile for the grenade turret.
Dan
EDIT: This is not the problem.
The "facing" field is correctly accessible from the superclass, otherwise you would have a compile time error.
I'd start debugging from this line:
if(Physics.Raycast(ray, out hit, $$anonymous$$athf.Infinity, playerLayer))
Does this actually ever happen?
Totally. I figured if it didn't recognise the variable it would throw a compile error but it doesn't. =(
I've done announcement debugs on all the lines involved and it seems as though, while the child script recognises the 'facing' variable from its parent, it does not receive the data to say it's true.
I've posted an answer to this question, but I think it's caught in the moderation queue or something. I think the problem is this:
Turret$$anonymous$$iniGun.Update() overrides Turret.Update(), so nothing in the superclass gets executed.
Try adding base.Update() to the beginning of Turret$$anonymous$$iniGun.Update().
Oh, FYI, the children usually execute before parents, that's why nothing is happening.
That would explain why Turret$$anonymous$$iniGun is always false, but how do we take advantage of OOP in that case?
Answer by Bunny83 · Jan 17, 2016 at 02:24 PM
Yeah, just as we thought ^^. In the screenshot you seem to have both classes attached to the same object. I have a feeling you haven't grasp what inheritance is all about. Inheritance is not some kind of "relation" between objects but between classes. Your TurretMiniGun class is also a Turret when you inherit from the Turret class. When you inherit a class from another, the new class literally inherits all methods and variables.
If you create an instance of your TurretMiniGun class that instance is also a Turret. It makes no sense to to attach both script to your object if you have a class inheritance in place.
You have two ways you could go:
stick with in heritance and remove the Turret script from your object.
remove the inerhitance and keep both objects.
first approach
If you want to stick with inheritance there are a few things you need to change. First of all your Update and Start method should be declared as "virtual". Second the deriving class has to "override" those methods properly. If you also want to keep the original code in Update / Start running you have to call the base methods inside the derived class.
// Turret.cs
public class Turret : MonoBehaviour
{
// ...
protected virtual void Start()
{
// ...
}
protected virtual void Update()
{
// ...
}
}
// TurretMiniGun.cs
public class TurretMiniGun : Turret
{
// ...
protected override void Start()
{
base.Start();
// ...
}
protected override void Update()
{
base.Update();
// ...
}
}
Now when you just attach your TurretMiniGun class you get the functionality of both classes.
Second approach
If you want to go a more component based approach, which Unity was actually made for, you might want to keep your two scripts seperated. Each script implements it's own behaviour. If one script need something from the other, it has to use GetComponent to access the second instance:
// Turret.cs
public class Turret : MonoBehaviour
{
// ...
void Start()
{
// ...
}
void Update()
{
// ...
}
}
// TurretMiniGun.cs
public class TurretMiniGun : MonoBehaviour
{
Turret turret;
// ...
void Start()
{
turret = GetCompoent<Turret>();
// ...
}
void Update()
{
if(turret.facing)
// ...
}
}
This would be the component approach without inheritance.
Oh, I've lost the count of how many times I've forgotten you have to explicitly declare virtual methods in C#.
I knew I had to be doing something wrong when I had both scripts on the object, but it was the only way I could get the turret to rotate/track the player. So it's the finer points of inheritance I'm missing. The official Unity tutorial could do with a few extra $$anonymous$$utes explaining this stuff.
Probably a really dumb question: if the child class Update() overrides the base class Update(), then how will the turret lerp/rotate?
This worked! =) I had a brief problem because I was not setting the facing bool to false when the player was out of sight. There was much swearing at my monitor when I found out what I'd missed.
@alishka, @Bunny83 and @Internet Friend - can you recommend any tutorial sites that cover intermediate C# topics like this? I like to learn things first hand as much as possible before bothering people on UA. I don't want to be THAT guy: "I'm making a game, write my code for me!" ;-)
@Internet Friend - I want to upvote your answer as well but I can't find it. =(
There are intermediate tutorials on scripting in C# on the Unity website, they have been there at least since 2013. $$anonymous$$ore specifically, this is the one you are looking for: https://unity3d.com/learn/tutorials/topics/scripting/overriding?playlist=17117
Answer by alishka · Jan 17, 2016 at 12:10 PM
Yes, this is valid inheritance. The reason why print("Facing");
is not printing anything is because your if(facing)
condition is never met.
You are declaring facing
in line #10, but aren't setting it to a value, so it is false by default. And if the turret is not facing your player, the facing
boolean stays false, and hence nothing prints in Minigun's update()
.
I'm assuming you have two objects: A parent object with the turret script attached, and a child object with the minigun script attached. Is that correct?
Try printing health
.
Cheers!
Above is how the scripts are attached. They are both on the root level of the GameObject. This pic is at runtime; the facing variable switches to true on the base class but not the child.
d
I can print the health variable that's on the Turret base class from the Update function of the child class.
void Update ()
{
if(facing)
{
print("Facing");
}
print(health);
}
Answer by alishka · Jan 17, 2016 at 02:30 PM
Moved from comment to answer
In this case, to benefit from OOP, you'd want to change Turret from being a Monobehaviour to a standard method that holds all the functionalities of Turret. Then do the same for Minigun. In the end, you would have to introduce a third class which is a monobehaviour which would control the methods of the turret and minigun in update and start
.
So, let's consolidate. You'd have to declare a public class TurretA
which holds parameters such as health
, etc. and methods such as findEnemy(), takeDamage()
, etc.
You'd also want to create a public class MinigunA
which holds parameters such as ammoType, fireSpeed, etc.
, and methods such as fire(), cooldown(), etc.
Then, create a third public class TurretController : MonoBehaviour
which controls your turret by calling Minigun
's and Turret
's methods in its update()
. TurretController
itself could hold many useful functionalities for example parameters such as turretLevel
, etc., and methods such as `upgradeTurret()', etc. That would allow you to upgrade your turret in-game.
In your hierarchy you would have three objects: TurretParent (has TurretController.cs) > TurretBase (has TurretA.cs) > TurretMinigun (has MinigunA.cs)
This design is truly modular, and would help you build a very maintainable turret for your game.
If that helped you, I will update my initial reply, so you could mark this question as "answered".
Cheers!
Answer by SneakySquid · Jan 17, 2016 at 12:00 PM
I could be wrong on this, but I believe your child class' Update method isn't being called, because it is not inheriting from MonoBehaviour.
Close. Turret$$anonymous$$iniGun.Update() overrides Turret.Update(), so nothing there gets executed.
EDIT: This was incorrect. Turret.Update() would have to be explicitly declared as virtual, and Turret$$anonymous$$iniGun.Update() as override, for the override to actually take place.
$$anonymous$$inigun is a child of Turret which is a $$anonymous$$onoBehaviour, which means that $$anonymous$$inigun is also a $$anonymous$$onoBehaviour.
The problem is that the Turret$$anonymous$$iniGun.Update() overrides Turret.Update(), so nothing in the superclass's method gets executed. Add base.Update(); to the beginning of Turret$$anonymous$$iniGun.Update(), and you should be good.
Your answer
Follow this Question
Related Questions
How would I get around multiple inheritance for this instance? 1 Answer
Protected variable is assigned in parent but null in child 1 Answer
Custom class, Null Reference Exception 4 Answers
C# syntax question Generic and overriding 0 Answers
Grab an inherited subclass script from a GameObject in C# 0 Answers