I'm trying to get my head around making a simplified way of making items, lists and databases in C# And while everything works, It involved me needing to cast the results out.
So far I have the following
namespace Game.Database
{
public abstract class DatabaseItem : ScriptableObject
{
}
public class DatabaseList : ScriptableObject
{
public List<DatabaseItem> items;
}
public class ShipClass : ScriptableObject
{
public string shipClassID;
new public string name;
}
public class Ship : DatabaseItem
{
public string shipID;
new public string name;
public ScriptableObject shipClass;
}
}
public class Database : MonoBehaviour
{
public List<DatabaseList> lists;
void Start()
{
Ship ship = (Ship)lists[0].items[0];
Debug.Log(shipClass.shipID);
ShipClass shipClass = (ShipClass)ship.shipClass;
Debug.Log(shipClass.shipClassID);
}
}
Bear in mind this is a unity project so these items are being instantiated and data being assigned through the UI.
As you can see I have an abstract for my items and will have multiple types of item, and multiple lists. I am trying to avoid having to make multiple class' for my lists, one for each type of item. So i have abstracted my items off DatabaseItem so that I can store a List of DatabaseItem in my DatabaseList. However this means when reading my data out i need to cast this back into a Ship class.
While this isn't bad for a simple implementation, in production these will be nested requiring multiple casts to get down to the required data.
Unfortunately I find myself lacking in the required c# vocabulary to really google the issue. Looking at the Microsoft User-defined conversion operators and their example just doesn't make sense if it's even what i want to achieve.
EDIT -
The issue if not accessing the data, as I can do that, it's having to break down every level of the data as in the end this will be very generic used for all game data and very nested, so having to cast every level out to be able to break it down is what I'm trying to avoid.
One way would be to expose a method on each type of item that does the writing out of the data, so the calling code doesn't need to know the low level details.
See below, on how to avoid doing any casting.
namespace Game.Database
{
public abstract class DatabaseItem : ScriptableObject
{
public abstract void WriteOut();
}
public class DatabaseList : ScriptableObject
{
public List<DatabaseItem> items;
}
public class Ship : DatabaseItem
{
public string shipID;
new public string name;
public override void WriteOut()
{
Debug.Log(shipID);
}
}
}
public class Database : MonoBehaviour
{
public List<DatabaseList> lists;
void Start()
{
lists[0].items[0].WriteOut();
}
}
This way you are allowing each item type to handle its own writing out. Id suggest thinking carefully about your API.
To be even more SOLID and clean, you could use dependency injection and inject the writing into the type, see below for another example.
This has the benefit of allowing multiple types to use the same writer code and you also keep your class following the single responsibility principle.
namespace Game.Database
{
public interface IWriter
{
void Write(string output);
}
public class ConsoleWriter: IWriter
{
public void Write(string output)
{
Debug.Log(output);
}
}
public abstract class DatabaseItem : ScriptableObject
{
public abstract void WriteOut();
}
public class DatabaseList : ScriptableObject
{
public List<DatabaseItem> items;
}
public class Ship : DatabaseItem
{
private IWriter _writer;
public Ship(IWriter writer)
{
_writer = writer;
}
public string shipID;
new public string name;
public override void WriteOut()
{
_writer.Write(shipID);
}
}
}
public class Database : MonoBehaviour
{
public List<DatabaseList> lists;
void Start()
{
lists[0].items[0].WriteOut();
}
}
Something like this should be a good starting point. The specific implementations need to be adapted to your game's needs. More informations about generic types here.
public abstract class Item: ScriptableObject
{
public string name;
public abstract void Use();
}
public class Ship: Item
{
public string id;
public override void Use()
{
Debug.Log($"I'm a Ship, my name is {name}, my id is {id}.");
}
}
public class Plane: Item
{
public float speed;
public override void Use()
{
Debug.Log($"I'm a Plane, my name is {name}, my speed is {speed}.");
}
}
public class Database: ScriptableObject
{
[SerializeField] private List<Item> items;
public T GetItem<T>(int i) { return (T) items[i]; }
public Item AddItem() { ... }
public Item RemoveItem() { ... }
}
public class DatabaseHolder: MonoBehaviour
{
public Database database;
void Start()
{
Ship ship = database.GetItem<Ship>(0);
// Or...
Plane plane = database.GetItem<Plane>(1);
}
}
Related
I ran into the problem of binding components from a child component, a solution to this problem is possible?
At the moment I have a solution, but it is inconvenient, you need to forward all methods through 1 component. Example:
public inteface ITextView
{
void SetText(string text);
}
public ButtonView : Monobehaviour, ITextView
{
[SerializedField] private Text _text;
public void SetText(string text)
{
_text.text = text;
}
}
public SomeWindow : Monobehaviour, IButtonView
{
[SerializedField] private TextView _textView;
public void SetText(string text) => _textView.SetText(text);
}
In this case, with the growth of inherited interfaces, the forwarding of methods grows.
As a possible solution to the problem, it is also possible to simply create an interface that stores references to all dependencies. Example:
public interface ISomeWindowFacade
{
ITextView TextView { get; }
//Some dependence
//Another one
}
But in this case, I will pass unnecessary dependencies to most classes
Is it possible to store links to the required dependencies in SomeWindow and bind the rest after its creation?
public Installer : ScriptableObjectInstaller
{
[SerializedField] private SomeWindow _window;
public override void InstallBindings()
{
Container.BindInterfacesTo<ISomeWindowFacade>.FromComponentInNewPrefab(_window).AsSingle();
}
}
Perhaps I did not state the problem correctly, but here's the gist. There is a SOInstaller that stores a link to the prefab, at the time of the bind I created an instance of this component and wanted to receive the component itself and its children as separate components. I found solutions to this problem:
The implementation of the component must store the MonoBehaviour list, you can make an abstract class that stores a link to the list
public interface IWindowView{}
public ConcreteWindow : MonoBehaviour, IWindowView
{
[SerializedField] private List<MonoBehaviour> _children;
public List<MonoBehavior> Children => _children;
}
In the installer (as in my case in SOInstaller) we add a link to the prefab with the component and instantiate the component and bind it
public class SomeInstaller : ScriptableObjectInstaller
{
[SerializeField] private ConcreteWindow _window;
public override InstallBindings()
{
//If you use an abstract class, you can convert to it
var window = Container.InstatiatePrefabForComponent<ConcreteWindow>(_window);
Container.BindInterfacesTo(window.GetType()).AsSingle();
}
}
Now we can go through all the child elements, inject them and bind
public class SomeInstaller : ScriptableObjectInstaller
{
[SerializeField] private ConcreteWindow _window;
public override InstallBindings()
{
//Previous
foreach(var component in window.Children)
{
Container.Inject(component);
Container.BindInterfacesTo(component.GetType()).AsSingle();
}
}
}
As a result, we get the following, we have created an object that stores the components and can receive them separately in the desired class
public class SomeClass
{
//ISomeView - child implementer
public SomeClass(IWindowView windowView, ISomeView someView)
{
}
}
I am trying to solve a simple OOPs problem. When I have to create few weapons and each weapon has a primary action and that primary action can be performed by a mouse click. Example for a shotgun it is shooting and for Katana is swinging it. Below my classes are.
public interface IShootable
{
void TakeShot();
}
public interface ISwingable
{
void Swing ();
}
public class ShotGun : IShootable
{
public void TakeShot()
{
}
}
public class Kanata : ISwingable
{
public void Swing ()
{
}
}
Each weapon has implemented different interface for their primary actions. (I'm not sure that I can create an abstract class, from which I can inherit these concrete classes. It seems not substitutable for two different type of weapons.)
What I wanted to achieve is in runtime when user selects one of the weapons, the user gets the right action on mouse click. For shotgun it is TakeShot() and for Katana it is Swing().
What I have to do adopt that. Or I should restructure the classes in some other way.
Both classes can both implement a third interface - IWeapon, with an Attack/Use method:
public interface IWeapon {
void Attack();
}
public class ShotGun : IShootable
{
void IWeapon.Attack() {
TakeShot();
}
public void TakeShot()
{
}
}
public class Kanata : ISwingable
{
public void Swing ()
{
}
void IWeapon.Attack() {
Swing();
}
}
Note that I have explicitly implemented the IWeapon interface here. You don't have to do it explicitly, and can still do it the "normal way". but I prefer it this way. This way, when you have a Katana object, only Swing is visible, but not Attack. I just feel that having both methods visible is confusing.
Also note that IWeapon is unrelated to ISwingable and IShootable, because swingable things (tennis racket) and shootable things (water pistol) are not necessarily weapons.
You might want to do something more generic:
public interface IWeaponPrimaryAction
{
void PerformPrimaryAction();
}
public interface IWeaponAction
{
void PrimaryAction();
}
public class Shoot :IWeaponAction
{
public void PrimaryAction()
{
//Fire in the hole
}
}
public class Swing :IWeaponAction
{
public void PrimaryAction()
{
//Into pieces
}
}
public class ShotGun : IWeaponPrimaryAction
{
private IWeaponAction _action = new Shoot();
public void PerformPrimaryAction()
{
_action.PrimaryAction();
}
}
public class Kanata : IWeaponPrimaryAction
{
private IWeaponAction _action = new Swing();
public void PerformPrimaryAction()
{
_action.PrimaryAction();
}
}
Better than that just inject the action:
public class ShotGun : IWeaponPrimaryAction
{
private IWeaponAction _action;
public ShotGun(IWeaponAction action)
{
_action = action;
}
public void PerformPrimaryAction()
{
_action.PrimaryAction();
}
}
My preference would be what #Riki suggests and create an interface that all weapons inherit IWeapon with a single method IWeapon.PerformPrimaryAction(). However if you don't like that you could create a "base" interface that all weapon interfaces derive from, then at runtime use casting, is or as to determine which weapon you have:
public interface IWeapon {};
public interface IShootable : IWeapon
{
void TakeShot();
}
public interface ISwingable : IWeapon
{
void Swing ();
}
public partial class YourGameClass
{
public void DoTheAction (IWeapon weapon)
{
if (weapon is IShootable)
(weapon as IShootable).TakeShot();
if (weapon is ISwingable)
(weapon as ISwingable).Swing();
}
}
I have got two abstract classes: Particle and ParticleHub<T> where T : Particle.
I want, when I inherit Particle, for its constructor to require a ParticleHub of its respective type to be passed as a parameter. I want its constructor to be like this:
ParticleHub</*type of the inheriting class*/> _particleHub;
public Particle(ParticleHub</*type of the inheriting class*/> particleHub, OtherParameters otherParameters)
{
_particleHub = particleHub;
//use other parameters
}
If I write ParticleHub<typeof(this)> I get overloaded with errors that make me think I broke the syntax for the rest of the file. If I do ParticleHub<this.GetType()> I get the same problem, but only with fewer errors. Though with that one, I can understand that I can't use a function in a parameter type like that.
So how do I go about this?
Simple example below of an interface and generic pattern
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
IChair chair = new Chesterfield<Arm>(new Arm());
chair.Sit();
Console.Write(chair.HasSat());
}
}
public interface IChair
{
void Sit();
bool HasSat();
}
public interface IPart
{
}
public class Chesterfield<TPart> : Chair<TPart> where TPart : IPart
{
public Chesterfield(TPart part) => _part = part;
private bool _hasSat;
private readonly TPart _part;
public override void Sit()
{
_hasSat = true;
}
public override bool HasSat() => _hasSat;
}
public abstract class Chair<TPart> : IChair where TPart : IPart
{
public abstract void Sit();
public abstract bool HasSat();
}
public class Arm : IPart
{
}
public class Back : IPart
{
}
public class Leg
{
}
You have no other choise i think but to pass the type as other generic. See the exaple how it is done in terms of fluent builders as I think it is very close what You want to have: Link.
Like:
FePatricle<T, U> where T: Particle, U: FeParticle<T, U>
Looks crazy but it should do the trick in the constructor:
public FeParticle(ParticleHub<U>...
EDIT:
Or more likely as far as I understand:
FePatricle<T>: Particle where T: FeParticle<T>
So FePartivle extends Particle and it carries with itself its own type T?
Passing the inherited class itself, as below.
public abstract class Particle
{
}
public abstract class ParticleHub<T> where T : Particle
{
}
public class k1 : Particle
{
ParticleHub<k1> _particleHub = null;
public k1(ParticleHub<k1> ph)
{
_particleHub = ph;
}
}
In short, I'm hoping to achieve a kind of barebones structure in one place and implement/define in another. I want to better "see" the interconnectedness without all the functionality clouding it up mostly for design discussions, explanations, etc. I could do this with inheritance, but I really don't want to change all the names of everything just to achieve this. Is this a thing somehow?
// Simple File for seeing relationships between classes
public class AllMyObjectTypes // A class because it will be its own object with functionality below all this structural stuff
{
public class Thing1
{
public Thing2[] things2;
public Thing3[] things3;
}
public class Thing2[]
{
public int version;
public Thing1[] thing1Utilizers;
}
public class Thing3[]
{
public string Title;
}
}
// Complicated file for doing all the hard work for Thing1 with all the internal variables to make it happen.
public class Thing1 : Thing1 // Implement itself somehow?
{
// Stuff I want to use and define but not cloud the structure above
private int[] internalStuff;
private string moreInternalStuff;
public void UsefulFunctionButWantSeparated()
{
// Hundreds of lines of code clouding junk up
}
}
Interface & Class declarations
public interface IThing
{
IThing2[] Thing2s();
string DoSomething();
}
public class Thing : IThing
{
private readonly IThing2[] _thing2s = new IThing2[1] { new Thing2() };
public IThing2[] Thing2s() => _thing2s;
public string DoSomething()
{
return "MyText";
}
}
public interface IThing2
{
}
public class Thing2 : IThing2
{
}
Use
IThing thing;
thing = new Thing();
var thing2s = thing.Thing2s();
var txt = thing.DoSomething();
Partial Classes is exactly what I was looking for, but it did require that I don't nest within another class. Unless maybe I made that partial too...? But either way, this gets me closest to my goal
// Simple File for seeing relationships between classes
//public class AllMyObjectTypes // A class because it will be its own object with functionality below all this structural stuff
//{
public partial class Thing1
{
public Thing2[] things2;
public Thing3[] things3;
}
public partial class Thing2[]
{
public int version;
public Thing1[] thing1Utilizers;
}
public partial class Thing3[]
{
public string Title;
}
//}
// Complicated file for doing all the hard work for Thing1 with all the internal variables to make it happen.
public partial class Thing1 // More implementation
{
// Stuff I want to use and define but not cloud the structure above
private int[] internalStuff;
private string moreInternalStuff;
public void UsefulFunctionButWantSeparated()
{
// Hundreds of lines of code [no longer] clouding junk up
}
}
I have the following Classes:
public abstract class Gear<T> : ScriptableObject, IGear { ... }
public class Armor : Gear<ArmorStatsLevel> { ... }
public class Weapon : Gear<WeaponStatsLevel> { ... }
Now I had the following methods to list my instances:
public abstract class WidgetListArmor {
public void ActionSelected(Armor gear) {
if (...) GameSession.Equip(gear);
}
}
public abstract class WidgetListWeapon {
public void ActionSelected(Weapon gear) {
if (...) GameSession.Equip(gear);
}
}
Because this was kind of redundant, I thought of moving it all to a base clase:
public abstract class WidgetListGear<T> : MonoBehaviour {
public void ActionSelected(T gear) {
if (...) GameSession.Equip(gear);
}
}
public class WidgetListArmors : WidgetListGear<Armor> { ... }
public class WidgetListWeapons : WidgetListGear<Weapon> { ... }
And while this seems cleaner, I have a new problem now. Because T is a Generic, GameSession.Equip can't overload gear.
Did I chose a bad pattern to organize my code? Am I missing something from Generics that allows me to do this operation?
UPDATE
Here is the GameSession signatures:
public class GameSession {
public static bool Equip(Armor armor);
public static bool Equip(Weapon weapon);
}
Make Weapon and Armor implement an interface called IGear, for example:
public interface IGear
{ }
public class Weapon : IGear
{
//snip
}
public class Armor : IGear
{
//snip
}
Constrain the generic type to IGear:
public abstract class WidgetListGear<T> : MonoBehaviour
where T : IGear
{
public void ActionSelected(T gear) {
if (...) GameSession.Equip(gear);
}
}
And make GameSession.Equip take IGear as the parameter type.
What you're looking for is dynamic dispatch. I would suggest you try the following:
GameSession.Equip((dynamic)gear);
However, I don't think it's the best idea since you've tried to encode your Game rules in type system and right now you're starting a mini-compiler in runtime to perform a dispatch for you.
I'd like to point you to Eric Lippert's articles on that subject. Looks like you have similar issues with what he's described.
Part 4 describes the dynamic approach I've provided as well as its disadvantages. Part 5 provides a completely different approach. Overall, I highly recommend reading each part.