Git Product home page Git Product logo

xelmish's Introduction

Xelmish - XNA + Elmish!

Nuget

Xelmish is a small project that creates an XNA Game loop (via Mono Game) and connects it to the Elmish MVU architecture, via a custom setState method in its own version of the classic Elmish.Program module (Xelmish.Program).

In this way, you can develop games using the excellent Elmish architecture, with all the power of an XNA renderer! You can also convert existing Elmish applications to use Xelmish by rewriting their view functions.

To use Xelmish, the Elmish program must provide a view function that returns a list of 'viewables', functions which take an XNA SpriteBatch. A set of common such functions like colour, image and text are provided in the Xelmish Viewables helper module.

Xelmish is for 2D games (the SpriteBatch object is for drawing textures, not rendering vertices). Hopefully it allows users to develop such games rapidly using the Elm architecture and F#!

Update: Available on Nuget here

Update Update: the project and its samples were recently upgraded to .NET 5. The older, dotnet core 2.2 version can be found in this branch, though its mainly just the framework that changed, not any code effectively.

Update Update Update: the project and its samples are now .NET 6, along with a MonoGame version bump.

Simple Example of Usage

The simplest usage of Xelmish is shown in the first sample, xelmish-first. This sample renders a square to the screen, and allows you to move and resize it with key presses. It doesnt have any loaded assets like textures, fonts or sound, and therefore also doesn't require the monogame content pipeline. Nice and simple.

I have decorated the code of Xelmish-first (what little there is) to give some general notes on Xelmish development, that will hopefully be useful in your own projects.

Once you have processed that, see the samples section below for a guide on the other, progressively more involved samples in the project.

Note: As Xelmish uses monogame, eventually you will have to learn about the monogame content pipeline. I suggest using the Monogame official documentation for this.

Development Info

Xelmish was developed first with Visual Studio Community 2017, then later with Visual Studio Community 2019, on various Windows 10 machines. A Visual Studio solution file is in the root of the project if you wish to build using these IDEs. However, it should be fully compilable from the command line and other IDEs if that is your preference.

It has been built with pure dotnet core 2.2, but has been upgraded without issue to .NET 5. So you will need to have the SDK for .NET 5 in order to compile. Xelmish and its samples have been tested on Windows 10, Mac OSX and Ubuntu 18.

It has been upgraded since to MonoGame 3.8, and the core Xelmish project made cross-platform. As part of this, the solution has been tested using Visual Studio 2022.

NOTE even with .NET 5 and official support for this as of MonoGame 3.8, I still needed to install Net Core 3.1 in order for the content builder to work. Not sure why... kind of annoying. Maybe the next version will fix this.

A note for Linux builders

On Linux the Monogame Content Pipeline may not work by default. If you get mono failure errors, try installing mono-complete, e.g. sudo apt install mono-complete. I was able to compile and run the samples on Ubuntu 18.04 after this without issue.

Note you also need the .NET 5 SDK to be installed on Linux in order to compile Xelmish and the samples.

Samples description

Under /samples, there are numerous projects that use Elmish and Xelmish. These are described below, in their order of complexity.

0. Xelmish-first

The most basic sample, described above. Just a coloured rectangle on the screen with move/resize commands.

1. Simple-Counter

The 'hello world' of Elmish, this sample should be almost identical (except for the Xelmish view) to other counters in other Elmish-* projects

There is also a Windows DX (Direct X API instead of OpenGL) version of this, to demonstrate cross platform support.

2. Sub-Model

An app with two sub components, each containing a counter and a clock. Pretty similar to other samples in Elmish projects, but with Xelmish views

3. Tetris-Clone

The game tetris, implemented using several elmish components for screens, with a relatively simple Xelmish view. Much more involved than prior samples, but still simple enough to follow easily I hope.

4. Space-Invaders-Clone

A clone of 1979's space invaders, though not a hundred percent accurate to the old version. Compared to Tetris, Space Invaders requires a great deal more events, animations and individual entities, so it serves as a good demonstration of how the bulky (compared to direct imperative style) Elmish eventing model performs in such a context.

This is also the first sample that uses audio, with retro beeps and explosions based on game events. Sounds and music are a little complex to handle in the Elmish/Monogame structure, due to their temporal differences from textures, which makes it worth seeing a real world example.

History and Reasoning

Xelmish has been built for the 2019 F# Applied Competition, but also as a replacement architecture for my prior fsharp-gamecore experimental engine.

Update: While Xelmish unfortunately did not win in the competition, my other submission did. Full results here.

While I have successfully built several small games with gamecore, I was finding that as my games increased in complexity the very simplistic model/view architecture in gamecore started to get stretched and warp. Things which were view-specific started to leak into model, and vice versa.

In contrast the battle-tested Elmish model has, so far, proved a pleasure to work with. Much more elegant, and it has also achieved in a far better way my goal of having games being purely functional (where performance permits) and agnostic of engine. The MVU architecture, and parent-child relationships that the Elm architecture handles so well, mean that a game can be designed and theorised without having the engine get in the way, which is (in my opinion) ideal.

License

Xelmish is provided under the MIT license. PLease contact me if you have issue with this. In addition, many if not all of the sample projects use fonts that are provided under the SIL Open Font License, a copy of which is in the root of the solution.

Credits

Everything in this repo has been built solely by me, in line with the requirements of the competition. That said, it references the Elmish library over Nuget, created by @et1975 and others. The ease of use of their library was what inspired the creation of this. I also learnt how to integrate with Elmish via Elmish.WPF, and how to use it well via this excellent article by @MangelMaxime, "My tips for working with Elmish"

xelmish's People

Contributors

adelarsq avatar chrispritchard avatar games avatar rickyycheng avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xelmish's Issues

QuitGame exception does not work as expected via nuget package

Using the quit helper, which throws a specific exception caught by the game loop, causes debuggers to stop on the exception (seen in win 10 via vs and vs code). This only occurs when using Xelmish via Nuget.

It doesn't throw the exception fully (the program doesn't crash) as it will still be caught by the gameloop handler, but evidently the debuggers of vs code and visual studio cant see this and pause.

Not ideal. Will need to investigate if this can be fixed to be more elegant.

Note, reported by @rishavs

[Question] Accessing the Game-object

I'd like to access the MonoGame Game-object, e.g. for retrieving the Content-folder.
Is this not meant to be done? Or is Xelmish just minimalistic in its approach and meant to be extended by its user?

Blank project

What would be the best way to start a blank project in VS 2019?

  • create an empty console app, add nuget package and modify?
  • use the monogame c# templates and go from there?
  • just use the sample app from the repository?

Thanks

Tetris sample has some timing problems

Jumping shapes, multiple spawn events - obvious something has gone iffy there in the time since build and xelmish release. Lots of changes to xelmish processing, so no surprise.

Message to all contributors

This project is awesome !!!!!

Thank for that.
I like the samples with all the comments, it make all the code very understandable👍🏻
Great job.

SpriteSortMode.Immediate control

Xelmish uses SpriteSortBatch.Immediate for its sort mode, which allows the user to do things like switch the sampler state from one texture to the next, i.e. if they want pixel textures drawn using pointclamp, and text drawn using some form of antialiasing.

Despite its name, immediate isn't really immediate in most cases: it does batching. Specifically, it batches by texture, so its basically efficient if you group all draw calls by texture.

This is the way I do most games anyway: all tiles share the same texture, terrain or units tend to be subsets of a single texture etc and again drawn all at once, so its not too much of an issue.

But it might be worth POCing out some sort of alternative: maybe view methods return a construct like (viewable list * sortMode * samplerState) list, then for each Xelmish can create an configure an individual spriteBatch? Hmm.

Reference:

Fixed time step config

Is it possible to make the fixed timestep configurable? I can add it myself just not sure if xelmish relies on the timestep being fixed in some way

Using DrawUserPrimitives with Xelmish

I am not that familiar with Monogame so its possible that there are better ways to do this, but I wanted to draw a hex grid and so needed to use DrawUserPrimitives. I was able to do this without making changes to Xelmish by making getting the GraphicsDevice out of the SpriteBatch and changing some settings on the graphicsDevice and basicEffect, like so:

        let graphicsDevice = spriteBatch.graphicsDevice
        let basicEffect = new BasicEffect(graphicsDevice)

        graphicsDevice.RasterizerState <- RasterizerState.CullCounterClockwise
        graphicsDevice.DepthStencilState <- DepthStencilState.Default;
        basicEffect.World <- Matrix.CreateOrthographicOffCenter(0f, float32 graphicsDevice.Viewport.Width, float32 graphicsDevice.Viewport.Height, 0f, 0f, 1f);
        basicEffect.VertexColorEnabled <- true;

        let centers =
            grid.Hexes
            |> Map.toList
            |> List.map (fun (coor, hex) -> (coor, hexToPixel grid.Orientiation grid.Size grid.Offset hex.Coor))
        
        basicEffect.CurrentTechnique.Passes 
            |> List.ofSeq
            |> List.iter (fun pass -> 
                pass.Apply()
                centers
                |> List.iter (fun (_, (cx, cy)) -> 
                    let points = hexToPoints (cx, cy) { X = grid.Size.X - 2.; Y = grid.Size.Y - 2. }
                    let hexVerts = 
                        points
                        |> List.map (fun (x, y) -> VertexPositionColor(Position = vector3 (float32 x) (float32 y) 0f, Color = Color.Red))
                        |> List.toArray
                    
                    let outlinePoints = hexToPoints (cx, cy) grid.Size
                    let outlineVerts =
                        outlinePoints
                        |> List.map (fun (x, y) -> VertexPositionColor(Position = vector3 (float32 x) (float32 y) 0f, Color = Color.Black))
                        |> List.toArray

                    graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, outlineVerts, 0, 6) 
                    graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, hexVerts, 0, 6) 
                )
            )
        spriteBatch.End() \\end the Xelmish sprite batch
        spriteBatch.Begin(SpriteSortMode.Immediate) \\start another sprite batch
        let font = loadedAssets.fonts.["basic"]
        centers
            |> List.iter (fun (coor, (cx, cy)) -> 
                let coorString = sprintf "(%i, %i)" coor.Q coor.R
                let measure = font.MeasureString(coorString)
                let tx = float32 cx - measure.X / 2f
                let ty = float32 cy - measure.Y / 2f
                spriteBatch.DrawString(font, coorString, vector2 tx ty, Colour.White)
            )
        \\leave the sprite batch unended so that Xelmish can end it

This works, but it seemed like maybe not the best approach, so I created my own GameLoop class and used a slightly different Draw and Viewables definition:

type GraphicsDevice = Microsoft.Xna.Framework.Graphics.GraphicsDevice

type BatchSetup = (LoadedAssets -> Inputs -> GraphicsDevice -> SpriteBatch -> unit)
type BatchContent = (LoadedAssets -> Inputs -> GraphicsDevice -> SpriteBatch -> unit) list
type BatchCleanup = (LoadedAssets -> Inputs -> GraphicsDevice -> SpriteBatch -> unit)

type Drawable = 
    | Sprite of (LoadedAssets -> Inputs -> SpriteBatch -> unit)
    | Graphic of (LoadedAssets -> Inputs -> GraphicsDevice -> unit)
    | Both of (LoadedAssets -> Inputs -> GraphicsDevice -> SpriteBatch -> unit)
    | Batch of BatchSetup*BatchContent*BatchCleanup

type ViewableGraphics = 
    | OnDraw of Drawable
    | OnUpdate of (Inputs -> unit
    override __.Draw gameTime =
        Option.iter this.GraphicsDevice.Clear config.clearColour

        let graphicsDevice = this.GraphicsDevice

        drawables
        |> List.iter (fun drawable ->
            match drawable with
            | Sprite df ->
                spriteBatch.Begin (sortMode = SpriteSortMode.Immediate)
                df assets inputs spriteBatch
                spriteBatch.End ()
            | Graphic df ->
                df assets inputs graphicsDevice
            | Both df ->
                df assets inputs graphicsDevice spriteBatch
            | Batch (setup,content,cleanup) ->
                setup assets inputs graphicsDevice spriteBatch
                content
                |> List.iter (fun df -> df assets inputs graphicsDevice spriteBatch)
                cleanup assets inputs graphicsDevice spriteBatch
        )

Since the Xelmish GameLoop class is marked as internal I couldn't base my GameLoop class off of it so it has to re implement a lot of the work that occurs in GameLoop . That is one possible change, make GameLoop not internal to better support this type of thing.

Not sure if the rest of it really fits in Xelmish but I wanted to surface it in case there is some changes I should make into a PR. I don't think they fit exactly as I have them now, but maybe some variant is worthwhile.

If nothing else, now if anyone else is looking to use DrawUserPrimitives with Xelmish they can reference this.

Running Xelmish on androi

Is it possible to run a Xelmish game on android?

I assume yes, but my attempts so far have not gotten anywhere near successful.

Cross Platform Support?

I found that the xelmish was written by MonoGame.DesktopGL.
When writing multi-platform game(such as UWP, desktopgl, or windowsdx), how should I share the core loop in xelmish?
Can any1 write an example?

Per draw sprite sampler blending?

The spritebatch used for drawing can be initialised with different types of sprite blending. Point clamp makes pixel graphics look great, but ruins text. Default ansiotropic makes text look good, but blurs pixel graphics.

I've added a config prop to define one setting for the app (rather than the earlier pixel hard coding) but it might be worth investigating to see what the difficulty/cost is to do so on a per render option.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.