It can if the interface is defined in the library project.
Bear in mind that you don’t need to create an interface for the entire GameRoot object, nor does it need to be a single interface. Your object in the library project wants to either do something with, or know something about, your GameRoot project. You can utilize the Interface Segregation Principle in SOLID (The I) to accomplish your goal here.
Your library class wants to do something with that GameRoot object. Whatever that something is, put it on your interface and then, in your game project, have GameRoot satisfy that interface.
I think you’re almost there, just remember that the interfaces that describe the objects that your library project controls need to interact with are defined in the actual library project. Your game project classes that are given to the library object classes need to implement those interfaces.
Many years ago I developed systems using what is now called SOLID principles without object-oriented languages. Now supposedly, C# and .net offers the discipline to make reliable software.
But what I find is that C# is very difficult to work with. If one is very conversant with C# and .net classes then possibly one can design a system that does not require an enormous number of changes. C# and .net do not allow you to design by building.
If you used the .net version 1 classes in the beginning you would have to roll your own to a great extent. Now version 2 arrives and duplicates some of your own classes that might be similar in functionality but the call signatures and interfaces are different. What does one choose to do?
In the time when .net had no collections we wrote our own based on an array. Now we have a plethora of Collections to choose from. Just try and upgrade that code.
As I build my game I find that there is much I didn’t know at the beginning. I started using XNA 2.0 and tried to evolve my game while Microsoft evolved their platform. I had several games that would run on an Xbox and Windows. But then found that I couldn’t run these on the web and had to use Silverlight. Anyway, the woes continued until I was doing nothing but trying to understand the different technologies. Each one pointing out that I should have done things differently in the first place.
Well enough soapboxing. Is there a SOLID game design template? A template that handles all the elements. I have followed the Microsoft XNA Game Studio 3.0 unleashed by Chad Carter. I have tried to convert all the examples to Monogame but am not there yet. But, I think there is a better way to do some of the things.
C# and .net are not very forgiving. If you don’t do it right in the first place you get overwhelmed with changes and it gets worse when you find a class you might like to use then you have to make lots of changes.
It’s not a workaround… but I don’t really know what else to tell you. Your object needs to interact with a thing, you need to define what kinds of things that thing can do. I think you’re very close here but it might be a good idea to sleep on it.
I see. Well, my approach of late has tended to be, “Do I need this?” If the answer is yes, then I add it. If the answer is no, then I don’t… and when that changes, I add it then.
As an example, here is my current interface for a wrapper of MediaPlayer (plays music in MonoGame/XNA). There’s a lot of functionality in MediaPlayer, but I’ve only created the things I need. Later on, if I need something like, say, functionality to manage playing a collection of songs, I’ll just extend my interface and implement that functionality on my wrapper. Since I don’t need it right now though, I don’t bother.
using ArbitraryPixel.Common.Audio.Factory;
using System;
namespace ArbitraryPixel.Common.Audio
{
/// <summary>
/// Represents an object that will play an ISong object.
/// </summary>
public interface ISongPlayer
{
/// <summary>
/// A factory that can create ISong objects.
/// </summary>
ISongFactory Factory { get; }
/// <summary>
/// Whether or not the currently playing song should repeat.
/// </summary>
bool IsRepeating { get; set; }
/// <summary>
/// Whether or not song playback is muted.
/// </summary>
bool IsMuted { get; set; }
/// <summary>
/// The volume, between 0 and 1, to play the song at.
/// </summary>
float Volume { get; set; }
/// <summary>
/// The current position that song playback is at.
/// </summary>
TimeSpan PlayPosition { get; }
/// <summary>
/// Play the supplied song.
/// </summary>
/// <param name="song">The song to play.</param>
void Play(ISong song);
/// <summary>
/// Play the supplied song at, starting at the supplied position.
/// </summary>
/// <param name="song">The song to play.</param>
/// <param name="startPosition">The position to start playback at.</param>
void Play(ISong song, TimeSpan startPosition);
/// <summary>
/// Stop any currently playing song.
/// </summary>
void Stop();
}
}
Here is the implementation…
using ArbitraryPixel.Common.Audio.Factory;
using Microsoft.Xna.Framework.Media;
using System;
#pragma warning disable 1591
namespace ArbitraryPixel.Common.Audio.MonoGame
{
public class MonoGameSongPlayer : ISongPlayer
{
public MonoGameSongPlayer(ISongFactory factory)
{
this.Factory = factory ?? throw new ArgumentNullException();
}
#region ISongManager Implementation
public ISongFactory Factory { get; private set; }
public bool IsRepeating
{
get { return MediaPlayer.IsRepeating; }
set { MediaPlayer.IsRepeating = value; }
}
public bool IsMuted
{
get { return MediaPlayer.IsMuted; }
set { MediaPlayer.IsMuted = value; }
}
public float Volume
{
get { return MediaPlayer.Volume; }
set { MediaPlayer.Volume = value; }
}
public TimeSpan PlayPosition
{
get { return MediaPlayer.PlayPosition; }
}
public void Play(ISong song)
{
MediaPlayer.Play(song.GetWrappedObject<Song>());
}
public void Play(ISong song, TimeSpan startPosition)
{
MediaPlayer.Play(song.GetWrappedObject<Song>(), startPosition);
}
public void Stop()
{
MediaPlayer.Stop();
}
#endregion
}
}
Yes I have something similar for audio in my game. I had put it aside because Monogame windows version did not work. It now does so I can implement my audio.
It seems to me that when you want to add some function you still need to make changes in 3 places. the class where the songs are used, the interface ISongPlayer and MonoGameSongPlayer. I suppose having the interface groups the functionality in one place. I don’t see how it reduces the refactoring. To replace MediaPlayer with another player will still require lots of changes and unless the call signatures are identical the interface will have to be reworked.
In my case, I have an interface in my InputHandler. At some point, I will have to rework the handler to properly accommodate touch input. I still see that this will require a lot of changes to my code in 3 places, the input routines. the interface and where I use the functions. It would be nice to find a portable input handler that is open source.
You’re correct in that I will have to make changes in three places if I want to add functionality. It’s very quick though since I’m adding functionality, not replacing it. Note that I’m not really following explicit Interface Segregation here. I feel that ISongPlayer is contained enough and am ok with making changes to that interface and anybody who implements it. This is a design choice I made.
Replacing MediaPlayer with another implementation is very easy. I can just write a new class that implements the interface and give that as an instance for ISongPlayer instead. I just have to ensure that my new implementation can work with the interface and object structure I’ve defined. I’ve used the names of things as they appear in MonoGame, but it’s really the method calls and properties I’m interested in. Even changing to another player, I would expect to have that functionality.
Interesting that you brought up input handling because I did exactly this. I have an interface, ISurfaceInputHandler. I then have two implementations of this… MouseSurfaceInputHandler and TouchScreenSurfaceInputHandler. I’m currently supporting Windows and Android and, in the entry point for each, I instantiate the appropriate dependency. Since my game interacts only with ISurfaceInputHandler, I don’t really need to worry about anything other than the things ISurfaceInputHandler lets me do.
I actually don’t mind sharing my input handler with you at all if you think it would help here.
Thanks, that would be great. I will have to add the translation code because I use monogame.extended’s boxing viewport adapter. Not a big change though.
I don’t think you’re going to be able to use this as-is, since you might have other applications than I do. For example, I’m writing my game so that it’s compatible with a touch screen in all the controls, which means I code as if I don’t have access to multiple buttons. It simply detects the position of a “touched surface”. I only wrote this because this is all I needed it to do currently
Also keep in mind that one of the core goals of the game I’m working on is to write testable code… specifically, I want to be able to unit test everything. At my previous job I spent the last three years learning to write SOLID code that is unit tested, but through a lot of politics and mixed messages, the way it was being applied wasn’t consistent. I wanted to gain experience in this area so I made it a goal and have been following that. This is why you see everything behind interfaces, even when I don’t actually need there to be an interface. It’s so I can more easily mock it in my unit tests
Anyway, I’d say use this as a starting place and as an example of what we are talking about here. I hope it helps!
(NOTE: You are welcome to just straight up use this as well if you want… I’m a big believer in code sharing so I’m not going to slap a restrictive license on this or anything. Actually, it’s a regret that I didn’t host my project on github. At the time I thought it was a good idea to have private hosting… I’ve since realized that was a silly choice, but I don’t feel like moving my repository.)
In this zip file:
ArbitraryPixel.Common.Input - Interface for input manager, ISurfaceInputManager, and a base class to handle some basic implementation that most implementors will probably need.
ArbitraryPixel.Common.Input.MonoGame - MonoGame implementations of ISurfaceInputManager for Mouse and TouchPanel.
ArbitraryPixel.Common.Input_Tests - Unit tests for ArbitraryPixel.Common.Input.
I downloaded the zip file and created a test solution with 4 projects
ArbitraryPixel.Common.Input
ArbitraryPixel.Common.Input.Monogame
ArbitraryPixel.Common.Input.Tests
GameTest
All with Monogame Windows
When I built the solution it told me that MSTest.TestAdapter was missing so used Nuget to install but the references in .Tests are unresolved.
What do I do now?
I have no idea why that’s not working for you. I just tried adding those projects to an existing solution I had and there were no issues, no missing references. Everything worked and the unit tests popped up. The only thing I had to do was change the build output since those were just straight out of my own environment.
These projects were made with Visual Studio 2017 Community… are you using a different version? Are you able to make Unit Test projects?
My intent here was to provide you with some code to look at, so if you can’t get the projects working, maybe just take a look at the code that’s there.
The implementation of the interfaces are in ArbitraryPixel.Common.Input.MonoGame, which is another project that was included in the zip file.
While I do think unit testing is important for good code stability, it wasn’t really my intent with sending you that. I do think you should learn it, if it’s not something you know already, but also try not to get hung up on it for now. If you do look over it though, I’m using something called NSubstitute (via NuGet), which is a mocking framework. This allows me to create a fake implementation of an interface and define behaviours and expectations on it. In this way, I can ensure that certain calls are made, or certain data is assigned… that kind of thing.
Anyway, you wanted to see an example of SOLID code in a game development environment, so there you go. Those projects also compile to dlls that I include in my game engine library. Both of these are included in my game project itself. My engine library has no knowledge of the implementations, only the interfaces. It’s my game itself that instantiates the implementations and hands them to the engine. This is the exact scenario that we were discussing above.
Thanks, I do have a lot to learn. Will not have a lot of time in the next 3 weeks for game development since I’m going away.
As I mentioned before, I am using Monogame.Extended classes. They appear to adhere to SOLID coding. I say appear because it seems to be a matter of where one segments the code. I have a game component called InputHandler. All user input interacts with the component through an interface. Really multiple interfaces, mouse, keypad, keyboard and touch. The other game components still have to know which device the user is using and which interface to use. Is there a way to make the input handler completely transparent to the game. For example, pointing device could be implemented as a mouse, touch, keypad, roller ball, etc. So the game would not know how the pointing device is implemented. Similarly with the keyboard, on screen or physical.
Monogame.Extended have InputListeners that I have been trying to implement. I use a lot of events and there is often confusion between the polling in the game loop and getting input in the event. Have you run into this? I thought that an InputListener might solve this.
These things take time but as long as you’re having fun and engaging, it’ll come!
Yep absolutely… that’s exactly what I’ve tried to accomplish with the code I’ve given you. You have to abstract your input behind an interface that’s a little more generic, then you can implement that interface with whatever means of input you like. In the code I gave you, I know I’m designing games that should have the same input on both PC and Android, which means I’m pretty constrained to just what the touch pad can provide. My interface only asks for the position of the input and whether or not it is currently touching the screen. On PC I can get this position and whether or not the mouse is down, on Android the surface position will only be available when the surface is touched, but it amounts to the same thing. That same interface can be implemented with a gamepad too, using the analog sticks to control the pointer position and any button to simulate a tap/click.
When you code with interfaces like this, the code that interacts with the interface doesn’t care what device is doing the work, all it cares about is the data that the interface says it will provide. Hopefully that makes sense
I have no experience with MonoGame.Extended… haha all I know is that they apparently use a library I co-wrote a long time ago that does simple primitive 2D graphics, which is kind of interesting Anyway, I have no idea how MonoGame.Extended does but I suspect the polling in the game loop is what generates the input event. You can probably use whichever method you like. As for specific help with using MonoGame.Extended, sorry, I don’t really have anything to offer. All I can tell you is that when I struggle to understand stuff that others have written, I have a tendency to go off and write my own version… not as a replacement, just as a way to gain basic understanding and insight into what they’re doing so I can better understand what’s happening. This is just my personal approach and isn’t for everyone… it just helps me understand what’s happening.