Inheritance in Unity issue - c#

I am trying to get all my abilities to follow instructions from a superclass to decrease the amount of repetitive code. I tried doing this by passing in Monobehavior as a parameter in constructor. This would work perfectly, except I get a warning saying I simply can't do this. Here is my super class.
public class Ability : MonoBehaviour {
private SpriteRenderer renderer;
private MonoBehaviour ability;
public Ability(MonoBehaviour b) {
ability = b;
renderer = ability.GetComponent<SpriteRenderer>();
}
public void Start () {
}
void Update () {
}
public void checkAvailability()
{
if (ability.GetComponentInParent<SpeedBall>().getAvail())
{
renderer.enabled = true;
}
else
renderer.enabled = false;
}
public void updateRenderer()
{
renderer.enabled = true;
renderer.transform.position = ability.GetComponentInParent<BoxCollider>().transform.position;
renderer.transform.localScale = new Vector3(.2f, .2f, 0);
}
and here is one of the child classes, which would work perfectly.
public class Sprite : MonoBehaviour {
private Ability ability;
void Start () {
ability = new Ability(this);
}
// Update is called once per frame
void Update () {
ability.updateRenderer();
ability.checkAvailability();
}
}
This is untraditional, but it should work. Is there anyway to accomplish this same thing without passing in Monobehavior. I can't extend multiple classes, and I need it to extend MonoBehavior. Thanks for any help!

You can access base class inherit members into derived class. For clarification suppose you have ExtendMonobhevior class (Your own version of Monobehaviour with additional functionalities).
class MonoBehaviourExtended : MonoBehaviour {
//your extended featuer of MonoBehaviour goes here
}
Now you can drive your normal classes(which you want to attach with gameobjects) from MonoBehaviourExtended(your custom extended version of MonoBehaviour ) it also contains MonoBehaviour
//inherit with extended monobehviour also contains extended features
public class Player : MonoBehaviourExtended {
//your normal class functinality
}
//inherit with extended monobehviour also contains extended features
public class Enemy : MonoBehaviourExtended
{
//your normal class functinality
}
And you get full access to the MonoBehaviour also.

Related

Use variable from another script or define it?

I have a Manager class and it has lots of variables so I can reference it when I need it.
Will this reduce the performance ? or should I define it
public class Controller: MonoBehaviour
{
public GameObject Manager;
}
public class GetController : MonoBehaviour
{
public GameObject Controller ;
void dosomthing(){
var Ma = Controller.Getcomponent<Controller>().Managet
}
}
TLDR; Getcomponent() calls are expensive. It is OK if used once/rarely, but starts to have an effect when called continiously.
Store a reference (define) to it:
public class GetController : MonoBehaviour
{
// Will show up in unity's inspector.
// Drag-and-Drop the gameObject with the 'Controller' component attached to it.
[SerializeField]
private Controller yourController ;
void Dosomthing(){
var Ma = yourController.Manager
}
}
Also, most things in Unity can be exposed to the Inspector as long as it is serializable.
You can expose a Controller field to the inspector, and drag-drop the same GameObject.This lets you skip a GetComponent call and another define.
I suggest using a singleton pattern for managers.
public class GameManager : MonoBehaviour
{
public static GameManager singleton; // define variable as singleton
public int score = 2; // for example...
private void Start() => singleton = this; // setup single user class instance
}
And in the player class or any other class, call it without redefining it.
public class Player : MonoBehaviour
{
public void AddScore() => GameManager.singleton.score += 1; // add score to manager
}

When I'm trying to set a value for a base class from a child class (inheritance) it does not work (Unity C#)

I'm creating an enemy (from EnemyCreator1 class) with both EnemyMove1 and MarkusEnemy scripts (EnemyMove1 is a parent class to MarkusEnemy class). In EnemyCreator1 class I set value mainState of the script EnemyMove1 to "CHASE", but when I'm trying to access it from that class it says that mainState is "IDLE" (Please read my coments below because there are more explanations about what am I trying to achieve)
public class EnemyMove1 : MonoBehaviour
{
public enum mainStates { IDLE, CHASE }
public mainStates mainState;
void Update()
{
Debug.Log(mainState); //mainstate == IDLE, but should be CHASE
}
}
public class EnemyCreator1 : MonoBehaviour
{
[SerializeField] private GameObject enemyPrefab;
public void CreateEnemyAndSetItsStateToChase()
{
GameObject enemy = Instantiate(enemyPrefab);
enemy.GetComponent<EnemyMove1>().mainState = EnemyMove1.mainStates.CHASE;
}
}
public class MarkusEnemy : EnemyMove1
{
void Update()
{
EnemyMove enemyMoveScript = GetComponent<EnemyMove>();
Debug.Log(enemyMoveScript.mainState); //mainstate == CHASE
}
}
From the above code it looks like you are inheriting from a different base class EnemyMove, not EnemyMove1.
Thank you guys for helping me, after searching for the information about base classes I decided that it is impossible to access directly it's variables from another objects' scripts so I just simply call methods with variables as arguments (I put variables in round brackets of the method)

Access public method from another namespace in Unity

I am trying to access a public method from the ARCoreBackgroundRenderer class which is a MonoBehavior and is located in GoogleARCore namespace, in another class which is also MonoBehavior and named UIController and is located in another namespace called CompanyController. Is this possible? How can I do it?
the method I am trying to call in my second class is:
public void DisableARBackgroundRendering()
{
if (m_CommandBuffer == null || m_Camera == null)
{
return;
}
m_Camera.clearFlags = m_CameraClearFlags;
.......
}
I want to call this method in my second Class in a simple method.
In general it helps to refer to the according API
ARCoreBackgroundRenderer (API Reference) as you said yourself is a class in the namespace GoogleARCore .. so import it via
using GoogleARCore;
or use
GoogleARCore.ARCoreBackgroundRenderer
as type in your code in UIController.
using UnityEngine;
using GoogleARCore;
namespace CompanyController
{
public class UIController : MonoBehaviour
{
// Reference this via the Inspector by drag and drop
// [SerializeField] simply allows to serialize also private fields in Unity
[SerializeField] private ARCoreBackgroundRenderer arRenderer;
// Alternatively you could as said use the type like
//[SerializeField] private GoogleARCore.ARCoreBackgroundRenderer arRenderer;
private void Awake ()
{
// As a fallback find it on the scene
if(!arRenderer) arRenderer = FindObjectOfType<ARCoreBackgroundRenderer>();
}
public void DisableARBackgroundRendering()
{
// Now use any public method of the arRenderer
arRenderer.SomePublicMethod();
}
}
}
However, you can also see that the method DisableARBackgroundRendering is private and you won't be able to use it. Also m_Camera and m_CommandBuffer are both private so you won't be able to access them.
What you can and - if you look closer into the implementation of ARBackgroundRenderer - want to do here is simply enabling and disabling the according component:
public void EnableARBackgroundRendering(bool enable)
{
arRenderer.enabled = enable;
}
since internally it will take care of the rest itself and you don't need any further access to its methods, fields and properties:
private void OnEnable()
{
if (BackgroundMaterial == null)
{
Debug.LogError("ArCameraBackground:: No material assigned.");
return;
}
LifecycleManager.Instance.OnSessionSetEnabled += _OnSessionSetEnabled;
m_Camera = GetComponent<Camera>();
m_TransitionImageTexture = Resources.Load<Texture2D>("ViewInARIcon");
BackgroundMaterial.SetTexture("_TransitionIconTex", m_TransitionImageTexture);
EnableARBackgroundRendering(); // AS YOU SEE IT ALREADY CALLS THIS ANYWAY
}
private void OnDisable()
{
LifecycleManager.Instance.OnSessionSetEnabled -= _OnSessionSetEnabled;
m_TransitionState = BackgroundTransitionState.BlackScreen;
m_CurrentStateElapsed = 0.0f;
m_Camera.ResetProjectionMatrix();
DisableARBackgroundRendering(); // AS YOU SEE IT ALREADY CALLS THIS ANYWAY
}

C# shared property & field inheritance

I've been trying to perfectly structure this project I'm working on in different classes while maximizing the benefits of inheritance. So far however, it's given me more headaches than benefits.
Consider this:
public class SuperClass : MonoBehaviour
{
[SerializeField] protected Camera _camera;
}
and this
public class SubClass : SuperClass
{
}
Both scripts are attached to different game objects in the scene.
The Camera is to be assigned by dragging it in the inspector
I tried this, and unity seemed to tell me that I had to assign the camera to the SuperClass game object AND to the subclass game object, which makes no sense to me.
How can I assign a camera to SuperClass.cs, which is then used and shared by all of its subclasses?
Thanks in advance!
shared by all of its subclasses
Shared by classes could can only be achieved by using "static" (static variable or singleton).
A workaround could be
public class SubClass :SuperClass
{
[SerializeField] Camera camera;
void Awake()
{
if(camera!=null)
{
_camera=camera;
}
}
// Start is called before the first frame update
void Start()
{
camera=_camera;
}
}
To further extend the solution, you could write a editor script or just get the camera from the code.
You need to create public static Camera property somewhere and reference it in your code, using property:
public static class StaticValues
{
public static Camera Camera {get; set;}
}
public class SuperClass : MonoBehaviour
{
[SerializeField] protected Camera _camera
{
get
{
return StaticValues.Camera;
}
set
{
StaticValues.Camera = value;
}
}
}
public class SubClass : SuperClass
{
}

how do I extend a class from another script in c# Unity

I'm trying to extend a base class on my player object.
player has damage script that looks like this
using UnityEngine;
using System.Collections;
public class Damage : MonoBehaviour
{
public int health = 100;
public virtual void ReceiveDamage(int damageAmount)
{
Debug.Log ("Original");
}
}
And then the same player has another script like this :
using UnityEngine;
using System.Collections;
public class playerDamage : Damage
{
public override void ReceiveDamage(int damageAmount)
{
Debug.Log ("Extended");
}
}
But when I call the script from a 3rd scrip on another object like this:
var damageScript = collision.gameObject.GetComponent<Damage>();
if( damageScript)
{
damageScript.ReceiveDamage(damageAmount);
}
the only response to the log is "Original"
Shouldn't the child be called and "Extended" written to the log?
There are several ways to do this. The easiest one is to SendMessage.
collision.gameObject.SendMessage("ReceiveDamage", damageAmount);
Whatever implementation of ReceivedDamage that the collision GameObject has, that will be the one that is called. This is awesome because you don't need to specify the type yourself nor use GetComponent.
Important Extra Information
In any implementation that you choose the key step is to make sure that the right script is attached to the collision.gameObject. If you attach both scripts then you are playing with fire.
To avoid playing with fire please make Damage an abstract class.
public abstract class Damage : MonoBehaviour
{
public int health = 100;
public virtual void ReceiveDamage(int damageAmount)
{
Debug.Log ("Original");
}
}
Abstract will give you the same functionality you want, except that Unity3d won't let you attach Damage to the GameObjects, which is good to avoid mistakes. You will always have the option to have the original ReceiveDamage and the choice to override it on future classes that inherit from Damage, like this:
public class Example : Damage
{
// This one still has ReceiveDamage but it happens in the base class.
}
or
public class PlayerDamage : Damage
{
public override void ReceiveDamage(int damageAmount)
{
Debug.Log("Extended");
}
}
I think when you call this line:
var damageScript = collision.gameObject.GetComponent<Damage>();
It gets the component by name, which in this case would be the Damage script, not the playerDamage script.
In you script it should be:
var damageScript = collision.gameObject.GetComponent<playerDamage>();
if( damageScript)
{
damageScript.ReceiveDamage(damageAmount);
}

Categories