- Home /
[C#] Using Static Create() methods with inheritance and Monobehaviours?
I have a world composed of Regions, Cities, Buildings, and [specific buildings]. They all inherit from an abstract Location, and the specific buildings inherit from Buildings.
I keep running into problems in creation though, since I can't use a constructor. Where I would usually just do:
public City(Region inParentRegion, string inName): base(inName)
{
//stuff
}
I was thinking I'd have the "top-level real gameobjects" like cities/regions/etc pass a prefab up to the Location create method, have it go through adding all the location specific code, return that object to the City(or such) Create method where the specific code is added and the object is parented to the right thing, but I'm not quite sure how to go about doing that or if it's even the right direction.
How have you dealt with inheritance in monobehaviours? And with the complications from having an abstract top-level class?
To add more wrinkles to the problem, it seems that while usually a class inheriting from a monobehaviour counts as one as well, it doesn't work if that class is abstract?
Currently, this is what I have but doesn't work, as "Location newLoc = (Location) Instantiate(prefab);" obviously wouldn't work in this context, but I'm not quite sure how to do this part.
public abstract class Location: MonoBehaviour, ITargetable
{
//stuff
protected static Location Create(GameObject prefab, string name)
{
Debug.Log(string.Format("Generating new location: {0}", name));
//Make Object
Location newLoc = (Location) Instantiate(prefab);
newLoc.Name = name;
//etc
return newLoc;
}
}
public class City: Location
{
//stuff
public static City Create(Region parentRegion, string name)
{
City newCity = (City) Location.Create(WorldManager.Find.CityPrefab, name);
newCity.ParentRegion = parentRegion;
//etc
return newCity;
}
}
What does your 'top-level'-class represent? Are you trying to add children to your (i.e. Region) GameObjects?
- Region
City one
City two
Or is the Location class the only class thats inherited?
City, Region, Zone, Building all inherit from Location, which handles the references to what is at that location, moving things, general "this is a place" stuff. Company is an example of one type of class that inherits from building (and thus location)
I can't use normal constructor logic since i can't instantiate a Location because it is abstract, so I instantiate Regions and Cities... but then I can't "pass them up" to Location to get the data all locations have.
What do you mean by "pasing them up"? If you inherit, say, a City from Location, then City will have access to all public and protected fields and methods of Location.
Edit: I read your question with more thought. Is the case that you need to invoke an initializer method in your Location before you do anything in your concrete class?
I mean how I could once do this:
public City(Region inParentRegion, string inName): base(inName)
{
//stuff
}
passing the name (or whatever other vars I need) up to the base Location constructor, that same style doesn't work without constructors.
I guess I have to put all the object creation code in each city/region/building/zone and copy/paste that crap in each, then have it pull the com[ponent and pass only that part to location to fill in the Location data, and have it passed back to finish the specific city/region/etc code.1
Have you tried adding a City.cs script to the CityPrefab and then instantiate it in Location and use Location newLoc = gameObject.GetComponent("Location");
? Or alternatively (since the comment box eats the tag marks) GetComponent "less than" Location "greater than" ()
Answer by Jamora · Jun 09, 2013 at 09:13 PM
It is not obvious from your question as to what you're trying to accomplish. But my interpretation is that you need your buildings to be some sort of a data class. Unity has ScriptableObjects for exactly that purpose. They are internally the same thing as MonoBehaviour, only don't need to be attached to a gameobject. http://docs.unity3d.com/Documentation/ScriptReference/ScriptableObject.html
However, if you do not need the functionality of a MonoBehaviour, as would be the case when representing some sort of internal logic of a part of your world, you need not inherit from MonoBehaviour, which leaves you open to programming 'normally' whereby your constructor approach could work.
I need them all to be monobehaviours, specifically. However having an abstract class Location that is also a monobehaviour doesn't seem to work, since the usual constructor style "base(params)" thing won't work in that fashion, as instantiating gameobjects doesn't work the same way as an inheritance constructor.
Answer by Loius · Jun 09, 2013 at 11:50 PM
Well, Start and Awake are essentially the constructor for MonoBehaviours. Awake runs inline with your code as you instantiate a thing, but Start waits until ... well, later. It's like, just before the next frame or something. But you can essentially "send constructor parameters to Start" by setting the behaviour's fields after instantiation/Awake. Then the MB can handle whatever it needs to do privately in Start. It's messy, but that's the way of MBs.
Set the values after instantiating your city, then each class's Start can call its own base.Start().
public abstract class Location : MonoBehaviour {
public string myName = "";
public virtual void Start() {
myName = "Location " + myName;
}
}
public class City : Location {
public override void Start() {
base.Start();
myName = "City " + myName;
Debug.Log(name + "'s name is " + myName);
}
}
...
City newCity = Instantiate(prefab);
newCity.myName = "Durham";
That mess is what I was trying to avoid, as you can't pass parameters directly into start or awake. I'm trying to make sure that when these objects are created, they are given the data they need to exist immediately, not having to be created and then getting the data from within after being created.
You might be able to get to it with a bunch of extension methods, tons of code dependence but it's tucked away at least:
public abstract class Location : $$anonymous$$B {
public static void Location(string name) {
// instantiate
newThing.FakeConstructor(name);
}
protected static void FakeConstructor(this Location l, string arg1) {
myName = arg1;
}
}
public class City : Location {
public static void City(string name, int population) {
// instantiate
newThing.FakeConstructor(name,population);
}
// might need 'new' between static and void
protected static void FakeConstructor(this City c,string arg1, int arg2) {
(c as Location).FakeConstructor(arg1);
myPop = arg2;
}
}
Just regarding the Start/Awake thing (as you seem not to be 100% sure), so as you said Awake is called when instantiating the object, but about Start it is called once before the first Update. If you instantiate an object you get Awake and Start and then Update. BUT if you instantiate and keep the guy disabled, like you would do when pooling object, then the Awake goes but that is it until you enabled the object for the first time.
Well, maybe it was clear already...
Answer by InfiniBuzz · Jun 09, 2013 at 11:51 PM
You could try to use interfaces
interface ILocation {
// put your interface members here
// Variables, methods..
}
then in your classes you inherit from MonoBehaviour and from the interface so your class needs to implement the interfaces members.
class City : MonoBehaviour, ILocation {
// implement ILocation member
}
Then you can handle all your objects as ILocation where needed.
Well, I want to use inheritance ins$$anonymous$$d of interfaces to avoid significant copy/pasted code. all the logic of location would have to be copied the exact same into every instance of an ilocation derived class.
Your answer
Follow this Question
Related Questions
An OS design issue: File types associated with their appropriate programs 1 Answer
C# Conception - Hide inherited members and functions 1 Answer
Is it possible to make sealed overridden MonoBehaviour method. 0 Answers
C# Inheriting from MonoBehaviour but still be able to instantiate with new()? How should I do this? 2 Answers
Multiple Cars not working 1 Answer