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

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

Related

Why Coroutine in Non-MonoBehaviour Script is not Working?

i want to call coroutine in a non-monobehavior script but this approach is not working.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Attacking : BaseState
{
private MovementSM _sm;
public float movementSpeed;
public MonoBehaviour mono; //here i calling monobehaviour
public bool attackBool;
public Attacking(MovementSM stateMachine) : base("Attacking", stateMachine)
{
_sm = stateMachine;
}
public override void Enter()
{
base.Enter();
_sm.animator.SetTrigger("attack");
}
public override void UpdateLogic()
{
base.UpdateLogic();
mono.StartCoroutine(DelayFunction()); //calling couroutine
}
IEnumerator DelayFunction()
{
yield return new WaitForSeconds(2);
Debug.Log("Test!");
}
}
i tried to acces the monobehaviour using parser method. but it is not clear and didnt work. how to solve this problem. what is wrong in this code.
I tried the other method shown in stackoverflow but not working.
Inheritance and MonoBehaviour aren't friends. In C# you can't inherit from two base classes. There are good reasons for that, but we have to deal with the inconvenience from time to time.
Problem with your script is that the mono variable is null and therefore you will get an error on the StartCoroutine line. Typically you would resolve that by constructing the missing class like this:
public MonoBehaviour mono = new MonoBehaviour();
Only problem with that, there is no constructor for MonoBehaviour (at least not one that is accessible). Sadly I don't have a document to quote, so take it with a grain of salt.
There is a workaround, that basically involves running the Coroutine thru your MonoBehaviour derived base class. I'm assuming that your BaseState is derived from MonoBehaviour.
public override void UpdateLogic()
{
base.UpdateLogic();
base.StartCoroutine(DelayFunction());
}

Is it possible to set a method of a class from another script in Unity C#?

I'm working on a weapon system that for my fps game. The player class contains it's own methods that might need to change when a weapon is used. The problem is I want to define the methods that will be replaced with player class' methods on the weapon class.
For example I have a shooting coroutine which uses private members of Player class, I want to change the corouitine using delegates but a coroutine defined in the Weapon class won't be able to access those members.
I know that I can define the coroutine that will come with given Weapon at the Player class and change it according to the attached item, but for clarity of the code, I want to define the shooting corouitines on the Weapon classes. Is there any approach to overcome this issue? Thanks in advance.
If I understand you correctly, something like this should work?
public interface IWeapon
{
void Shoot();
}
// -------------------------------------
public class WaterGun : IWeapon
{
void Shoot()
{
// Shoot water?
}
}
// -------------------------------------
public class LaserPistol : IWeapon
{
void Shoot()
{
// Shoot laser?
}
}
// -------------------------------------
public class Player {
IWeapon weapon;
void Start()
{
this.weapon = new WaterGun();
// later
this.weapon = new LaserPistol();
}
IEnumerator Shoot()
{
// player shoot logic here then weapon-specific logic ->
this.weapon.Shoot();
}
}
But if you simply want to keep a function in a variable, there are ways to do that too, for example:
Action shootFunction;
var waterGun = new WaterGun();
shootFunction = waterGun.Shoot; // assigning a reference to the function without executing the method
shootFunction(); // calls waterGun's Shoot() method
I think you need to detail what you have & what you want to achieve in order to get a good answer here.
It looks like your question boils down to:
I want to define the shooting corouitines(sic) on the Weapon classes
Yeah, we can do that. Let's start by looking at the bits we'd need. In this scenario, it makes sense that we use an interface:
public interface IWeapon
{
bool weaponFiring {get;}
IEnumerator StartWeaponFire ( Player player );
}
Let's look at a sample weapon:
public class WaterPistol : IWeapon
{
public bool weaponFiring { get; private set; }
public IEnumerator StartWeaponFire ( Player player )
{
weaponFiring = true;
Debug.Log ( "Squirt!" );
// Do your weapon logic/animation/cooldown here ..
yield return new WaitForSeconds ( 0.5f );
// We can acccess the 'player' data because we've sent a reference as an argument.
player.currentHealth -= 1;
Debug.Log ( "..." );
weaponFiring = false;
}
}
Now, to run the StartWeaponFire is just as easy as if the coroutine were actually on the player, but it's on the IWeapon instead.
public class Player : MonoBehaviour
{
// An example of data on this player class.
public float currentHealth { get; set; }
// A reference to the current weapon. Has the coroutine we want to start.
public IWeapon currentWeapon { get; set; }
// This can be used to manually stop a coroutine if needed.
private Coroutine _weaponCoroutine;
private void Update ( )
{
if ( Input.GetMouseButton ( 0 )
&& currentWeapon != null
&& !currentWeapon.weaponFiring )
{
_weaponCoroutine = StartCoroutine ( currentWeapon.StartWeaponFire ( this ) );
}
}
}
Notice we're starting a coroutine which has been defined on the currentWeapon, and we're sending through a reference to the Player class. The other method, the 'coroutine' in this case, can then call the public fields, properties and methods of the Player instance.
This is great way to enable an item to define a "coroutine" but allow a specified object to run that coroutine code. This scenario would allow you to have multiple 'players' be able to run the same coroutine, and you don't need to clutter your `Player' class with code for each individual weapon you might include in the game.

How can I use inheritance in Unity effectively?

I have basic understanding of inheritance but since every script I want as a component on a game object has to inherit from MonoBehaviour things get tricky.
Let's say I want to have a base class called Character and I want Player and Enemy to derive from Character. How am I suppose to add Player and Enemy script to Player and Enemy objects without getting an error?
I also tried to create empty game object and add CharacterStats to it then populate this class with different character objects with different stats. Guess what, you can't use new keyword to create objects if the script derives from Monobehaviour.
Is there some tutorials about this topic to make it more clear?
Probably the thing to understand is that MonoBehaviour itself inherits Component - A MonoBehaviour IS a Component.
When you look at it as if Components are a part of a thing, it makes at least some sense that you can't just new a Component - could I make a new Arm? If I did, where would it got? But I could make a new Body body and then dobody.AddComponent<Arm>, right? Because then it's clear what happens to the Arm - it gets attached to the body.
Similarly you can't just new a Component because they're supposed to be a part of a GameObject. What you can do is to make a new GameObject and .AddComponent<>() to that object. Again, now it's clear where that Component is going.
I generally agree with UnholySheep's comment that you should prefer composition over inheritance because this generally makes your code more modular. That said, I definitely do also use inheritance.
The question you should be asking yourself when you're outlining your class is, "Is this new class a KIND of _____ or does this new class HAVE a _____." And it's easy I guess to say a Player is a Character and an Enemy is a Character, but do you need to subclass? Or would a Player just be a Character that also has a PlayerController? Maybe you could have an interface like IPlayerController and have a LocalPlayerController for the user and an AIPlayerController for the enemies? Lots of ways to problem-solve with programming.
Or maybe all Characters have a PlayerController and just the .activeControl bit is false for non-player characters. Then you could do things like ghost/spectate NPCs, etc.
But to your question specifically:
Let's say I want to have a base class called Character and I want Player and Enemy to derive from Character. How am I suppose to add Player and Enemy script to Player and Enemy objects without getting an error?
You would start with a Character script that inherits MonoBehaviour and implements any of the logic that is common to both classes:
public class Character : MonoBehaviour
{
// Your common character logic
}
Then you would have subclasses inherit Character:
public class Player : Character
{
// Your Player-specific logic here
}
and
public class Enemy : Character
{
// Your Enemy-specific logic here
}
Then you either add the Enemy and Player scripts to a GameObject in the editor or you can do it programmatically by getting a reference to the target GameObject (by setting the reference in editor or by creating a new GameObject in a script) and then you call targetGameObject.AddComponent<YourComponentToAdd>();.
Using inheritance well comes from planning and iterating. Perhaps your Character class doesn't work well as a class for your enemies. I tend to have the player in their own class (unless its multiplayer) with enemies using their own base class. Below is an example of some inheritance I use in a 2D game I'm working on.
Base Class:
Enemy.cs
public abstract class Enemy : MonoBehaviour
{
public Vector2 flyInPos;
public Vector2 startPos;
public bool isFlyingIn;
public float flyInDuration = 5000f;
public float flyInTime = 0f;
public abstract void Destroy();
public abstract void DestroyWithoutScore();
public abstract void Attack();
public abstract void Attack2();
public abstract void Attack3();
public abstract void FlyOut();
public virtual void FlyIn()
{
isFlyingIn = true;
}
}
Every enemy needs to have these elements. You'll notice most of the methods in my base class are marked abstract so that each class that inherits can have a unique set of Attacks, and Flying patterns. One thing to notice is that the base class is inheriting from MonoBehaviour. Here is how I implemented inheritance in one of my enemies. Sorry its still a WIP.
Inherited Class:
ShootingDrone.cs
public class ShootingDrone : Enemy
{
public GameObject projectileSpawner;
public GameObject projectile;
public void Start()
{
startPos = transform.position;
Scheduler.Invoke(Attack, 5f, gameObject);
}
public void Update()
{
if(isFlyingIn)
{
transform.position = Vector3.Lerp(startPos, flyInPos, flyInTime / flyInDuration);
flyInTime += Time.deltaTime;
}
}
public override void Attack()
{
Shoot();
Scheduler.Invoke(Shoot, 0.25f, gameObject);
Scheduler.Invoke(Shoot, 0.5f, gameObject);
}
public override void Attack2()
{
Attack();
}
public override void Attack3()
{
Attack();
}
public void Shoot()
{
GameObject newProjectile = Instantiate(projectile, projectileSpawner.transform);
newProjectile.transform.parent = null;
}
public override void FlyOut()
{
throw new System.NotImplementedException();
}
public override void DestroyWithoutScore()
{
throw new System.NotImplementedException();
}
public override void Destroy()
{
Destroy(gameObject);
}
}
The base class of Enemy inherits from MonoBehaviour. So when Shooting Drone inherits from Enemy it also is inheriting from MonoBehaviour.
For your problem of trying to add a new MonoBehaviour. The reason for this is that a MonoBehavior HAS to be attached to something. Imagine a Rigidbody with no GameObject, it doesn't work. If you wanted to make a "new" CharacterStats the way you would do that is:
CharacterStats stats = gameObject.AddComponent<CharacterStats>();
If you don't want your CharacterStats as a component, then simply remove the MonoBehavior inheritance from the class and instantiate it as new CharacterStats.
There are a variety of tutorials that cover inheritance, but depending on how new you are to the subject I would start with the Unity official inheritance tutorial. I do think this tutorial is too brief but I also like CircutStreams tutorial since it also mentions implementing Interfaces which can be a better solution to inheritance in many cases.

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)

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
{
}

Categories