Tag Archives: Sprite Sheet

Creating a Game in Blazor – Part 3 – Graphics

In this post I started writing a game in Blazor. In this last post, I covered how we could use the keyboard to move an object around, and how we could apply gravity.

In this post, we’re going to refactor, and we’re going to replace the word test with something approximating Willy.

Refactor

Just because we’re writing a game in Blazor is no reason not to use the IoC container in order to better structure the code. I’m not going to cover all of the refactoring here; however, the changes are here.

We’ve added a sub-directory called GameLogic which contains all the relevant classes:

At some point in the future, we may separate this directory into its own project; but for now, we have four classes:

Controls – this handles the user input.

Graphics – this will handle the manipulation of the graphics.

Player – this handles behaviour of the player.

World – this deals with the things such as collisions, gravity, etc.

WorldSettings – these are just a list of variables that control how things move. In the original game, there was a POKE that meant you could jump so high that you went into the next room.

I won’t cover what’s actually in these classes – it’s essentially what was in the main Razor file. I will cover the change to the razor file itself:

@page "/"
@using System.Timers
@using BlazorGame.GameLogic
@inject IPlayer Player
@inject IControls Controls
@inject IWorld World

<div @onkeydown="HandleKeyDown" @onkeyup="HandleKeyUp" @onkeydown:preventDefault 
    style="background-color: #000000; width: 80vw; height: 80vh; margin: auto"
    tabindex="0" @ref="mainDiv">
    <div style="color: white; top: @(Player.Top)px; left: @(Player.Left)px; width: 20px; position: relative">test</div>
</div>

@code {

    private ElementReference mainDiv;    
    private Timer _timer;

    private void HandleKeyUp(KeyboardEventArgs e) =>
        Controls.KeyUp(e.Code);    

    private void HandleKeyDown(KeyboardEventArgs e) =>    
        Controls.KeyDown(e.Code);    

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await mainDiv.FocusAsync();
        }
    }

    protected override Task OnInitializedAsync()
    {
        _timer = new Timer();
        _timer.Interval = 16;
        _timer.Elapsed += TimerElapsed;
        _timer.AutoReset = true;
        _timer.Enabled = true;        

        return base.OnInitializedAsync();
    }

    private void TimerElapsed(Object source, System.Timers.ElapsedEventArgs e)
    {
        Update();
        Draw();
    }

    private void Update()
    {
        World.ApplyPhysics();
    }

    private void Draw() => 
        this.StateHasChanged();    

}

This is much more terse than before, and delegates most of its functionality to the classes that we described above. You’ll see at the top that we @inject those classes into the page.

Finally, the classes are registered in Program.cs:

        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("#app");

            builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
            builder.Services.AddSingleton<IWorld, World>();
            builder.Services.AddSingleton<IPlayer, Player>();
            builder.Services.AddSingleton<IControls, Controls>();

            await builder.Build().RunAsync();
        }

This is by no means the last refactoring that we’ll do, but it’s perhaps the last one that will make it into its own section of a post.

Graphics

For the graphics, I spent a while trying to get various graphics libraries to work cross platform. I finally realised that, not only did I not need a graphics library, but that I’d solved this issue before – well, more or less. The answer was to use CSS to animate the image. The very first step was to add a sprite sheet; which I got from here, and since Jet Set Willy is the same character as manic miner (with just one pixel difference in the hat), I managed to add a sprite sheet:

The next change was to alter the HTML in Game.razor slightly:

<div @onkeydown="HandleKeyDown" @onkeyup="HandleKeyUp" @onkeydown:preventDefault 
    style="background-color: #000000; width: 80vw; height: 80vh; margin: auto"
    tabindex="0" @ref="mainDiv">
    <div style="color: white; top: @(Player.Top)px; left: @(Player.Left)px; width: 16px; height: 17px; overflow: hidden; position: relative">
        <img 
            src="/images/Willy-Sprite-Sheet.png" 
            style="margin: 0 @(Graphics.PlayerOffset)px; transform: scaleX(@(Graphics.PlayerDirection))" />
    </div>
</div>

There’s a few things to unpick here. Let’s start with the interaction between the div and the img. Essentially, we’re using the div as a window into the image; similar to this:

Both the margin and transform are set to bound properties of a new Graphics class, which we’ll come to in a second; but first, let’s see the other change to this file:

    private void Update()
    {
        World.ApplyPhysics();
        if (Player.IsWalking)
        {
            Graphics.CyclePlayer();
        }
    }

This allows us to change the bound variables that we mentioned earlier.

Now that we’ve seen the changes to the main razor file, let’s see the new Graphics class:

    public class Graphics : IGraphics
    {
        private readonly IPlayer _player;
        private int _playerOffset = 0;
        private DateTime _lastUpdate = DateTime.MinValue;

        public Graphics(IPlayer player)
        {
            _player = player;
        }

        public int PlayerOffset => _playerOffset;

        public int PlayerDirection =>
            _player switch
            {
                { IsWalkingLeft: true } => -1,
                { IsWalkingRight: true } => 1,
                _ => 0
            };
        
        public void CyclePlayer()
        {
            if (_lastUpdate.AddMilliseconds(100) > DateTime.Now) return;

            if (_playerOffset > -48)
                _playerOffset -= 16;
            else
                _playerOffset = 0;

            _lastUpdate = DateTime.Now;
        }
    }

This is essentially a utility, or helper class. It encapsulates details about the graphics that are displayed, and uses the Player class to do so. Most of it is relatively self-explanatory, with the possible exception of CyclePlayer which moves the offset that we mentioned earlier no more frequently that every 100ms.

That’s pretty much it; we now have a walking Willy:

What’s Next?

In the next post, we’ll try to add a platform, and some collision logic.

References

https://www.spriters-resource.com/fullview/113060/

https://gunnarpeipman.com/csharp-reading-embedded-files/

https://www.hanselman.com/blog/how-do-you-use-systemdrawing-in-net-core

https://www.davidguida.net/blazor-gamedev-part-11-improved-assets-loading/

https://stackoverflow.com/questions/493296/css-display-an-image-resized-and-cropped

CSS Animations Sprite Sheet

I’ve recently been investigating the prospect of creating a web-site, where an animation (admittedly a cheesy one) is played out when you load the page. My idea was that two sprites would walk to the centre of the screen. I was actually thinking to mimic something like Manic Miner. Initially, I thought I’d probably need to do some messing around with image manipulation in Javascript, but this gave me the idea that I might be able to run the entire thing through CSS.

Sprite Sheet

The first thing that you need to get is something to animate. The principle of a sprite sheet is that you have many images in a single image file. This is simply a speed trick – it’s faster to pull down a single image, and cache it, than to constantly load separate images for each part of the animation.

If you have separate images, then for this post to work for you, you’ll need to combine them into a single sprite sheet. The tool suggested in the video above is Sprite Sheet Packer. I’ve used this before, and it does the job – you can also do this manually (although you would have to be careful about the spacing).

Now that we have a sprite sheet, we can add it to our project; in my case, I’ve put it in wwwroot\assets.

Let’s talk about how we can layout our page, and animate it; we’ll start with the HTML.

HTML

The HTML here is the simplest part: we just want two divs:

<div id="testdiv"></div>
<div id="testdiv2"></div>

That’s all the HTML; everything else is CSS; let’s start with the animation.

CSS

Onto the CSS, which is the crux of the whole thing. Let’s start with the @keyframes. This is the part where you can actually define an animation. In our case, we’ll need three of them: one to move the sprite left, one to move it right, and one to animate it.

Move Left & Right

The animation to move an element on the screen is pretty straightforward, you just tell it where to start, and where to stop:

@keyframes moverightanimation {
    from { 
        left: 10%; 
    }
    to {
        left: calc(50% - 25px);
    }
}

@keyframes moveleftanimation {
    from {
        left: 90%;
    }
    to {
        left: calc(50% + 25px);
    }
}

As you can see, you can start (or stop) at an absolute position (10px for example), or a percentage, or a calculated value.

Animate

The animation is a bit strange; here’s the code:

@keyframes move {
    100% {
        background-position: -72px, 0px;
    }
}

What this is doing is setting the final position to be negative the width of the full sprite sheet (this will only work for horizontal sheets). We’ll then tell it to step through the images in the element itself. Here’s the CSS for one of the divs:

#testdiv {
    position: absolute;
    left: 20%;
    top: 300px;
    width: 24px;
    height: 30px;    
    animation: moverightanimation 4s forwards, move 1s steps(3) infinite;
    background: transparent url('../assets/spritesheet.png') 0 0 no-repeat;    
}

Here, we’re calling multiple animations (moverightanimation and move); for the move, we’re specifying a step – that is, we’re telling it that it needs to get from where it currently is, to 100% over 3 steps, and there are 3 sprites in my sprite sheet, so it will helpfully divide one by the other and come up with a value to increase by each time.

The opposite call does almost the same:

#testdiv2 {
    position: absolute;
    left: 80%;
    top: 300px;
    width: 24px;
    height: 30px;    
    animation: moveleftanimation 4s forwards, move 1s steps(3) infinite;
    background: transparent url('../assets/spritesheet2') 0 0 no-repeat;
}

Summary

As someone who spends as little time as they can messing with UI and CSS, I thought this was a fun little exercise.