Say I have a base class called Enemy and a derived class called Ogre.
What is the difference between creating an instance these two ways:
Enemy newOgre = new Ogre();
Ogre newOgre = new Ogre();
Actually, the piece of code that is creating the instance is only new Ogre(). What is in the left side of the equal sign has nothing to do with creating the instance.
The first statement is simply assigning the created instance to a variable of type Enemy. The second one is assigning it to a variable of type Ogre.
So you have two variables of different types pointing to objects of the same type, i.e. Ogre.
The variable (what is on the left side of the equal sign), only determines what you can access from the object. For example, if the Ogre class has a method that is not inherited from Enemy, then using the Enemy variable, you will not be able to access it.
Please note that the variable does not effect how the object behave. For example, if Ogre overrides a method defined in Enemy that does something different. Calling this method on an instance of Ogre using a variable of type Enemy would cause the overridden method in Ogre to be invoked, not the one in Enemy,
For example, consider these classes:
public class Enemy
{
public virtual void Test()
{
Console.WriteLine("enemy");
}
}
public class Ogre: Enemy
{
public override void Test()
{
Console.WriteLine("ogre");
}
}
Now if you do this:
Orge enemy = new Orge();
enemy.Test();
The console would print "ogre".
And if you do this:
Enemy enemy = new Ogre();
enemy.Test();
The console would still print "orge".
In addition to Yacoub's answer, in this case, Enemy would not contain the properties, and methods that Ogre has.
public class Enemy
{
public int Property1 { get; set; }
public int Property2 { get; set; }
}
public class Ogre : Enemy
{
public int Property3 { get; set; }
}
Let's say you inherit Enemy in your Ogre class. This mean that your Ogre will effectively contain 3 properties: 1,2 and 3.
In your example you're assigning an Ogre to an Enemy type.
The Enemy type doesn't contain a "Property3" and therefor you won't be able to work with the extended class "Ogre" in an Enemy cast object.
//This will work
Ogre newOgre = new Ogre();
int newInt = newOgre.Property3;
//This wont.
Enemy newOgre = new Ogre();
int newInt = newOgre.Property3;
One declares a variable of type Enemy and references a new Ogre object. The other declares a variable of type Ogre and references a new Ogre object.
Some differences (not an exhaustive list):
You can't call non-inherited methods of Ogre on the variable of type Enemy.
Any virtual methods of Enemy that are overridden in Ogre will use Ogre's implementation when called on either variable.
Assume your Enemy class looks like this:
public class Enemy
{
public void Attack() { }
public void Move() { }
}
and your Ogre class like this:
public class Ogre : Enemy
{
public void OgreSmash() { }
}
With the Enemy variable you would only have access to Attack() and Move() but not to the OgreSmash() with the Ogre variable you will have access to the methods of the base and derived class.
Here
Enemy newOgre = new Ogre();
you can't call method using newOgre that was later added to the class Ogre for example and was not in the base class, whereas using the other variable you can call those methods.
Related
I got stuck in pretty dumb situation: I'm making new instance of the generic class but it returns "weird" null.
Rule rule2 = new Rule(); // initiate the class
Debug.Log(rule2); //1st debug
rule2.RuleSetup(r: "CaughtEnough", li: 0); //setting up the parameters
Debug.Log(rule2.rule); //2nd debug
1st debug gives me
null
UnityEngine.Debug:Log(Object)
at the same time setting up the parameters works, and 2nd debug gives me
CaughtEnough
UnityEngine.Debug:Log(Object)
which is what supposed to be in the proper class instance.
One (only so far) issue that it is bringing to me is that if whitin this Rule class instance I call
Invoke(rule, 0f);
it gives me the NullReferenceException error. But at the same time the actual function
CaughtEnough();
works just fine and as expected.
Any ideas what could be the source of the problem and how to overcome it?
UPD also posting setup part of Rule class, as asked, though it is straightforward
public class Rule : MonoBehaviour {
public string rule;
public int leftInt;
public Dictionary<string, int> leftDict;
public float countdown;
public int outcome;
public CatchManager catchMan;
public Net net;
// Use this for initialization
void Start () {
RuleSetup();
}
public void RuleSetup(string r = "NoRule", int li = 0, Dictionary<string, int> ld = null, float cd = float.PositiveInfinity) {
rule = r;
leftInt = li;
leftDict = ld;
countdown = cd;
}
.....
public class Rule : MonoBehaviour{}
Rule rule2 = new Rule();
You can't use new keyword to create new instance if you are inheriting from MonoBehaviour.
You should get exception that says:
You are trying to create a MonoBehaviour using the 'new' keyword.
This is not allowed. MonoBehaviours can only be added using
AddComponent(). Alternatively, your script can inherit from
ScriptableObject or no base class at all
Your code would have worked if you had public class Rule {} but you have public class Rule : MonoBehaviour {}.
Creating new instance of class that derives from MonoBehaviour:
Example class:
public class Rule : MonoBehaviour
{
public Rule(int i)
{
}
}
If you inherit from MonoBehaviour, you should either use GameObject.AddComponent or Instantiate to create new instance of it.
Rule rule2 = null;
void Start()
{
rule2 = gameObject.AddComponent<Rule>();
}
OR
public Rule rulePrefab;
Rule rule2;
void Start()
{
rule2 = Instantiate(rulePrefab) as Rule;
}
If the Rule script already exist and is attached to the GameObject, you don't need to create/add/instantiate new instance of that script. Just use GetComponent function to get the script instance from the GameObject it is attached to.
Rule rule2;
void Start()
{
rule2 = GameObject.Find("NameObjectScriptIsAttachedTo").GetComponent<Rule>();
}
You will notice that you cannot use the parameter in the constructor when you derive your script from MonoBehaviour.
Creating new instance of class that does NOT derives from MonoBehaviour:
Example class: (Note that it does not derive from "MonoBehaviour"
public class Rule
{
public Rule(int i)
{
}
}
If you don't inherit from MonoBehaviour, you should use the new keyword to create new instance of it. Now, you can use the parameter in the constructor if you want.
Rule rule2 = null;
void Start()
{
rule2 = new Rule(3);
}
EDIT:
In the latest version of Unity, creating new instance of a script that inherits from MonoBehaviour with the new keyword may not give you error and may not be null too but all the callback functions will not execute. These includes the Awake, Start, Update functions and others. So, you still have to do it properly as mentioned at the top of this answer.
Just a a follow up, how I ended up doing it and why:
I no longer inherit the Rule class from MonoBehaviour to avoid tracking cretion and deletion of the gameObjects, which appeared to be the pain.
As Invoke method does not exist in generic classes, I replaced it with reflection, as described here
I have two classes. One called GameManager and another one Enemies.
I have two variables in GameManager which I have changed from inspector currentLevel=1 and totalEnemy=10.
// GameManager.cs
private static GameManager instance = new GameManager();
public static GameManager get(){ return instance; }
public int currentLevel;
public int curLevel { get; set; }
public int totalEnemy;
public int totLevel { get; set; }
void Start () {
curLevel = currentLevel;
totLevel = totalEnemy;
}
I'm trying to access these two variable from Eneimes class like this; but everytime it gives me curLevel = 0, but I'm expecting to get curLevel = 1. What I'm doing wrong?
// Enemies.cs
void Start () {
Debug.Log (GameManager.get().curLevel); // always output = 0
}
The line private static GameManager instance = new GameManager(); is the issue.
When a script is attached to a GameObject, an instance of the type of the script is referenced as this inside the script. In other words, there can be multiple instances of same type if the same script is attached to multiple GameObjects.
Therefore, the specific instance that have curLevel = 1 as you set in the Inspector is an instance of the type attached to the specific GameObject. This means the one should be referred to as this inside the script.
If you declare a new instance of GameManager as in your code, you are basically ignoring all values in the Inspector because the static GameManager instance is pointing to a different instance than the instance you set values for in the Inspector.
In order to use the specific instance that you declared using the Inspector, you should do the following.
using System.Collections.Generic;
using System.Collections;
using UnityEngine;
public class GameManager : MonoBehaviour
{
private static GameManager instance;
public static GameManager get() { return instance; }
public int currentLevel;
public int curLevel { get; set; }
public int totalEnemy;
public int totLevel { get; set; }
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Debug.LogError(string.Format("GameManager.Awake(): More than one instances of this type {0} is being initialised but it's meant to be Singleton and should not be initialised twice. It is currently being initialised under the GameObject {1}.", this.GetType(), this.gameObject.name));
Destroy(gameObject);
}
curLevel = currentLevel;
totLevel = totalEnemy;
}
}
Note that I changed Start() to Awake(). This is because you are referring to values initiliased in this method from other scripts, and you cannot guarantee which Start() is called first between different MonoBehaviours in the runtime. However, Unity guarantees that Awake() is always called earlier than Start(). Further, it is Unity's best practice to initialise self-initialisable variables in Awake(), and initialise variables dependent on other scripts in Start() because of this execution order.
Lastly, there will be problems when there are multiple GameObject that has GameManager as its component in your scene. Consider a case where you have two such objects. when the scene is loaded, each of the script will call Awake(), and both of them will set private static GameManager instance; to each of the two this. The result would be one is overriden by another.
You could say that you will be careful to use this script and make sure only one GameObject has this script as its component. However, you should always write your code as if someone who do not know about your code can use it without thinking, and stupid mistakes of other people new to the project could be easily detected.
EDIT:
To respond to the OP's comment, I added code to handle when this type is initialised more than once in the project. In addition to #Kardux's suggestion, I added Debug.LogError() because I do not want the project to silently solve things. If a problem happens, I want to get notified of it.
If you are using Singletons frequently in your project, you might want to have a parent abstract class Singleton that handles this instance checking process for all child Singletons, and have GameManager inherit from Singleton.
However, use Singleton with care as it is considered a bad design pattern if misused. (And I don't know how to use it properly so I avoid using it.)
I want to create a class named Enemy, which should be used in a programmed rpg-themed-battlesystem. The problem is that I would want to create multiple monster types in the Enemy class, but then I would have to create a possibility for the battlesystem with every enemy class for example Enemy.Goblin or Enemy.Golem.
Question:
How could I achieve this by using only one parameter in the battlesystem function? I wanted to use
public static void InitiateBattle ( Player player, Enemy enemy )
but now I cannot use the Enemy.Goblin instance, because it cant implicitly convert Enemy.Goblin to Enemy. How could I most easily and with minimal code fix this?
You need to use inheritance.
public class Enemy
{
// put all properties and methods common to all here
}
public class Goblin: Enemy
{
// goblin specific stuff here
}
you will then be able to pass in a goblin as an enemy.
It sounds like you want to use inheritance?
public class Enemy {}
public class Goblin : Enemy {}
public class Golem : Enemy {}
You can then pass in an instance of Goblin or Golem to your method and the statement will be valid because the compiler will 'box' your object into an instance of the parent type.
Then, if you want to use a member from the Goblin or Golem subclasses, you would need to 'cast' the enemy parameter variable back into the appropriate type using as:
public static void InitiateBattle (Player player, Enemy enemy)
{
var golem = enemy as Golem;
var goblin = enemy as Goblin;
}
Make sure you check for null after the cast!
Bear in mind that C# does not allow multiple-inheritance; each class can inherit from only one parent.
I think it would be best to use interface.
public interface IEnemy
{
//e.g.
public void Attack();
}
public class Goblin : IEnemy
{
public void Attack()
{
throw new System.NotImplementedException();
}
}
public class Battle
{
public static void InitiateBattle(Player player, IEnemy enemy);
}
Is it possible to force a static data member in inherited subclasses?
Here is my problem/thought process:
I want to make a "GameObject' base class that all other objects inherit from to lend to polymorphism.
I want each inherited class to have a static data member list of vertexes to render the polygons. The exact process would be:
Create object based on static list of vertexes
Apply textures
Rotate object based on instanced object's rotation variables
Transform object based on instanced object's world coordinates
for all objects that inherit from Game Object, I'd like to guarantee that it has the static vertex list
Should I care if it has the list or not, or should I just care that it has a draw method (guaranteed by an interface iRender)?
By virtue of inheritance, a protected static List<Vertex> in the base class is automatically part of any derived classes. This should work as long as you don't need each derived class to have its own list separate from the list in any parent class.
There is a single instance of a static class member declared in a base class for all descendants and the base class itself. Therefore a single list of vertices in a base class will not do. What can be done:
- in the base class declare
private static Dictionary<string, List<vertex>> PointsPerClass =
new Dictionary<string, List<vertex>>();
You did not say how descendants specify their vertices, but let's say there a virtual base class method overwritten by all descendants:
protected virtual FillStaticVertices(List<vertex> aVertices)
{
}
Then the base class has public method:
public GameObject CreateObject()
{
string key = GetType().FullName;
List<vertex> points = null;
if (PointsPerClass.ContainsKey(key))
points = PointsPerClass[key];
else {
points = new List<vertex>();
PointsPerClass.Add(key, points);
FillStaticVertices(points);
}
// CreateObjectFromPoints is an abstract method implemented by descendants
GameObject gameObj = CreateObjectFromPoints(points);
// apply textures, rotations, etc
return gameObj;
}
If the GameObject is an interface, then you can enforce the existence of a static Property.
interface GameObject
{
static List<vertex> vertices {get; set;}
//other common code
};
Alternatively, you could just have the list of vertices as a protected data member in GameObject itself, and have the derived classes access it.
I'm working on a game for WP7 with XNA. Here is my structure:
public abstract class enemy
{}
Child elements:
public class genericEnemy : enemy{}
...
public class snake : enemy {}
etc...
In WP7, a lot of things have been moved around and/or removed (especially with Serialization) it seems. Despite much searching, I haven't been able to find a solution. I'm trying to duplicate the child elements.
For example: On loading a level, I pass an array of three different enemies into the loading phase. During loading, I need to duplicate each of those enemies so that 20 of each are flying around doing their own thing during gameplay.
All the solutions I've seen refer to things that are not present in the WP7 library.
There's no "library" way of doing this as far as I know. One solution would be:
1) Declare a Clone() method in enemy that returns a copy of that enemy.
abstract class Enemy {
public abstract Enemy Clone();
}
2) Implement it in every concrete type, so a Snake creates a new Snake, etc. Example:
class Snake : Enemy {
int speed;
public override void Enemy Clone() {
var clone = new Snake();
clone.speed = speed;
return clone;
}
}
3) Now any object of a concrete type knows how to clone itself, so if you have an array of Enemies, you can call Clone() on each and it will create the proper concrete type in the proper way.
Create an enemy factory that can create enemies from an id of some sorts. While loading your level, you can then call the factory when you need to create an enemy:
class EnemyFactory
{
Enemy CreateEnemy(int id)
{
if (id == 0)
return new Snake();
return new GenericEnemy();
}
}
void LoadLevel()
{
// bla bla
Level level = new Level();
int enemyId = LoadFromFile();
level.AddEnemy(EnemyFactory.CreateEnemy(enemyId));
}
This way you get rid of the nasty cloning code, and you can control all enemy instantiation in the factory class.
use an abstract method that calls a copy constructor:
public abstract class Enemy
{
private readonly int mEnemyData;
protected Enemy(Enemy pEnemy)
{
mEnemyData = pEnemy.mEnemyData;
}
public abstract Enemy Clone();
}
public sealed class GenericEnemy : Enemy
{
private readonly double mGenericEnemyData;
private GenericEnemy(GenericEnemy pGenericEnemy)
: base(pGenericEnemy)
{
mGenericEnemyData = pGenericEnemy.mGenericEnemyData;
}
public override Enemy Clone()
{
return new GenericEnemy(this);
}
}