I have a Unity + Zenject setup with a ProjectInstaller with some global dependencies that adhere to a "modal" interface, e.g.,
public class ProjectInstaller : MonoInstaller {
public override void InstallBindings() {
Container.Bind<ModalManager>().AsSingle();
Container.Bind<Modal>().To<DialogManager>().AsSingle();
}
}
Some modals are only relevant to certain scenes, so I bind those in the SceneInstaller:
public class SceneInstaller : MonoInstaller {
public override void InstallBindings() {
Container.BindInterfacesAndSelfTo<InventoryManager>()
.FromComponentInNewPrefab(InventoryPrefab)
.AsSingle()
}
}
I want to manage all modals from the single ModalManager, defined at the project scope. So it has a List<Modal> binding:
public class ModalManager : MonoBehaviour {
[Inject]
protected List<Modal> _modals;
}
When I run this, the ModalManager only gets a single modal: the one defined in the project scope. In my understanding the SceneContext is a subcontainer of the ProjectContext. So I should be able to use FromSubContainerResolve in the ProjectInstaller to bind items in the child scene, perhaps by adding a line like:
// ProjectInstaller.cs
public override void InstallBindings() {
// ...
Container.Bind<Modal>().To<InventoryManager>().FromSubContainerResolve();
}
But I'm not sure which of the eleventy FromSubContainerResolve methods make sense for this case. They all seem pertinent to prefabs with a game object context, not for use from within the ProjectContext.
Does this use case make sense? Is there an easier or better way?
The problem is that that ModalManager can only be injected with dependencies that are added directly to ProjectContext. For these kinds of problems I recommend using the following pattern:
public interface IModal
{
}
public class ModalManager
{
private readonly List<IModal> _modals = new List<IModal>();
public IReadOnlyList<IModal> Modals
{
get { return _modals; }
}
public void AddModal(IModal modal)
{
_modals.Add(modal);
}
public bool RemoveModal(IModal modal)
{
return _modals.Remove(modal);
}
}
public class ModalRegisterHandler : IInitializable, IDisposable
{
private readonly List<IModal> _modals;
private readonly ModalManager _modalManager;
public ModalRegisterHandler(
// We need to use InjectSources.Local here, otherwise we will
// add any project context modals again in each scene
[Inject(Source = InjectSources.Local)]
List<IModal> modals, ModalManager modalManager)
{
_modals = modals;
_modalManager = modalManager;
}
public void Initialize()
{
foreach (var modal in _modals)
{
_modalManager.AddModal(modal);
}
}
public void Dispose()
{
// We don't want ModalManager to retain references to Modals defined in unloaded scenes
// (dispose is executed on scene unload)
foreach (var modal in _modals)
{
_modalManager.RemoveModal(modal);
}
}
}
public class SceneInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<IModal>().To<FooModal>();
Container.Bind<IModal>().To<BarModal>();
}
}
public class ProjectInstaller : MonoInstaller
{
public override void InstallBindings()
{
// We use CopyIntoDirectSubContainers so that ModalRegisterHandler gets automatically added to every
// scene context
Container.BindInterfacesTo<ModalRegisterHandler>().AsSingle().CopyIntoDirectSubContainers();
Container.Bind<ModalManager>().AsSingle();
Container.Bind<IModal>().To<QuxModal>();
Container.Bind<IModal>().To<FizzModal>();
}
}
Related
I am trying to use virtual and abstract methods to make my game architecture better.
I'm using C# and Unity for this example.
I use a ShipComponent as a base Class because I want all the child classes to do the same thing.
But sometimes I want a certain ShipComponent to do something else.
The code will make it a lot clearer:
ShipComponent.cs:
public abstract class ShipComponent : MonoBehaviour
{
[HideInInspector] public ShipControl shipControl;
public virtual void Init(ShipControl control)
{
this.shipControl = control;
}
public virtual void IsPlayer()
{
SetListeners();
}
public abstract void IsNotPlayer();
public abstract void ReEnable();
public abstract void SetListeners();
}
One of the many child classes that inherits from ShipComponent:
public class Rudder : ShipComponent
{
[Header("Settings")]
public Transform rudder;
[Header("Debug Info")]
[SerializeField] float rudderSpeed;
[SerializeField][Range(-45, 45)] int setRudderAngle = 0;
[SerializeField][Range(-45f, 45f)] float realRudderAngle = 0f;
public override void Init(ShipControl shipControl)
{
base.Init(shipControl);
rudder = transform.GetChild(0).GetChild(4);
StartCoroutine(SmoothRudderChange());
SetListeners();
}
public override void IsPlayer()
{
base.IsPlayer();
}
public override void IsNotPlayer()
{
PlayerShipControl.OnRudderChange -= SetRudder;
}
public override void ReEnable()
{
StartCoroutine(SmoothRudderChange());
SetListeners();
}
public override void SetListeners()
{
PlayerShipControl.OnRudderChange -= SetRudder;
if (!shipControl.shipWrapper.ship.IsPlayer) return;
PlayerShipControl.OnRudderChange += SetRudder;
}
void OnDisable()
{
PlayerShipControl.OnRudderChange -= SetRudder;
StopAllCoroutines();
}
The main draw back I experience with this, is that I have to copy paste all 5 or 6 methods everytime I create a new ShipComponent class.
It seems messy and theres a lot of repeating code, most of the time the only difference in each ShipComponent is the SetListeners part, and StartCoroutines if any.
Is there a way to dynamically set delegate listeners up?
So I could set them in the base class ShipComponent?
Instead of setting each component individually?
Another script that inherits from ShipComponent for completeness:
public class Guns : ShipComponent
{
IEnumerator mouseAimCycle;
public override void Init(ShipControl shipControl)
{
base.Init(shipControl);
InitCannons();
SetListeners();
}
public override void ReEnable()
{
SetListeners();
}
public override void IsPlayer()
{
base.IsPlayer();
mouseAimCycle = AimCycle();
StartCoroutine(mouseAimCycle);
SetListeners();
}
public override void SetListeners()
{
PlayerShipControl.OnFireGuns -= TryFire;
if (!shipControl.shipWrapper.ship.IsPlayer) return;
PlayerShipControl.OnFireGuns += TryFire;
}
public override void IsNotPlayer()
{
StopCoroutine(mouseAimCycle);
PlayerShipControl.OnFireGuns -= TryFire;
}
void OnDisable()
{
PlayerShipControl.OnFireGuns -= TryFire;
StopAllCoroutines();
}
Calling the ShipComponent virtual and abstract methods:
public class ShipControl : MonoBehaviour
{
// Contains Ship + Cargo + Crew and a ref to this ShipControl
public ShipWrapper shipWrapper { get; private set; }
ShipComponent[] shipComponents;
// Gather all ShipComponents and Initialize them.
public void Start()
{
shipComponents = transform.GetComponents<ShipComponent>();
foreach (ShipComponent comp in shipComponents)
{
comp.Init(this);
}
}
// Call this to check if this is players current ship and set the components accordingly.
public void UpdateIsPlayer()
{
if (!shipWrapper.ship.IsPlayer)
foreach (ShipComponent component in shipComponents)
component.IsNotPlayer();
else
foreach (ShipComponent component in shipComponents)
component.IsPlayer();
}
And PlayerShipControl, which I use for input, broadcasting the input through delegates, and the theory is that only the players currently controlled ship will be listening for this input:
public class PlayerShipControl : MonoBehaviour
{
public static event Action<Transform> SetCamToPlayerShip;
public static event Action SetShipPanelUI;
public static event Action<bool> ToggleAnchorIcon, ToggleFlagIcon, ToggleAutofireIcon, ToggleBoatsIcon;
public static event Action OnFireGuns;
public static event Action<int> OnRudderChange;
public static event Action<int> OnSailStateChange;
public static event Action<bool> OnAllAnchorsCommand;
public static event Action<bool> OnAllBoatsCommand;
bool anchor, flag, autofire, boats;
ShipControl shipControl;
void Update()
{
if (Input.GetKeyUp(KeyCode.W)) // Raise Sails SailState++
{
OnSailStateChange?.Invoke(1);
}
if (Input.GetKeyUp(KeyCode.S)) // Furl Sails SailState--
{
OnSailStateChange?.Invoke(-1);
}
if (Input.GetKey(KeyCode.D))
{
OnRudderChange?.Invoke(1);
}
if (Input.GetKey(KeyCode.A))
{
OnRudderChange?.Invoke(-1);
}
if (Input.GetKeyDown(KeyCode.M))
{
OnRudderChange?.Invoke(0);
}
// Drop All Anchors
if (Input.GetKeyDown(KeyCode.V))
{
anchor = true;
ToggleAnchorIcon?.Invoke(anchor);
OnAllAnchorsCommand?.Invoke(anchor);
}
// Haul All Anchors
if (Input.GetKeyDown(KeyCode.H))
{
anchor = false;
ToggleAnchorIcon?.Invoke(anchor);
OnAllAnchorsCommand?.Invoke(anchor);
}
// Drop All Boats
if (Input.GetKeyDown(KeyCode.B))
{
boats = true;
ToggleBoatsIcon?.Invoke(boats);
OnAllBoatsCommand?.Invoke(boats);
}
// Take In All Boats
if (Input.GetKeyDown(KeyCode.U))
{
OnAllBoatsCommand?.Invoke(false);
// TO DO When all boats are back on deck, boatIcon + boatsBoolFlag should be turned off again.
}
if (Input.GetKeyDown(KeyCode.Space))
{
OnFireGuns?.Invoke();
}
}
}
Its a long string of scripts sometimes though so I have left out all the managers and such.
Ship ship inside shipWrapper.ship is a custom data class that stores the info about the ship, not a Monobehaviour, but it holds a bool called IsPlayer aswell. Nothing else of interest I can think of.
The main draw back I experience with this, is that I have to copy paste all 5 or 6 methods every time I create a new ShipComponent class. It seems messy and there's a lot of repeating code, most of the time the only difference in each ShipComponent is the SetListeners part, and StartCoroutines if any.
In the show example you have more differences between implementations then ones described. Without seeing the full code it is hard to suggest something meaningful.
Few notes on the current code:
In Rudder you don't need to specify IsPlayer because the following:
public override void IsPlayer()
{
base.IsPlayer();
}
does not add anything extra, so you can just skip implementation in the derived class.
Based on provided examples it seems that ReEnable can be defined as virtual in base class with default implementation set to calling SetListeners (the same approach as you have with Init and IsPlayer).
PlayerShipControl.Update possibly can be improved by moving handlers to dictionary. Something along this lines:
public class PlayerShipControl : MonoBehaviour
{
// ...
Dictionary<KeyCode, Action> keyActions = new() // not sure about the type
{
{ KeyCode.W, () => OnSailStateChange?.Invoke(1) },
// ...
{ KeyCode.V, () =>
{
anchor = true;
ToggleAnchorIcon?.Invoke(anchor);
OnAllAnchorsCommand?.Invoke(anchor);
}
},
// ...
};
void Update()
{
foreach (var kvp in keyActions)
{
if (Input.GetKeyUp(kvp.Key))
{
kvp.Value();
break;
}
}
}
}
I'm looking to implement a certain behavior but I'm not sure how to implement it.
Given a base class :
public class Base
{
void Start() { }
void Update() { }
}
And these two classes which inherit it.
public class Behavior1 : Base
{
private int member;
void Start() { member = 0; }
void Update() { member++; }
}
public class Behavior2 : Base
{
private string name;
void Start() { name = "some string"; }
void Update() { if(name) { Console.WriteLine(name) } }
}
And then a final class which I wish to inherit the logic of the two sub classes.
public class Child : Base // ? Behavior1, Behavior2
{
void Start() { } // logic and members implemented but don't need to be referenced
void Update() { }
}
How would I go about having the Child class implement the two Behavior classes? I don't think you can inherit more than one class at a time so I can't do that. Is there another construct which can accomplish this?
Wihtout enter to valorate the inheritance, that probably need some think as you can read in the comments, you can do something like this if you want use both behaviors ni a class that doesn't inherith them:
public class Child : Base
{
private readonly Behavior1 _behavior1;
private readonly Behavior2 _behavior2;
public Child()
{
this._behavior1 = new Behavior1();
this._behavior2 = new Behavior2();
}
public override void Start()
{
this._behavior1.Start();
}
public override void Update()
{
this._behavior2.Update();
}
}
You can also inherith from Behavior1 and only add Behavior2 as a field:
public class Child : Behavior1
{
private readonly Behavior2 _behavior2;
public Child()
{
this._behavior2 = new Behavior2();
}
public override void Update()
{
this._behavior2.Update();
}
}
But, as I said, is probably that you find a better solution thinking about your models and their composition/inheritance.
I have PhotoBase class
public abstract class PhotoBase
{
public string Path { get; set; }
}
And I have multiple derived classes, for example the path may indicate a location in the file system or an external url.
public class FilePhoto : PhotoBase {}
public class ExternalPhoto : PhotoBase {}
I want to load these photos, I have a PhotoLoader class like below:
public class PhotoLoader
{
public void Load(FilePhoto Photo)
{
// get the photo from file system
}
public void Load(ExternalPhoto Photo)
{
// download the photo from path
}
}
Now I want to load these photos, so I have to do:
public class PhotoImporter
{
private PhotoLoader _photoLoader;
public PhotoImporter(PhotoLoader photoLoader)
{
_photoLoader = photoLoader;
}
public void ImportPhoto(PhotoBase photo)
{
if (photo is FilePhoto)
{
_photoLoader.Load(photo as FilePhoto);
}
if (photo is ExternalPhoto)
{
_photoLoader.Load(photo as ExternalPhoto);
}
}
}
I have several derived classes and I may add more photo types in the future. Is there a more elegant way that I could get rid of if conditions? Using factory pattern?
Another approach would be to have an abstract method Load on PhotoBase, that's then implemented by each subclass:
public abstract class PhotoBase
{
public string Path { get; set; }
public abstract void Load();
}
public class FilePhoto : PhotoBase
{
public override void Load()
{
// load from file system
}
}
public class ExternalPhoto : PhotoBase
{
public override void Load()
{
// load from path
}
}
That way, you can simply call the Load method on a PhotoBase:
public class PhotoImporter
{
public void ImportPhoto(PhotoBase photo)
{
photo.Load();
}
}
The main advantage is that each subclass implements the Load method, and therefore you can add as many subclasses as you want, without worrying about forgetting to implement it.
The main drawback to this implementation is that the Load methods can't depend on other dependencies. So for example, if one day you need to load a photo from a database, you can't pass the DBContext.
Proper to way to implement it which will make code easy to maintain is as follows:
public abstract class PhotoBase
{
public string Path { get; set; }
public abstract void Load(); // Have a abtract method
}
public class FilePhoto : PhotoBase {
public override void Load() { // Implement the abtract method
Console.WriteLine("FilePhoto");
}
}
public class ExternalPhoto : PhotoBase {
public override void Load() { // // Implement the abtract method
Console.WriteLine("ExternalPhoto");
}
}
public class PhotoLoader
{
private PhotoBase _PhotoBase;
public PhotoLoader(PhotoBase photoBase) { // Resolved by Dependency Injection
_PhotoBase = photoBase;
}
public void Load()
{
_PhotoBase.Load();
}
}
public class PhotoImporter
{
private PhotoLoader _photoLoader;
public PhotoImporter(PhotoLoader photoLoader) // Resolved by Dependency Injection
{
_photoLoader = photoLoader;
}
public void ImportPhoto()
{
_photoLoader.Load();
}
}
You need to have Dependency Injection define so that specified implementation will get resolved and appropriate 'Load' method will be invoked.
void Main()
{
// Following dependency should be resolved by Dependency Injection
PhotoBase filePhotoBase = new FilePhoto();
PhotoLoader filePhotoLoader = new PhotoLoader(filePhotoBase);
PhotoImporter filePhotoImporter = new PhotoImporter(filePhotoLoader);
PhotoBase externalPhotoBase = new ExternalPhoto();
PhotoLoader externalPhotoLoader = new PhotoLoader(externalPhotoBase);
PhotoImporter externalPhotoImporter = new PhotoImporter(externalPhotoLoader);
filePhotoImporter.ImportPhoto(); // Shows output 'FilePhoto'
externalPhotoImporter.ImportPhoto(); // Shows output 'ExternalPhoto'
}
You can have as much implementation as you want, but you need to have proper instance of PhotoImporter by Dependency Injection. Everything else will work smoothly.
This is how I could do this:
public class PhotoImporter
{
private PhotoLoader _photoLoader;
public PhotoImporter(PhotoLoader photoLoader)
{
_photoLoader = photoLoader;
}
public void ImportPhoto(PhotoBase photo)
{
var childType = photo.GetType();
dynamic childPhoto = Convert.ChangeType(photo, childType);
_photoLoader.Load(childPhoto);
}
}
You can simplify your ImportPhotoby using pattern matching:
public void ImportPhoto(photoBase photo)
{
switch(photo)
{
case FilePhoto filePhoto:
loader.Load(filePhoto);
break;
case ExternalPhoto externalPhoto:
loader.Load(externalPhoto);
break;
}
}
But this is only syntactic sugar and doesn't solve your real problem. What might help is a abstract Load method in your base class:
public abstract class PhotoBase
{
public string Path { get; set; }
public abstract void Load();
}
You have to implement this method in your child classes. If you call the method, the correct implementation will be chosen.
Online demo: https://dotnetfiddle.net/LfkxBQ
Here's some pseudo code to illustrate what I'm looking at.
public class Loader
{
public Execute()
{
var currentPage = new ItemPageDocumentBuilder();
while(reader.Read())
{
currentPage.Add(reader.XmlDoc);
}
}
private class ItemsToLoad
{
private XmlDocument _page
public void Add(XmlElement itemelement)
{
_page.DocumentElement.AppendChild(itemElement);
}
}
}
I need to derive a class from Loader, and then override the Add method of the ItemsToLoad class inside it, and then call base.Execute(). In other words I want the Execute() method of my derived class to be exactly the same as that of Loader, but to use the overridden Add method of ItemsToLoad to to its work.
I suspect the neatest way to do this would be to remove ItemsToLoad from inside Loader, and make it abstract, correct?
If I couldn't do that, out of interest, what's the best solution?
If I understand your requirement, you have two responsabilities: executing something (which is always the same), and adding something (which differs).
I would do it much simpler, without inheritance and inner classes.
For the adding task, you define an interface:
public interface IItemAdder
{
void Add();
}
And one ore more implementations:
public class ItemAdder1 : IItemAdder
{
public void Add()
{
// specific implementation here
}
}
Then, you have a Loader, in which you inject a specific instance of item adder:
public class Loader : ILoader
{
private IItemAdder _itemAdder;
public Loader(IItemAdder itemAdder)
{
_itemAdder = itemAdder;
}
public void Execute()
{
// use injected item adder to do work
_itemAdder.Add();
}
}
public interface ILoader
{
void Execute();
}
And so usage is:
var loader = new Loader(new ItemAdder1());
loader.Execute();
This way everything is injected, can be replaced and mocked easily; and you clearly separate concerns.
Here is a suggestion (Syntax might not be correct though):
public class Loader
{
ItemsToLoad item;
public Loader(ItemsToLoad item) {
this.item = item;
}
public Execute()
{
// do things using item like item.add();
}
}
interface ItemsToLoad
{
void add();
}
class ItemsToLoad1: ItemsToLoad
{
void add(){
// implementation
}
}
class ItemsToLoad2: ItemsToLoad
{
void add(){
// implementation
}
}
And here is how to use them;
ItemsToLoad item;
if (some condition) {
item = new ItemsToLoad1()
} else {
item = new ItemsToLoad2()
}
Loader loader = new Loader(item);
loader.execute();
You can inherit both classes and inject child sub-class object to its parent.
class Loader
{
public void Execute(ItemsToLoad argObj)
{
if(argObj == null)
argObj = new ItemsToLoad();
argObj.Add(19);
}
public class ItemsToLoad
{
public virtual void Add(int a)
{
Console.WriteLine("Reached ItemsToLoad.");
}
}
}
class ChildLoader:Loader
{
public void Execute(ItemsToLoad argObjLoader)
{
if (argObjLoader == null)
argObjLoader = new ChildItemsToLoad();
base.Execute(argObjLoader);
}
class ChildItemsToLoad : Loader.ItemsToLoad
{
public override void Add(int b)
{
Console.WriteLine("Reached ChildItemsToLoad.");
}
}
}
And can start with
ChildLoader obj999 = new ChildLoader();
obj999.Execute(null);
I need to derive a class from Loader, and then override the Add method of the ItemsToLoad class inside it, and then call base.Execute(). In other words I want the Execute() method of my derived class to be exactly the same as that of Loader, but to use the overridden Add method of ItemsToLoad to to its work.
You need to override Loader, not ItemsToLoad. You haven't shown the code that uses ItemsToLoad, so it's difficult to be specific - but at the very least, you would need to override the new ItemsToLoad to point to your subclass. Also, ItemsToLoad is private - meaning you can't use it except from within Loader. As it is now, you'd need a completely rewritten ItemsToLoad and to override every method in Loader that uses ItemsToLoad.
If you control the Loader class, the easiest changes would probably be to abstract out the creating of ItemsToLoad and open up ItemsToLoad so it can be subclassed. Something like:
public class Loader {
private ItemsToLoad Items { get; set; }
protected virtual ItemsToLoad CreateItemsToLoad() {
return new ItemsToLoad();
}
protected class ItemsToLoad {
public virtual void Add() {
}
}
}
public class MyOtherLoader : Loader {
protected override ItemsToLoad CreateItemsToLoad() {
return new MyOtherItemsToLoad();
}
private class MyOtherItemsToLoad : ItemsToLoad {
public override void Add() {
}
}
}
I have two projects: ClientProj and ServerProj, which both share a SharedLibrary containing the basics of my game.
Inside this library I have the class GameObject which is the base class from which many other game items inherit.
Inside GameObject is a SetPosition() method.
Here's my problem: When I run SetPosition() on the client, I wish to add some additional code / override the method completely. The code I wish to add however relates to classes that are only present in the ClientProj namespace, which the SharedLibrary knows nothing about.
Is there any clean way to override or extend the library methods?
Updated: Note that the instances of GameObject and all things that inherit it are defined, contained and handled all within the SharedLibrary namespace. For the most part the ClientProj and ServerProj only handle networking, users and input/output.
You can use the Proxy pattern and have the game objects inherit from the proxy class instead of the real class:
SharedLibrary:
public class GameObject
{
public virtual void SetPosition() { ... }
}
public class DelegatingGameObject : GameObject
{
public GameObject Inner;
public override void SetPosition() { Inner.SetPosition(); }
}
public class Tree : DelegatingGameObject
{
}
ClientLibrary:
class ClientGameObject : GameObject
{
public override void SetPosition()
{
if (isMonday) base.SetPosition();
}
}
var tree = new Tree { Inner = new ClientGameObject() };
tree.SetPosition();
SharedLibrary:
public class GameObject
{
public virtual void SetPosition() { Console.WriteLine("GameObject.SetPosition"); }
public static event Func<GameObject> Factory;
internal static GameObject CreateBase() { var factory = Factory; return (factory != null) ? factory() : new GameObject(); }
}
internal class GameObjectBase : GameObject
{
private readonly GameObject baseGameObject;
protected GameObjectBase() { baseGameObject = GameObject.CreateBase(); }
public override void SetPosition() { baseGameObject.SetPosition(); }
}
internal class Tree : GameObjectBase
{
public override void SetPosition()
{
Console.WriteLine("Tree.SetPosition");
base.SetPosition();
}
}
public static class Game
{
public static void Start()
{
new Tree().SetPosition();
}
}
ClientLibrary:
internal class ClientGameObject : GameObject
{
public override void SetPosition()
{
Console.WriteLine("ClientGameObject.SetPosition Before");
base.SetPosition();
Console.WriteLine("ClientGameObject.SetPosition After");
}
}
internal static class Program
{
static void Main(string[] args)
{
GameObject.Factory += () => new ClientGameObject();
Game.Start();
}
}
Make SetPosition method virtual and use override keyword to override its behaviour in ClientProj.
You can do it virtual in base class, override in derived, and in overriden method call your methods and after base class method.
A psudocode can look like this:
public class GameObject
{
public virtual void SetPosition()
{
//do something here
}
}
public class Derived: GameObject
{
public override void SetPosition()
{
// do something specific to Derived
base.SetPosition(); // CALL BASE CLASS METHOD AFTER
}
}