Polymorphism in Unity with mono-behaviour - c#

I have a problem with polymorphism in unity. I want to create a abstract base "Gun" class to represent guns in my doom-clone game. Next i want to create some children classes like "shotgun","pistol" and so on then make for example list of "Guns" when i store each children. But when i want to do this i need to derive from my base "Gun" class, and not from mono behaviour. That way i cannot store references to for example animator, or audio Source in my children scripts like Shotgun. I want the sound of shotgun to be stored inside shotgun script, so when i want to play it i just call specific object method. But without mono behaviour it seems impossible. I don't think scriptable objects solve my problem either. What should i do then?

Just have
public abstract class Gun : MonoBehaviour
{
// common members like e.g.
// either private -> only this class has access
// or protected -> only this class and inherited classes have accss
// or public -> every other type has access
[SerializeField] protected AudioSource audio;
[SerializeField] protected Animator animator;
// abstract methods all inheritors HAVE TO implement
// maybe virtual methods inheritors CAN overrule or extend but don't have to
// other common private/protected/public methods etc
public virtual void Shoot()
{
// reduce one bullet
// play your sound file etc
}
}
and then each in its own file
public class Pistol : Gun
{
}
and
public class Shutgun : Gun
{
}

Related

Unity/C# what's wrong with the way I think about this problem

I have an abstract parent class called Item
From this I inherent to Equipment and from here to Weapon and Armor. Since I want my weapons to have varying behavior, I want my logic to operate on the classes. From my Item class I also inherit to a Resource class, which should be static. My Equipment class shouldn't and should be instansiable , since an individual sword eg. Can be sharpened or damaged, to change it from its base class without changing all other swords. So I need to clone these class instances. But since cloning a class is a very non forward thing to do, I was wondering if I am doing something wrong in my architecture, and thinking about the problem in the wrong way? Do anyone have any thoughts? /Mikael
I think you should consider setting the 'stats' for items in a file (xml or such) and then create a Factory class that can give you a file with all the stats already set.
i.e.
public abstract ItemFactory{
public static Sword GetSword(){
var sword = new Sword();
//Set stats for sword from file
return sword;
}
}
Why use cloning when you can just use referencing? If I understand your problem correctly, I would utilize ScriptableObjects for defining your item objects, which makes them Assets in your project. Which then can be assigned via the inspector.
The general class layout could look something like this.
class Equipment : ScriptableObject
{
public int goldCost;
}
class Weapon : Equipment
{
public intDefaultSharpness;
}
These are your definition classes, remember they are to define the general properties of each item category. So you might have a Sword, but not a "Broadsword of fiery Dragonkiller". If you check the link about ScriptableObjects you will find code that allows you to instantiate new Assets from these definition classes. Now you can create your Broadsword and ofc many more. Now your objects just become assets like Textures and 3D models. You can easily edit them in the inspector (and probably want it to make it even nicer with custom editors)
Similar to a texture or 3D Model we don't copy it every time we use it, instead we reference it. So when your player character holds a weapon you have a sub GameObject that just keeps a reference to the original object and any additional "local" data.
class PlayerWeapon : GameObject
{
public Weapon template;
public int durability;
}
The nice thing is, the template Weapon is assignable via inspector in the editor, as it is just a normal asset. And the durability property is local to this playerWeapon and the GameObject could be used multiple times.
Of course, this can be expanded on as much as you want. I would probably add getter/setter to hide additional calculation based on buffs/debuffs. Something like
public int AttackStrength
{
get
{
return owner.strength + template.strength
}
}

Does not implement interface member StartActivityInAndroid

I am trying to open an activity in MainPage through dependency and have the following error:
Can you help me? Thanks
A C# interface is basically a contract - if your class implements an interface, you are saying that you will do all the things the interface requires.
In this case, your class is implementing INativePages which defines a method StartActivityAndroid. So your class must provide an implementation of that method signature.
If you click on the "show potential fixes" link VS will automatically create a method stub for you.
Let's take an example. Say that I am a Robot and I can shoot people all around around me. But, the issue is I don't have a gun. I also don't understand how a gun works. But I know that a gun is an object which has a trigger which when pressed kills people. So, the code for this will look something like below:-
public class Robot{
public void ShootPeopleWith( weapon IWeapon ){
weapon.PressTrigger()
}
}
interface IWeapon{
PressTrigger()
}
You see the Robot expects anyone who wants it to shoot people to provide him with a weapon first. Now, he doesn't know what weapon. Whatever you provide me, it should have a trigger because I only know to press the trigger and I know it serves my purpose. If you provide me with something that doesn't have a trigger, I cannot function.
Now, there comes a drone that instructs the Robot to shoot people. It also provides it with a gun.
public class Drone{
List<Robots> allRobotsInArea = someList
public void DelegateARobot(){
robot = select a robot from allRobotsInArea
IWeapon weapon = new MachineGun(); //procuring a machine gun
robot.ShootPeopleWith(weapon);
}
}
You can see here that IWeapon is an agreement between the Robot and Drone. It says that whatever you give me must have a trigger. So, machine gun must have a trigger. Let's implement few weapons:-
public class MachineGun : IWeapon{
public void PressTrigger(){
Fire40RoundsPerSecond();
}
...
}
public class Sniper() : IWeapon{
public void PressTrigger(){
SayQuackQauck();
}
}
The Drone can now easily pass in any weapon like Sniper and MachineGun to the Robot.
public class Pumpkin{
public void FreakPeopleOut(){
GlowInTheDark();
}
}
The drone cannot pass in a Pumpkin even though it can be used as a weapon as robot.ShootPeopleWith(new Pumpkin()); because it doesn't have a trigger and breaches the contract that Robot expects.
In your case, the Android Operating System is possibly the Robot in the above story which expects anything of type INativePages so when it consumes your NativePages object, it will try to call StartActivityInAndroid(). It doesn't know the activity or how to start it, so you must specify it in the method. Pressing Alt + Enter in Windows will auto-generate this method for you. You will have to then write logic on how to Start the Activity in Android.

How to set different classes at runtime in Unity

I am making a game in Unity C# where the character will have different characterstics and movement functions in each scene. So I am trying to have different scripts for a player in different scenes, while all of them inheriting from the same base class. The definition will be something like below
public class PlayerScript1 : PlayerBase { /* for scene 1*/
public void Move(){
/* my movement code*/
}
}
Similarly, I have a separate class for Scene2 as
public class PlayerScript2 : PlayerBase { /* for scene 2*/
public void Move(){
/* my movement code*/
}
}
Now the problem is, my other scripts, like HealthScript,ScoreScript etc they do not change with scene. But they do access PlayerScript1 script. And thats why, I have the PlayerScript1 declaration in them. Like below:
public class HealthScript : MonoBehaviour {
PlayerScript1 playerScript;
void Start(){
/*accessing the script attached to game object*/
}
}
So how can I have my Health Script access different instances of my PlayerScript based on the scene? I know I could use delegates to call different methods in runtime, but how can I do the same with classes?
So how can I have my Health Script access different instances of my PlayerScript based on the scene?
Well first, you'll want to declare that object as of Type PlayerBase as you will be unable to assign an instance of PlayerScript2 to a variable of type PlayerScript1: those classes might inherit from the same parent, but they are not the same and you cannot convert from one to the other.
After that you will need to search for the player object in the scene, something like...
void Start(){
playerScript = GameObject.Find("Player").GetComponent<PlayerBase>();
}
Assuming, of course, that PlayerBase extends MonoBehaviour. If it doesn't you can't get a reference this way (as it won't exist in the scene at all). Additionally if you want this health object to persist from scene to scene, you need to call DontDestroyOnLoad() for it (as well as remembering that if you don't start testing from Scene 1 where this object is, it won't exist at all, or if you have a copy in every scene, you'll have duplication problems).
Someone answered to my question on the Unity forum which cleared all my doubts:
The key was using the below line in my HealthScript :
PlayerBase player = (PlayerBase)FindObjectOfType(typeof(PlayerBase));
http://answers.unity3d.com/answers/1348311/view.html

What is MonoBehaviour in Unity 3D?

using UnityEngine;
using System.Collections;
public class VariablesAndFunctions : MonoBehaviour
{
int myInt = 5;
}
The full code is here Unity Official Tutorials
What is the purpose of MonoBehaviour
MonoBehaviour is the base class from which every Unity script derives. It offers some life cycle functions that are easier for you to develop your app and game.
A picture is worthy of thousands of words.
Source of the image: https://docs.unity3d.com/uploads/Main/monobehaviour_flowchart.svg
While the following statement is correct,
"MonoBehaviour is the base class from which every Unity script derives" -
I honestly feel it can be misleading to beginners. The phrase - "every Unity script" - being the culprit.
It gives a beginner the notion that all scripts created in unity must extend Monobehaviour. Which is not the case. You can create scripts that house classes that extend the c# base object class. In doing so, your script is then categorised as not a Unity script but nothing stops it from interacting with other Unity scripts and vice versa.
MonoBehaviour is another class that VariablesAndFunctions is inheriting from. This allows the inheritor to use the methods and variables of the other class providing they have the correct access level modifier set.
In the below example Class1 inherits from Base and so can use the protected method Method1
public class Base
{
protected void Method1 { /*...*/ }
}
public class Class1 : Base
{
public void Method2 { Method1(); }
}
Note in this particular example it would be better for Method1 to be marked as abstract or virtual so then Class1 can override it like so:
protected override Method1()
{
//...
base.Method1(); //Call the implementation of Method1 in Base here
//...
}
In particular though MonoBehaviour is described as being:
MonoBehaviour is the base class from which every Unity script derives.
Therefore when doing scripting in unity, you use this base class to better control how things are accessed so you do not need to do it yourself.
Monobehavior is what most of your scripts inherit from,
if you go to the documentation Click here!
you will see a bunch of variables and methods you get from this Inheritance.
such as:
Public Methods
Messages
Properties
Public Methods
Static methods
The most commonly used method (its under message in the documentation but honestly its better to see it as a function) is Update , its the main game loop, the speed at which the update function is called is based on your fps. But the important thing to take away is that if you didn't inherit from monobehavior, you wouldn't have access to this game loop.
Another important function that you get from Monobehavior is Start, which is called once on a script, and it's called after awake, so if you want to set some variables up you can do it here.
The important thing to take is that if you made a simple C# class that inherits from nothing, you wouldn't have access to these methods discussed. Monobehavior gives you access to many functions that help you build your game.
There are other behaviors your scripts can inherit from like ScriptableObject and StateMachineBehaviour, which give you access to other methods, but Monobehavior is the most common behavior your scripts will inherit from.
It's also good to note that whenever you use Monobehavior, it comes with a transform, some other scripts (Scriptable objects) don't come with a transform. The transform is simply a position in your game/scene where the gameobject lies its an x,y,z coordinate with rotation and scale.

Finding and starting a generic class?

I have a Unity C# application where every game inside has one abstract controller. It inherits from another class enabling it to be accessed like a static version of the base behavior in unity.
public class AbstractController<T> : SingletonMonoBehavior<T> {
virtual public void Begin() {
//startup code here
}
}
So, to find this class, I have to know what T will be. Do I need reflection for this? Or can I just store T types in a List somewhere, and access them dynamically? Right now, I DO have a dictionary of game names and classes that inherit from abstract controllers -- so I know what T is supposed to be, but when I do this:
_controllerTypes = new Dictionary<GameScene, Type> ();
_controllerTypes.Add (GameScene.FrogJump, typeof(FJGameController));
Type T = _controllerTypes [_startScene];
AbstractController<T>[] controllers = GameObject.FindObjectsOfType<AbstractController<T>> ();
I get a compiler error stating that "The type or namespace name `T' could not be found." Is there a way to design around this? I don't want to use reflection, but I want it to be pretty.
What you are about to do is absolutely possible but requires the use of reflection. You shouldn't do this as it will affect the performance of your game. Like Catlard mentioned in the comment section, use interface.
interfaces allow you to guarantee that a class has a function, but how
do you access that class in the first place? You can't just
GetComponent for all IControllable interfaces, for example. I'd still
have to have an abstract controller class, right?
You can use GetComponent to check for an interface.
public class Player : MonoBehaviour, IControllable
{
}
then your Interface:
public interface IControllable
{
}
Now, lets say that your Player script is attached to a GameObject called "Player".
GameObject plyrObj = GameObject.Find("Player");
if (plyrObj.GetComponent<IControllable>() != null)
{
Debug.Log("Player is Controllable");
}
Your can also have multiple interface for different classes. You can also make the interface generic with something like public interface IControllable<T>{}. This should help you re-do your work.

Categories