Explogine is NotExplosive's wrapper around MonoGame to abstract away MonoGame's paradigms into something that's a bit nicer to work with [citation needed].
Explogine is still under development. At time of writing I use Explogine regularly and make frequent additions to it as I discover features that are missing. That being said, it already has a lot of features that are very cool and worth having.
- Development Screenshots on every build
- You can turn this off by passing
--skipSnapshotinto the command line args of your project. - This only affects debug builds
- Upon running your game, explogine automatically takes a screenshot of the current game canvas. It takes more screenshots periodically after that. This lets you create really cool timelapses for game jams.
- You can turn this off by passing
- Demo Recording
- You can record a series of inputs (mouse movements, button presses, etc) and play them back. Useful for debugging!
- If you pass
--demo=recordinto the command line args of your project, you'll start recording a demo - You can then press
CTRL Pin game to save the demo - Then, if you pass
--demo=playbackin a subsequent run, the game will playback your inputs recorded in the demo
- Frame Stepping
- In a debug build you can press
CTRL Spaceto freeze the update loop - You can then scroll down on the scroll wheel to step forward one frame.
- In a debug build you can press
- Crash Handling
- If an exception is ever thrown at runtime, Explogine presents a crash screen reporting the thrown exception. (which beats the alternative of stopping the process)
- This only effects Release builds, so the debug build debugging experience is not effected.
- It also drops a log file that includes everything that was logged to the console that run
- Debug Mode
- Pressing
CTRL SHIFT ~will cycle your debug mode.Off -> Passive -> Active -> Passive - Debug builds starts out as
Passive - Release starts out as
Off Passiveenables debug features like Frame Step and the Developer ConsoleActiveenables more "aggressive" debug features (ie: making collision boxes visible)
- Pressing
If you want to get started with an Explogine project, the easiest way to do that is using the NewProject tool. You can just manually import all the csproj and shproj files into a solution and work from there. But NewProject gets you setup quickly.
- Install .NET 6 make sure that
dotnet --versionreports6.0.xxx - Clone this repo (for this example we'll say it's cloned to
D:\dev\explogine) - Enter a terminal in the folder you want to create your project in (we'll put our terminal at
D:\dev, with the intention to createD:\dev\MyProject) - Run
dotnet run --project D:\dev\csharp\explogine\Tools\NewProject\NewProject.csproj -- --name=MyProject- This builds Explogine's
NewProjectproject, and then invokes it with the args:--name=MyProject
- This builds Explogine's
- Open the newly created sln (in this example that would end up at
D:\dev\MyProject\MyProject.sln) - Open
Program.cs - Create a class called
MyCartridgethat derives fromICartridgeand replace theBlankCartridgewith it.
Bootstrap.Run(args, new WindowConfig(config), new BlankCartridge());
^^^^^^^^^^^^^^
Bootstrap.Run(args, new WindowConfig(config), new MyCartridge());
- Implement
ICartridgeby doing the following:CartridgeConfig => new()<-- Feel free to put parameters here, but empty is fine- Leave
OnCartridgeStartedandUpdateblank ShouldLoadNextCartridge() => false;Drawshould look like the following:
public void Draw(Painter painter)
{
// 1
painter.BeginSpriteBatch(SamplerState.LinearWrap);
// 2
painter.DrawStringWithinRectangle(
// 3
Client.Assets.GetFont("engine/console-font", 40),
"Hello world!",
new Rectangle(
Point.Zero,
// 4
Client.Window.RenderResolution),
// 5
Alignment.Center,
// 6
new DrawSettings {Color = Color.White}
);
// 7
painter.EndSpriteBatch();
}- We cannot draw anything until we tell the
Painterto Begin its spriteBatch. This is basically 1:1 with MonoGame'sSpriteBatch.Beginonly with fewer parameters. Painterhas a number of convenient methods for drawing. In this case, we're usingDrawStringWithinRectanglewhich renders some text that is linebroken within a rectangle. In this case we're providing a giant rectangle (the whole viewable space!) and then usingAlignmentto center it within that rectangle.- We obtain the
engine/console-fontFont fromClient.Assets, this is guaranteed to be available because explogine uses it for rendering the developer console and the loading screen - We ask
Client.Windowfor the render resolution. This gives us back the currentRenderResolutionfor the cartridge. If you customized it inCartridgeConfig, you'll get back the value you put in. Not thatRenderResolutionis not the same asClient.Window.Sizewhich is the size of the window. Alignmentis a struct that behaves like an enum.- All of the
Painter's draw methods take aDrawSettings, this is a property bag of optional parameters. Often times you can leave this blank and get an OK result, the intended use to is to call it using the{}braced initialization to pick and choose the exact values you want. - We have to end the
SpriteBatchbefore anything renders, just like MonoGame normally works.
Cartridges are an abstraction introduced by Explogine to make it easier to load several distinct "Game-Type-Things" in sequence. By default, there are 3 cartridges loaded in Explogine:
[Loading Screen Cartridge] -> [Intro Screen Cartridge] -> [Your Cartridge]
- Upon startup, we query each cartridge for any command line parameters it wants to register, and we queue up any
LoadEventsthey want to register. - We parse the registered command lines against the provided
args[]by the user and set them aside. - Then we load the
[Loading Screen Cartridge]which executes all loading events (this includesContentloading).- See
LoadingCartridge.csfor details
- See
- Once loading is finished
[Loading Screen Cartridge]yields control over to the next cartridge in the chain,[Intro Screen Cartridge] [Intro Screen Cartridge]is an optional cartridge that displaysNotExplosive.netin a randomly selected animation. You're free to remove this if you don't want it.- We then load
[Your Cartridge], which is the cartridge you provided to theBootstrap.Runmethod.
A "Cartridge" just needs to implement ICartridge, so in short, you just need to implement ICartridge and pass the cartridge into Bootstrap.Run and the following happens for free:
- Your
CartridgeConfigwill be set upon loading your cartridge.- At time of writing CartridgeConfig just contains
RenderResolution
- At time of writing CartridgeConfig just contains
OnCartridgeStartedwill be run right after settingCartridgeConfig, it will be run exactly once.Updatewill run every frameDrawwill be run every frame, just after Update (same as MonoGame normally does it)ShouldLoadNextCartridgewill pop this cartridge and load the next one in the chain when true.- Unless you're actually taking advantage of the
Cartridgesubsystem, you should always returnfalsefromShouldLoadNextCartridge
- Unless you're actually taking advantage of the
Cartridges have two interfaces they can optionally implement to get more functionality.
ILoadEventProviderrequires implementing a method that returns anIEnumerable<LoadEvent?>. Each LoadEvent yielded will be executed at the loading screen. It will be made available inClient.Assetsunder the name you provided in theLoadEventICommandLineParameterProviderrequires implementing a method that takes anCommandLineParametersWriter, with this object you can register command line parameters that you can use with the format--parameterName=value. If you register a parameter in this method, it will be availble inClient.Args.GetValue<T>(parameterName).- Only string, int, float, and bool are supported parameter types.
- If you invoke a parameter without an equals sign (eg:
--parameterName) it assumes this is a boolean value and it parsed as--parameterName=true - Command line parameter names are case insensitive (eg:
--parameterNameis the same is--parametername)
Note: you can also derive your Cartridge from the abstract class BasicGameCartridge to implement these for free (it also implements ShouldLoadNextCartridge for you)
One of the most notorious features of MonoGame is the Content Pipeline. Explogine attempts to abstract away the need for the Content Pipeline tool by building your mgcb file for you. This means I make some assumptions about what you want your Content to look like and I might not get it right (also my tooling is incomplete). So you might still be better off using that awful pipeline GUI.
That being said, once your build-time content is setup, Explogine does load it for you and put it in a convenient place.
- If you used the
NewProjectquickstart guide above, you should have a folder calledAssetswhich contains a folder calledContent - Say inside this folder you have a png file at a path like
.\characters\smily.pngor./characters/smily.png - Assuming
smily.pngis included in yourContent.mgcb(which will happen automatically if you runrebuild_content.batat the root of your project) it will be available in your project withClient.Assets.GetTexture("characters/smily")- The path must be with
/forward slashes, and the extension gets removed.
- The path must be with
- This will be a similar story for other MonoGame supported Content types, namely
SoundEffectandSoundEffectInstance. SpriteFontsare handled a little differently, you'll useGetFontwhich will give you aFont, a wrapper aroundSpriteFontthat can be scaled to any size.- The intended use case here is you create a
SpriteFontwith a very large font size and then scale it down when you use it... because SpriteFont rendering is terrible and this is the only way to get something that looks decent.
- The intended use case here is you create a
- Explogine supports a number of other
Assettypes (and you can implement your own!). You'll want to useClient.Assets.GetAsset<T>()to obtain any of those.
Explogine has one big Singleton object called the Client. Normally I don't like using singleton but MonoGame already makes the assumption that there is only ever one Game so we may as well codify it.
Client is the only Singleton (at least the only one you need to be aware of). Every way you interface with Explogine is through the Client. The main things you'll probably want from Client:
Client.Input- Use.Keyboard.Mouseand.GamePadto obtain the state of any button, trigger, or pointer.Client.Assets- Asset Library, includes content and other loaded assets.Client.Graphics- Global Graphics APIs (also gives you access to thePainter, but you should probably access the Painter via passing down fromDraw)Client.FileSystem- Use to access the FileSystem, you can, of course, use other means, but Client.FileSystem is meant to be platform agnostic, meaning it will eventually work on Android and iOS with the same API.Client.Args- Use to access the command line args and their values. Must be setup via cartridge implementingICommandLineParameterProviderClient.Random- Clean, seeded, noise-based randomness