some GameObjects in my scene implement the interace ISaveable. In my script, I want to find all these interfaces and store them. Later on I can loop through them and call their implemented method SaveData().
My current workaround to find these interfaces:
List<ISaveable> saveables = new List<ISaveable>();
MonoBehaviour[] sceneObjects = FindObjectsOfType<MonoBehaviour>();
for (int i = 0; i < sceneObjects.Length; i++)
{
MonoBehaviour currentObj = sceneObjects[i];
ISaveable currentComponent = currentObj.GetComponent<ISaveable>();
if (currentComponent != null)
{
saveables.Add(currentComponent);
}
}
The code works fine but is there a better way? I don't want to search for each Monobehaviour in the scene and then try to get its interface component.
Simplified filter using Linq
You can use Linq OfType
ISaveable[] saveables = FindObjectsOfType<MonoBehaviour>().OfType<ISaveable>().ToArray();
of course it still requires to find all objects first.
Note though that it underlies the general limitations of FindObjectsOfType regarding inactive GameObjects or disabled behaviors.
Iterate through the Hierachy
You could extend it to go through all root level objects of the scene. This works since afaik GetComponentsInChildren indeed does work also with interfaces!
var saveables = new List<ISaveable>();
var rootObjs = SceneManager.GetActiveScene().GetRootGameObjects();
foreach(var root in rootObjs)
{
// Pass in "true" to include inactive and disabled children
saveables.AddRange(root.GetComponentsInChildren<ISaveable>(true));
}
If it's more efficient - yes, no, maybe, I don't know - but it includes also inactive and disabled objects.
And yes one could extend that to iterate through multiple loaded scenes using
var saveables = new List<ISaveable>();
for(var i = 0; i < SceneManager.sceneCount; i++)
{
var rootObjs = SceneManager.GetSceneAt(i).GetRootGameObjects();
foreach(var root in rootObjs)
{
saveables.AddRange(root.GetComponentsInChildren<ISaveable>(true));
}
}
Not use an interface in the first place
This alternative is a bit similar to this answer but it has a huge flaw: You would need a specific implementation for the interface in order to make it work which invalidates the whole idea of an interface.
So the big question also from the comments there is: Why have an interface at all?
If it is anyway only going to be used for MonoBehaviour you should rather have an abstract class like
public abstract class SaveableBehaviour : MonoBehaviour
{
// Inheritors have to implement this (just like with an interface)
public abstract void SaveData();
}
This already solves the entire issue with using FindObjectsOfType anyway since now you could simply use
SaveableBehaviour[] saveables = FindObjectsOfType<SaveableBehaviour>();
but you can still go further: For even easier and more efficient access you can make them register themselves completely without the need of a manager or Singleton pattern! Why should a the type not simply handle its instances itself?
public abstract class SaveableBehaviour : MonoBehaviour
{
// Inheritors have to implement this (just like with an interface)
public abstract void SaveData();
private static readonly HashSet<SaveableBehaviour> instances = new HashSet<SaveableBehaviour>();
// public read-only access to the instances by only providing a clone
// of the HashSet so nobody can remove items from the outside
public static HashSet<SaveableBehaviour> Instances => new HashSet<SaveableBehaviour>(instances);
protected virtual void Awake()
{
// simply register yourself to the existing instances
instances.Add(this);
}
protected virtual void OnDestroy()
{
// don't forget to also remove yourself at the end of your lifetime
instances.Remove(this);
}
}
so you can then simply inherit
public class Example : SaveableBehaviour
{
public override void SaveData()
{
// ...
}
protected override void Awake()
{
base.Awake(); // Make sure to always keep that line
// do additional stuff
}
}
and you could access all instances of that type via
HashSet<SaveableBehaviour> saveables = SaveableBehaviour.Instances;
foreach(var saveable in saveables)
{
saveable.SaveData();
}
You could have a manager class holding a collection/list of ISaveable.
You can then make that manager class a singleton by setting the Singleton value in its Awake method.
class SaveManager(){
public static SaveManager Instance;
Awake(){
if (Instance == null)
{
Instance = this;
}
}
List<ISaveable> SaveAbles;
public void AddSaveAble(ISaveAble saveAble){
//add it to the list.
}
}
Then in the Start method of class implementing the ISaveable interface you can use the Singleton to add it to the total list.
class Example : MonoBehaviour, ISaveable
{
void Start(){
SaveManager.Instance.AddSaveAble(this);
}
}
That way, each SaveAble adds itself to the manager via the managers method when it is created.
Note that it is important that the Singleton is set in Awake so it can be used in Start, as Awake comes first in the lifecycle.
How about having global state, perhaps an list of ISaveable, and have each savable object add a reference to it during start()? Then you can simply iterate through the array, invoking SaveData() on each ISaveable reference?
The typical real-world solution:
public interface Automateme
{
public void Automate();
}
then ...
void _do()
{
Automateme[] aa = GameObject
.FindObjectsOfType<MonoBehaviour>()
.OfType<Automateme>()
.Where(a => ((MonoBehaviour)a).enabled)
.ToArray<Automateme>();
if (aa.Length == 0)
{
Debug.Log($"Automator .. none");
return;
}
Debug.Log($"Automator .. " + aa.Length);
foreach (Automateme a in aa)
{
Debug.Log($"Automator {a.GetType()} {((MonoBehaviour)a).gameObject.name}");
}
// do something to one of them randomly, for example! ->
aa.AnyOne().Automate();
}
One point, it is all-but inevitable that you will need the object also as a MonoBehavior, ie you will be casting it to a MonoBehavior (as in the simple example of getting the name of the game object), so it's sensible (and no less/more inefficient) to use the FindObjectsOfType<MonoBehaviour>() idea first mentioned by #derHugo
Some points
Of course, obviously, in some situations you wouldn't at all do what is asked in this question, you'd trivially "keep a list" of the items under consideration. (And that would vary greatly depending on the situation, do you only want active ones, do you want to include items on their first frame of bringup, etc etc etc). The entire point of the question is for the (many, typical) situations where you want to get them on the fly and caching is not relevant
Unity development, confusingly, has a massive, just ridiculously big, domain range. Many types of projects have perhaps a dozen game objects in the whole scene; other types of projects can have vast numbers of game objects in the scene. Games with 2D physics are incredibly different from games with 3D physics, rendering approaches are incredibly different from shader approaches, and so on. Naturally with any "style" question like this if you are coming from a certain "domain" in your head, the answer can be quite different.
Regarding FindObjectsOfType and similar calls, it's worth remembering that Unity efficiency has incredibly moved on from the early days. It used to be that the "first thing you'd tell new Unity programmers" was "Don't do something every frame that searches the scene!!! OMG! You'll slow down to 1fps!" Of course that is still true in certain, perhaps many, domains. BUT it is (for a decade now) often just completely wrong. Unity's caching and handling of scene objects is now blisteringly fast (you can't write faster) and - as mentioned in the previous point - you're often just talking about a handful of items anyways. {A good example of the change in efficiency of the entire Unity paradigm is "pooling". (Any game programmers not as old as me can look up what that was :) ) In the early days of Unity you'd Pool! everything in a panic, My God, I may have three tanks at once, get a pooling library! Indeed I am so old I wrote one of the first popular Pooling articles that everyone used for awhile (long since deleted). Nowadays Unity's handling of instantiating prefabs or offscreen models is so fast and clever that you could more or less say they offer pooling built-in, and nowadays there's no need at all to pool things like everyday bullets from machine guns, etc. Anyway the point of this example is just that the (very old) "standard advice" about not searching the scene has to be understood more completely, these days.}
Swinging back to the original question:
I don't want to search for each Monobehaviour in the scene and then try to get its interface component.
The only way in a game engine to search each object in a scene, is to search each object in the scene. So there's no way around that. Of course, one can "keep a list" BUT it rather misses the two points (A) in some/many situations that's just not viable/sensible in a game architecture and (B) Unity is already incredibly good at keeping a list of game objects / components, it's the whole raison d'etre of a frame engine. (Note that if it's a physics game, the whole PhysX system for goodness sake is doing an astounding, cutting-edge job, of "finding things every frame", using mathematically freaky spatial hashing and more, dealing with orders of complexity that go with the square or worse of every physics poly in the scene! Compared to that it is misguided to worry about you pulling out 3 items from a list of 50 (which are flawlessly hashed and managed for efficiency by the frame engine anyway).)
In my case I was using this implementation:
MonoBehaviour[] saveableScripts = (MonoBehaviour[])FindObjectsOfType(typeof(MonoBehaviour));
foreach (var enemy in saveableScripts)
{
if (enemy is ISaveable == false)
continue;
// you code ...
saveables.Add(currentComponent);
}
Note that this solution is highly inefficient, but you could use LINQ to filter out the components that implement your interface. In this case MyInterface is the interface that we want to query for.
MyInterface[] myInterfaces = GameObject.FindObjectsOfType<MonoBehaviour>()
.OfType<MyInterface>()
.ToArray();
Small note to clarify "inefficient": With Unity optimizing their object querying methods the call is not even that bad. The issue is that LINQ allocates a lot of GC, which could introduce frame drops, which is why you should never run these "heavy" operations on a per-frame basis.
Running the query upon startup or when doing an obvious intensive operation (saving/loading) is totally fine.
Here is an improve of your method to get all the MonoBehavior wiht your interface actually in your scenes.
void Start()
{
var myInterface = typeof(MyInterface);
var monoBehavior = typeof(MonoBehaviour);
var types = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(p => myInterface.IsAssignableFrom(p) && monoBehavior.IsAssignableFrom(p));
var myInstance = new List<MyInterface>();
foreach (var type in types)
{
myInstance.AddRange(FindObjectsOfType(type).Select(x => x as MyInterface));
}
}
Here you don't go through all the GameObject in the scenes, but you do more FindObjectsOfType call, + you have to cast. But no more GetComponent needed.
So depend on your case, but if you put the reflection part in cache (during a loading moment) it can avoid you to implement the Manager pattern and you can keep your interface architecture.
Otherwise you can take a look at the observer pattern, it is a different way to manage call over different class type. https://sourcemaking.com/design_patterns/observer
You can improve the code by replacing
ISaveable currentComponent = currentObj.GetComponent<ISaveable>();
with
ISaveable currentComponent = currentObj as ISaveable;
I agree with the idea of using an ISaveable manager for this use case, as Doh09 and others suggest. But to answer your question as asked, you could traverse the entire scene hierarchy, or multiple scenes' hierarchies, as follows:
public class Foo : MonoBehaviour
{
List<ISaveable> saveables;
void RefreshSaveables()
{
saveables = new List<ISaveable>();
for (var i = 0; i < SceneManager.sceneCount; ++i)
{
var roots = SceneManager.GetSceneAt(i).GetRootGameObjects();
for (var j = 0; j < roots.Length; ++j)
{
saveables.AddRange(roots[j].GetComponentsInChildren<ISaveable>());
}
}
}
}
The advantage of this over your original code is that you don't need to save every MonoBehaviour in the scene in order to later filter it - however I still think it is preferable for each ISaveable to register itself on Start and let the manager call their Save methods.
One way is to use unity container (though slightly older way). Register all the dependencies to container through configuration. All the instances of the ISaveable can be registered to unit container and then resolve them in the code
example:
unity container in web.config:
<container name=“GameObjectContainer”>
<register type=“ISaveable” mapTo=“Example1” />
<register type=“ISaveable” mapTo=“Example2” />
</container>
ISavable definition:
public interface ISavable
{
void SaveData();
}
ISavable implementation:
public class Example1 : ISavable
{
public void SaveData()
{
}
}
public class Example2 : ISavable
{
public void SaveData()
{
}
}
Related
I'm looking for a bit of direction on an issue I keep running into. I've recently challenged myself to code very granularly, so that small pieces of functionality can exist in a single component and operate independently of any other components.
The issue that I am running into is that some components use the same functions, functions that will likely never need to differ from each other. For example, I have a generic function that you can pass a string, and it executes an animation with that name. It seems silly to copy and paste this function into two separate components that need to trigger an animation, creating two versions of the function. The only other idea I have though is creating a separate modular piece that handles all animation. The only issue is, if I do that, now my modular components require the existence of this new component to fully function. Obviously I could make it "function" without it and throw debug warnings, but ultimately it makes objects more difficult to properly configure.
I am curious if anyone has any insight into these situations, as I am imagining there's got to be a technique to it.
Thanks
Questions like this are kind of the heart of object-oriented programming. I'd highly recommend the "Gang of Four" book on Design Patterns.
It is a whole textbook, but a few key approaches to your particular problem might be:
Favor composition over inheritance
Code to interfaces instead of classes, and
Use factories to instantiate and initialize objects.
So you could have an enum that defines the kind of animator you get:
public enum AnimationType
{
Knight,
Peasant
}
then you can have an interface that defines the animation states:
public interface IAnimator
{
void Idle();
void Walk();
void Attack();
}
With an interface it doesn't matter what the actual class is that's doing the work, all you care about is that it does the actions listed in the interface.
This means that you can now have anything inherit the interface, like:
public class AnimatorA : IAnimator
{
public AnimatorA(string filepath)
{
// load whatever you need to
}
public void Walk()
{
// do the walk animation
}
// also all the other things required by IAnimator
}
You can create as many or few animator classes as you want now, and they're all interchangeable because the class that needs it is only going to refer to it as an IAnimator.
Almost finally, now you can make a factory to get the particular things you want:
public static class IAnimatorFactory
{
private const string knightPath = "/path/to/knight";
private const string peasantPath = "/path/to/peasant";
public IAnimator GetIAnimator(AnimationType animationType)
{
switch(animationType)
{
case AnimationType.Knight:
return new AnimatorA(knightPath);
case AnimationType.Peasant:
return new AnimatorA(peasantPath);
}
}
}
and then finally you can have your class use the enum, which shows as a dropdown in the inspector, and a private IAnimator, and just get the AnimationType on Start():
public class YourActor
{
public AnimationType animationType;
private IAnimator animator;
void Start()
{
animator = IAnimatorFactory.GetIAnimator(animationType);
}
void Update()
{
if(walkCondition)
{
animator.Walk();
}
// etc.
}
}
The factory is a static class, so you don't need an instance of it. Want to change the animator type? Make a new class, modify when the factory should use that new class, and you don't touch any of the user classes. Want to change the thing the class is using? Change the enum and don't touch the factory or any of the animators.
This can add a fair bit of overhead, but paying the price for adding that infrastructure buys you easier maintenance down the road.
Lets say I'm making a game with a bunch of levels, whereby as you progress through levels, new types of enemies are introduced. If a developer has an enemy and wants to get the level an enemy can be found in, the most intuitive way I can think of allowing them to do this would be to store a list levels on the enemy that they are contained within. This however I feel like is a problem, because now I've coupled my enemy and my level together. My enemy now knows about the context in which it used. My research says that these kinds of relationships are unhealthy for maintaining software, but it seems intuitive to use from a level above.
Here's a very basic implementation:
public class Level
{
List<Enemy> typesOfEnemies;
public Level(List<Enemy> typesOfEnemies)
{
this.typesOfEnemies = new List<Enemy>(typesOfEnemies);
for (int i = 0; i < typesOfEnemies.Count; i++)
{
typesOfEnemies[i].AddLevelContainedWithin(this);
}
}
}
public class Enemy
{
public List<Level> levelsContainedWithin = new List<Level>();
public void AddLevelContainedWithin(Level level)
{
levelsContainedWithin.Add(level);
}
}
So there's probably a few problems associated with this code. The most obvious one that sticks out to me is that I can add a level to an enemy without updating my Level's types of enemies which puts them out of sync. I could resolve this by creating another class that does the work, ensuring the Level and Enemy stay in-sync, but now I've added a layer of code complexity. The developer now has another class they have to know about to work with.
My question is, how do I make this maintainable and intuitive to for a developer to use?
You can introduce another class that does the coupling:
class LevelManager
{
public List<Enemy> GetEnemies(Level l) { ... }
public List<Level> GetLevels(Enemy e) { ... }
}
Now all your data-objects have to know is the LevelManager.
Actually it´s not even neccessary to have this dependcy in neither your Enemy- nor your Level-class. If a developer wants to know in which levels an enemy can spawn, he may use the LevelManager instead.
When I trying to think over game architecture in Unity, I face the following problem: there are several ways to build relations between components, and I cannot understand which of them is the most optimal.
For example we have GameplayObject component with following parametres
public class GameplayObject : MonoBehaviour
{
// every gameplay object has chanse of appear on board
[SerializeField, Min(0)] int m_ChanseOfAppear = 0;
public int ChanseOfAppear => m_ChanseOfAppear;
//every gameplay object may be destroyed
public virtual void Destroy()
{
}
}
Destroy method can be executed after user input(click or drag is just a few types of input) or another gameplay object can execute destory method.
For example we have following TapBehaviour component
public class CustomDestroyer: MonoBehaviour, IPointerClickHandler
{
public event System.Action OnTap = delegate { };
IPointerClickHandler.OnPointerClick()
{
OnTap();
}
}
We have concrete GameplayObject (for exapmle CustomDestroyer) who should destroy objects in a concrete way. And now it need a dependency for our input component
[RequireComponent(typeof(TapBehaviour), typeof(BoxColiider2D))]
public class CustomDestroyer: GameplayObject
{
TapBehaviour m_TapBehaviour;
TapBehaviour TapBehaviour
{
get
{
if (m_TapBehaviour == null)
m_TapBehaviour = GetComponent<TapBehaviour>();
return m_TapBehaviour;
}
}
public override void Destroy()
{
}
}
But we may do it just in an opposite way: inherit CustomDestroyer from TapBehaviour and use GameplayObject like component.
So the main question is how you build architecture of your projects? When you use component's inheritance, when use just components? May be I'm losing some more preferable ways?
The more you explain to me, the more I will be grateful to you!
And sorry for bad english, just learning it.
Unity offers a great degree of freedom regarding architecture, its sometimes overwhelming when you can successfully do stuff in so many different ways that will all work. Regarding your question - in terms of future proofing I would lean towards using seperate components (aka single responsibility), loosely tied together using interfaces (like IDestroy instead of concrete types), the idea is to, whenever possible, avoid interdependency of components.
But for a great deal of cases inheritance with overrides will be fine, and refactoring is often painless (and there's many ways to automate editor tasks if you go too far down one of the alleys) so my advice would be: do not overcomplicate things, do it as simple as possible, but not simpler
From my experience with Unity, you should exactly know what do you want to do and possible future implementations, then find a way to do it and that doesn't limit your future implementations.
There's not ONE way to relate things in Unity, that's part of its ease of use. Everyone ends up doing things their way.
Try to find a solution that balances readability and efficiency, and stick to the newest components which are usually better overall.
Edit: Forgot to say that if you're a code guy try not to abuse the power of scripts since unity was designed for avoiding code. The UI has a lot of options that, in comparison with writing code, seem like magic.
I've been learning C# over the summer and now feel like making a small project out of what I've done so far. I've decided on a sort of text based adventure game.
The basic structure of the game will involve having a number of sectors(or rooms). Upon entry into a room, a description will be outputted and a number of actions and such you may take; the ability to examine, pick up, use stuff in that room; possibly a battle system, etc etc. A sector may be connected up to 4 other sectors.
Anyway, scribbling ideas on paper on how to design the code for this, I'm scratching my head over the structure of part of my code.
I've decided on a player class, and a 'level' class that represents a level/dungeon/area. This level class would consist of a number of interconnected 'sectors'. At any given time, the player would be present in one certain sector in the level.
So here's the confusion:
Logically, one would expect a method such as player.Move(Dir d)
Such a method should change the 'current sector' field in the level object. This means class Player would need to know about class Level. Hmmm.
And Level may have to manipulate the Player object (eg. player enters room, ambushed by something, loses something from inventory.) So now Level also needs to hold a reference to the Player object?
This doesn't feel nice; everything having to hold a reference to everything else.
At this point I remembered reading about delegates from the book I'm using. Though I know about function pointers from C++, the chapter on delegates was presented with examples with a sort of 'event based' programming viewpoint, with which I did not have much enlightenment about.
That gave me the idea to design the classes as follows:
Player:
class Player
{
//...
public delegate void Movement(Dir d); //enum Dir{NORTH, SOUTH, ...}
public event Movement PlayerMoved;
public void Move(Dir d)
{
PlayerMoved(d);
//Other code...
}
}
Level:
class Level
{
private Sector currSector;
private Player p;
//etc etc...
private void OnMove(Dir d)
{
switch (d)
{
case Dir.NORTH:
//change currSector
//other code
break;
//other cases
}
}
public Level(Player p)
{
p.PlayerMoved += OnMove;
currSector = START_SECTOR;
//other code
}
//etc...
}
Is this an alright way to do this?
If the delegate chapter was not presented the way it was, I would not have thought of using such 'events'. So what would be a good way to implement this without using callbacks?
I have a habit of making highly detailed posts... sorry v__v
What about a 'Game' class which would hold the majority of the information like a Player and a current room. For an operation such as moving the player, the Game class could move the player to a different room based on the room's level map.
The game class would manage all the interactions between the various components of the games.
Using events for something like this brings the danger that your events will get tangled. If you're not careful you'll end up with events firing each other off and overflowing your stack, which will lead to flags to turn events off under special circumstances, and a less understandable program.
UDPATE:
To make the code more manageable, you could model some of the interactions between the main classes as classes themselves, such as a Fight class. Use interfaces to enable your main classes to perform certain interactions. (Note that I have taken the liberty of inventing a few things you may not want in your game).
For example:
// Supports existance in a room.
interface IExistInRoom { Room GetCurrentRoom(); }
// Supports moving from one room to another.
interface IMoveable : IExistInRoom { void SetCurrentRoom(Room room); }
// Supports being involved in a fight.
interface IFightable
{
Int32 HitPoints { get; set; }
Int32 Skill { get; }
Int32 Luck { get; }
}
// Example class declarations.
class RoomFeature : IExistInRoom
class Player : IMoveable, IFightable
class Monster : IMoveable, IFightable
// I'd proably choose to have this method in Game, as it alters the
// games state over one turn only.
void Move(IMoveable m, Direction d)
{
// TODO: Check whether move is valid, if so perform move by
// setting the player's location.
}
// I'd choose to put a fight in its own class because it might
// last more than one turn, and may contain some complex logic
// and involve player input.
class Fight
{
public Fight(IFightable[] participants)
public void Fight()
{
// TODO: Logic to perform the fight between the participants.
}
}
In your question, you identified the fact that you'd have many classes which have to know about each other if you stuck something like a Move method on your Player class. This is because something like a move neither belongs to a player or to a room - the move affects both objects mutually. By modelling the 'interactions' between the main objects you can avoid many of those dependencies.
Sounds like a scenario I often use a Command class or Service class for. For example, I might create a MoveCommand class that performs the operations and coordinations on and between Levels and Persons.
This pattern has the advantage of further enforcing the Single Responsibility Principal (SRP). SRP says that a class should only have one reason to change. If the Person class is responsible for moving it will undoubtedly have more than one reason to change. By breaking the logic of a Move off into its own class, it is better encapsulated.
There are several ways to implement a Command class, each fitting different scenarios better. Command classes could have an Execute method that takes all necessary parameters:
public class MoveCommand {
public void Execute(Player currentPlayer, Level currentLevel) { ... }
}
public static void Main() {
var cmd = new MoveCommand();
cmd.Execute(player, currentLevel);
}
Or, sometimes I find it more straightforward, and flexible, to use properties on the command object, but it makes it easier for client code to misuse the class by forgetting to set properties - but the advantage is that you have the same function signature for Execute on all command classes, so you can make an interface for that method and work with abstract Commands:
public class MoveCommand {
public Player CurrentPlayer { get; set; }
public Level CurrentLevel { get; set; }
public void Execute() { ... }
}
public static void Main() {
var cmd = new MoveCommand();
cmd.CurrentPlayer = currentPlayer;
cmd.CurrentLevel = currentLevel;
cmd.Execute();
}
Lastly, you could provide the parameters as constructor arguments to the Command class, but I'll forgo that code.
In any event, I find using Commands or Services a very powerful way to handle operations, like Move.
For a text-based game, you're almost certainly going to have a CommandInterpretor (or similar) object, which evaluates the user's typed commands. With that level of abstraction, you don't have to implement every possible action on your Player object. Your interpreter might push some typed commands to your Player object ("show inventory"), some commands to the currently-occupied Sector object ("list exits"), some commands to the Level object ("move player North"), and some commands to specialty objects ("attack" might be pushed to a CombatManager object).
In that way, the Player object becomes more like the Character, and the CommandInterpretor is more respresentational of the actual human player sitting at the keyboard.
Avoid getting emotionally or intellectually mired in what the "right" way to do something is. Focus instead on doing. Don't put too much value on the code you've already written, because any or all of it may need to change to support things that you want to do.
IMO there's way too much energy being spent on patterns and cool techniques and all of that jazz. Just write simple code to do the thing you want to do.
The level "contains" everything within it. You can start there. The level shouldn't necessarily drive everything, but everything is in the level.
The player can move, but only within the confines of the level. Therefore, the player needs to query the level to see if a move direction is valid.
The level isn't taking items from the player, nor is the level dealing damage. Other objects in the level are doing these things. Those other objects should be searching for the player, or maybe told of the player's proximity, and then they can do what they want directly to the player.
It's ok for the level to "own" the player and for the player to have a reference to its level. This "makes sense" from an OO perspective; you stand on Planet Earth and can affect it, but it is dragging you around the universe while you're digging holes.
Do Simple Things. Any time something gets complicated, figure out how to make it simple. Simple code is easier to work with and is more resistant to bugs.
So firstly, is this an alright way to
do this?
Absolutely!
Secondly, if the delegate chapter was
not presented the way it was, I would
not have thought of using such
'events'. So what would be a good way
to implement this without using
callbacks?
I know a lot of other ways to implement this, but no any other good way without some kind of callback mechanism. IMHO it is the most natural way to create a decoupled implementation.
A basic problem I run into quite often, but ever found a clean solution to, is one where you want to code behaviour for interaction between different objects of a common base class or interface. To make it a bit concrete, I'll throw in an example;
Bob has been coding on a strategy game which supports "cool geographical effects". These round up to simple constraints such as if troops are walking in water, they are slowed 25%. If they are walking on grass, they are slowed 5%, and if they are walking on pavement they are slowed by 0%.
Now, management told Bob that they needed new sorts of troops. There would be jeeps, boats and also hovercrafts. Also, they wanted jeeps to take damage if they went drove into water, and hovercrafts would ignore all three of the terrain types. Rumor has it also that they might add another terrain type with even more features than slowing units down and taking damage.
A very rough pseudo code example follows:
public interface ITerrain
{
void AffectUnit(IUnit unit);
}
public class Water : ITerrain
{
public void AffectUnit(IUnit unit)
{
if (unit is HoverCraft)
{
// Don't affect it anyhow
}
if (unit is FootSoldier)
{
unit.SpeedMultiplier = 0.75f;
}
if (unit is Jeep)
{
unit.SpeedMultiplier = 0.70f;
unit.Health -= 5.0f;
}
if (unit is Boat)
{
// Don't affect it anyhow
}
/*
* List grows larger each day...
*/
}
}
public class Grass : ITerrain
{
public void AffectUnit(IUnit unit)
{
if (unit is HoverCraft)
{
// Don't affect it anyhow
}
if (unit is FootSoldier)
{
unit.SpeedMultiplier = 0.95f;
}
if (unit is Jeep)
{
unit.SpeedMultiplier = 0.85f;
}
if (unit is Boat)
{
unit.SpeedMultiplier = 0.0f;
unit.Health = 0.0f;
Boat boat = unit as Boat;
boat.DamagePropeller();
// Perhaps throw in an explosion aswell?
}
/*
* List grows larger each day...
*/
}
}
As you can see, things would have been better if Bob had a solid design document from the beginning. As the number of units and terrain types grow, so does code complexity. Not only does Bob have to worry about figuring out which members might need to be added to the unit interface, but he also has to repeat alot of code. It's very likely that new terrain types require additional information from what can be obtained from the basic IUnit interface.
Each time we add another unit into the game, each terrain must be updated to handle the new unit. Clearly, this makes for a lot of repetition, not to mention the ugly runtime check which determines the type of unit being dealt with. I've opted out calls to the specific subtypes in this example, but those kinds of calls are neccessary to make. An example would be that when a boat hits land, its propeller should be damaged. Not all units have propellers.
I am unsure what this kind of problem is called, but it is a many-to-many dependence which I have a hard time decoupling. I don't fancy having 100's of overloads for each IUnit subclass on ITerrain as I would want to come clean with coupling.
Any light on this problem is highly sought after. Perhaps I'm thinking way out of orbit all together?
Terrain has-a Terrain Attribute
Terrain Attributes are multidimensional.
Units has-a Propulsion.
Propulsion is compatible able with Terrain Attributes.
Units move by a Terrain visit with Propulsion as an argument.
That gets delegated to the Propulsion.
Units may get affected by terrain as part of the visit.
Unit code knows nothing about propulsion.
Terrain types can change w/o changing anything except Terrain Attributes and Propulsion.
Propuslion's constructors protect existing units from new methods of travel.
The limitation you're running into here is that C#, unlike some other OOP languages, lacks multiple dispatch.
In other words, given these base classes:
public class Base
{
public virtual void Go() { Console.WriteLine("in Base"); }
}
public class Derived : Base
{
public virtual void Go() { Console.WriteLine("in Derived"); }
}
This function:
public void Test()
{
Base obj = new Derived();
obj.Go();
}
will correctly output "in Derived" even though the reference "obj" is of type Base. This is because at runtime C# will correctly find the most-derived Go() to call.
However, since C# is a single dispatch language, it only does this for the "first parameter" which is implicitly "this" in an OOP language. The following code does not work like the above:
public class TestClass
{
public void Go(Base b)
{
Console.WriteLine("Base arg");
}
public void Go(Derived d)
{
Console.WriteLine("Derived arg");
}
public void Test()
{
Base obj = new Derived();
Go(obj);
}
}
This will output "Base arg" because aside from "this" all other parameters are statically dispatched, which means they are bound to the called method at compile time. At compile time, the only thing the compiler knows is the declared type of the argument being passed ("Base obj") and not its actual type, so the method call is bound to the Go(Base b) one.
A solution to your problem then, is to basically hand-author a little method dispatcher:
public class Dispatcher
{
public void Dispatch(IUnit unit, ITerrain terrain)
{
Type unitType = unit.GetType();
Type terrainType = terrain.GetType();
// go through the list and find the action that corresponds to the
// most-derived IUnit and ITerrain types that are in the ancestor
// chain for unitType and terrainType.
Action<IUnit, ITerrain> action = /* left as exercise for reader ;) */
action(unit, terrain);
}
// add functions to this
public List<Action<IUnit, ITerrain>> Actions = new List<Action<IUnit, ITerrain>>();
}
You can use reflection to inspect the generic parameters of each Action passed in and then choose the most-derived one that matches the unit and terrain given, then call that function. The functions added to Actions can be anywhere, even distributed across multiple assemblies.
Interestingly, I've run into this problem a few times, but never outside of the context of games.
decouple the interaction rules from the Unit and Terrain classes; interaction rules are more general than that. For example a hash table might be used with the key being a pair of interacting types and the value being an 'effector' method operating on objects of those types.
when two objects must interact, find ALL of the interaction rules in the hash table and execute them
this eliminates the inter-class dependencies, not to mention the hideous switch statements in your original example
if performance becomes an issue, and the interaction rules do not change during execution, cache the rule-sets for type pairs as they are encountered and emit a new MSIL method to run them all at once
There's definitely three objects in play here:
1) Terrain
2) Terrain Effects
3) Units
I would not suggest creating a map with the pair of terrain/unit as a key to look up the action. That is going to make it difficult for you to make sure you've got every combination covered as the lists of units and terrains grow.
In fact, it appears that every terrain-unit combination has a unique terrain effect so it's doubtful that you'd see a benefit from having a common list of terrain effects at all.
Instead, I would have each unit maintain its own map of terrain to terrain effect. Then, the terrain can just call Unit->AffectUnit(myTerrainType) and the unit can look up the effect that the terrain will have on itself.
Old idea:
Make a class iTerrain and another
class iUnit which accepts an argument
which is the terrain type including a
method for affecting each unit type
example:
boat = new
iUnit("watercraft") field = new
iTerrain("grass")
field.effects(boat)
ok forget all that I have a better idea:
Make the effects of each terrain a property of each unit
Example:
public class hovercraft : unit {
#You make a base class for defaults and redefine as necessary
speed_multiplier.water = 1
}
public class boat : unit {
speed_multiplier.land = 0
}