Tag Archives: Game Development

Creating a Game in Blazor – Part 4 – Platform and Collision

I’ve recently been creating a JetSet Willy clone in Blazor. I use the term clone loosely – I’m actually not creating a clone or anything like it – I’m simply replicating some aspects of the game (moving, jumping, platforms, collision, etc.).

This is the fourth post in the series that started here. In this post, I’m going to add a platform – so far we’ve been walking on the air.

As with previous posts, the code for this can be found here on GitHub.

In order to do this, we’ll need to do a bit of refactoring, and to introduce a crude collision concept. Let’s start with the refactoring and platform display:

We’re introducing the concept of a base GameObject, from which, Platform, Player, and NPC inherit. We’re dispensibg of IPlayer, and instead, adapting World to manage the elements in the world:

    public class World : IWorld
    {
        private readonly IEnumerable<GameObject> _gameObjects;

        public World(IEnumerable<GameObject> gameObjects)
        {
            _gameObjects = gameObjects;
        }

        public Player Player
        {
            get => (Player)_gameObjects.First(a => a.GameObjectType == GameObjectType.Player);
        }

        public IEnumerable<GameObject> Platforms 
        {  
            get => _gameObjects.Where(a => a.
                GameObjectType == GameObjectType.Platform);
        }

        public void ApplyPhysics()
        {   
            foreach (var gameObject in _gameObjects)
            {
                gameObject.Update();
            }            

        }
        
    }

We’re not dealing with NPCs in this post, but we are dealing with platforms. The new Platform class currently looks like this:

    public class Platform : GameObject
    {
        public Platform(int width, int height, int left, int top)
        {
            Width = width;
            Height = height;
            Left = left;
            Top = top;
            GameObjectType = GameObjectType.Platform;
        }

        public override void Update()
        {
            //throw new System.NotImplementedException();
        }
    }

We’ll no doubt come back to this, but essentially, all we’re doing is maintaining a collection of GameObjects with a specific type.

For now, we’ll inject the properties of the World in through Program.cs:

    public class Program
    {
        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>(srv =>
            {
                var platform = new Platform(
                    50, 10, 0, WorldSettings.FLOOR);

                var player = new Player(17, 21, new[] { platform })
                {
                    GameObjectType = GameObjectType.Player,
                };

                var world = new World(new GameObject[] { player, platform });                
                return world;
            });            
            builder.Services.AddSingleton<IControls, Controls>();
            builder.Services.AddSingleton<IGraphics, Graphics>();

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

As you can see, we build the Platform and Player and then build the world. Finally, we adapt Game.razor to display the platforms:

@page "/"
@using System.Timers
@using BlazorGame.GameLogic
@inject IEnumerable<GameObject> GameObjects
@inject IControls Controls
@inject IWorld World
@inject IGraphics Graphics

<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: @(World.Player.Top)px; left: @(World.Player.Left)px; width: @(World.Player.Width)px; height: @(World.Player.Height)px; overflow: hidden; position: relative">
        <img 
            src="/images/Willy-Sprite-Sheet.png" 
            style="margin: 0 @(Graphics.PlayerOffset)px; transform: scaleX(@(Graphics.PlayerDirection))" />
    </div>

    @foreach (var platform in World.Platforms)
    {
        <div style="position: relative; top:@(platform.Top)px; left:@(platform.Left)px; width:@(platform.Width)px; height:@(platform.Height)px; border: 1px solid #FFFFFF; background-color: #FFFFFF"></div>
    }
</div>

Finally, we need to ensure that our player lands on the platform (and doesn’t simply drop through). For now, we’ll pass the World into Player and perform the logic there:

        public override void Update()
        {
            _forceUp -= WorldSettings.GRAVITY;

            Top -= _forceUp;

            Console.WriteLine($"Top: {Top}, Left: {Left}, Width: {Width}");

            var platform = _gameObjects.FirstOrDefault(a =>
                a.Top <= Top &&
                a.Left <= Left + Width &&
                a.Left + a.Width >= Left + Width &&
                a.GameObjectType == GameObjectType.Platform);
            
            if (platform != null)
            {
                Top = platform.Top;
                _forceUp = 0;
            }
            
            if (Left <= 0 && _forceRight < 0) 
                _forceRight = 0;
            else if (_forceRight != 0)
                _direction = _forceRight;            

            Left += _forceRight;
            
        }

It’s not quite finished yet, but we now have a platform that we can walk on, and fall off:

What’s Next?

In the next post, I’ll try to introduce an NPC (not quite sure where I can get the graphics from yet, so it might just be a rectangle or something).

Creating a Game in Blazor – Part 2 – Gravity, and Controls

In this post I started the process of writing a very vague approximation of Jet Set Willy, in an effort to regain my youth.

Here, we’re going to take those very vague foundations, and allow left and right controls, along with a jump function. As a caveat, I’m going to state here that I expect quite large swathes of this code to change significantly before I declare it complete; and, as another caveat, there were 60 rooms in the original Jet Set Willy. I’ll be surprised if this series of posts manages to replicate the bathroom (which is the first room).

From the first post, we have a Game.razor, and all the code for this post will go into there. You can simply view that code here.

I’ve introduced a game loop here, which calls an update and then a draw method. Currently, the game loop is just a crude timer:

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

This gets registered in the OnInitializedAsync method:

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

        return base.OnInitializedAsync();
    }

The Draw method is, in fact, just a single line:

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

The reason for this is that Blazor doesn’t always automatically refresh state – updating bound objects on a timer is one of the times when it doesn’t. The Update method has a little more to it:

    private void Update()
    {
        ApplyGravity();
        _top -= _forceUp;

        if (_top > FLOOR)
        {
            _top = FLOOR;
            _forceUp = 0;
        }

        if (_left <= 0 && _forceRight < 0) _forceRight = 0;

        _left += _forceRight;
    }

Anything in caps is just a constant. The rest of the code basically takes care of the left, right, and jump.

Next, we’ll need to modify the HandleKeyDown method:

    private void HandleKeyDown(KeyboardEventArgs e)
    {
        switch (e.Code)
        {
            case "ArrowLeft": // Left
                if (_forceUp == 0)
                {
                    Walk(-SPEED);
                }
                break;
            case "ArrowUp": // Up
                Jump();
                break;
            case "ArrowRight": // Right
                if (_forceUp == 0)
                {
                    Walk(SPEED);
                }
                break;
            default:
                break;
        }
    }

As you can see, we’ve added Jump and Walk methods. All the logic has moved into those methods:

    private void Walk(int amt) => _forceRight = amt;

    private void Jump()
    {
        if (_forceUp == 0)
        {
            _forceUp += JUMP_FORCE;
        }
    }

Since Walk needs to be immediate, we just set the _forceRight to whatever value it’s passed; similarly with the jump, although we can’t allow a double jump, so we determine if a jump is in progress first.

Finally, we’ve added a HandleKeyUp: this stop walking when the key is released.

    private void HandleKeyUp(KeyboardEventArgs e)
    {
        switch (e.Code)
        {
            case "ArrowLeft": // Left
                Walk(0);
                break;

            case "ArrowRight": // Right
                Walk(0);
                break;

            default:
                break;
        }
    }

If you run this, you should now be able to walk “test” around the screen, and cause him to jump.

What’s Next?

For the next steps, I’m going to add a platform, and try to use some graphics. I’ll also try to refactor a little, as the main form is becoming a little large.

References

https://docs.microsoft.com/en-us/aspnet/core/blazor/components/event-handling?view=aspnetcore-5.0

https://www.pmichaels.net/2019/06/03/creating-a-car-game-in-react-part-1-drawing-and-moving/

https://stackoverflow.com/questions/58920461/how-to-detect-key-press-without-using-an-input-tag-in-blazor

Creating a Running Game in VueJS – Part 4 – The Finish Line

This post follows on from a series on creating a game in Vue. If you’d like to start from the beginning, then go here.

In the previous post we refactored the single file index.html that we’d been working on into multiple component files, and created a new Vue templated application.

In this post, we’ll do some further refactoring, and we’ll add a finish line.

Reusing Components

Since we now have components, we should re-use them; otherwise, there’s not much point in them being components. Our Player object is just a game object, and since we now need a second game object, let’s rename Player to GameObject. We can also add an additional property; for now, it will simply allow us to see which object is which:

<template>
    <div v-bind:style="location">
        
    </div>
</template>
<script>
export default {
  name: 'GameObject',
  props: {
    type: String,
    location: String
  }
}
</script>

We’ll change the template in App.vue, too:

<template>
    <GameObject
        v-bind:location="playerLocation"
        v-bind:type="player">
    </GameObject>
    <Timer 
        v-bind:seconds="secondsRemaining">
    </Timer>
    <GameObject
        v-bind:location="finishLineLocation"
        v-bind:type="finish-line">
    </GameObject>
</template>

As you can see, we’ve changed the data a little: instead of location we now have two pieces of computed data (in fact one isn’t really computed, but we’ll come back to that later):

computed: {                    
    playerLocation: function() {
        return 'width:10px; height:10px; left:' + this.playerX + 'px; top:' + this.playerY + 'px; border:1px solid #000; position: absolute;';
    },
    finishLineLocation: function() {
        return 'width:3px; height:300px; left:500px; top:50px; border:1px solid #000; position: absolute;';
    },

If you run that now, you’ll see the finish line; but alas we can “run” straight past the finish line and nothing happens!

Collision – have we won, or lost?

The first thing we’ll do here is create a new component: GameFinished. This will simply display some text for now saying whether you won or lost. The code inside the component will be very straightforward:

<template>
  <p>Game is finished, you {{ wonOrLost }}</p>
</template>
<script>
  export default {
    name: 'GameFinished',
    props: {
      isWon: Boolean
    },
    computed: {
      wonOrLost: function() {
        return this.isWon ? "Won" : "Lost";
    }
  }
}
</script>

We’ll need to both import and register the component. If you don’t, you’ll start getting errors such as

error ‘GameFinished’ is defined but never used no-unused-vars

Or:

error ‘GameFinished’ is not defined no-undef

To import the component, you’ll need this in App.vue:

<script>
    import GameObject from './components/GameObject.vue';
    import Timer from './components/Timer.vue';
    import GameFinished from './components/GameFinished.vue';

And registering is done slightly further down:

components: {    
    GameObject,
    Timer,
    GameFinished
},

We’ll add a new data property:

data: function() {                    
    return {
	. . . 
        isGameFinished: false

We’ll add a new computed function that will tell us whether we’ve won:

playerWon: function() {
    return this.isGameFinished && (this.secondsRemaining > 0);
}

And finally, the collision detection. Inside the Update method, we’ll just check the player position against the finish line:

update() {
    this.playerX += this.speed;
    if (this.speed > 0) {
        this.speed -= 0.01;
    } else {
        this.speed = 0;
    }
    this.timeNow = new Date().getTime();
    // Collision check
    if (this.playerX > this.finishLine) {
        this.isGameFinished = true;
    }
}

Where is the finish line?

Our last piece of refactoring will be to set the finish line based on a data property:

data: function() {                    
    return {
        . . . 
        finishLine: 500,

Now let’s tweak out finishLineLocation:

finishLineLocation: function() {
    return 'width:3px; height:300px; left:' + this.finishLine + 'px; top:50px; border:1px solid #000; position: absolute;';
},

In the next post, we’ll tweak the controls a little, and we’ll add some graphics.

Playing with the HTML Canvas Again

I’ve previously written about how you can write games using tools like React. I’ve also written about creating a basic game in Html and Javascript, and things like rotating shapes.

In this post, I’m going to demonstrate a sort of Etch A Sketch type program.

The HTML is very simple here:

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="test.css">
    <script src="test.js"></script>
</head>
<body onload="doDrawing()">
    <canvas id="canvas">
    </canvas>
</body>
</html>

Basically just bringing in a Javascript file, CSS and putting a canvas on the screen. I’m also calling a function onload – it’s worth bearing in mind that, as it goes, this won’t resize should you change the size of the browser. If you want that behaviour, then have a look at one of my previous posts mentioned above.

The CSS is even simpler:

* { 
    margin:0; 
    padding:0; 
}
canvas {  
    display: block;    
}

All we’re doing here is removing the default margin, and stopping scroll bars from appearing.

Javascript

The Javascript is where everything is happening; let’s start with some variables:

let x = 10;
let y = 10;
let directionHorizontal = 1;
let directionVertical = 0;

The four variables determine the position that we want to draw in, and which way we’re heading. We can now render this to the screen like this:

const doDrawing = () => {
    var c = document.getElementById("canvas");
    
    c.width = window.innerWidth;
    c.height = window.innerHeight;

    var ctx = c.getContext("2d");
    setInterval(() => {
        ctx.fillRect(x, y, 1, 1);
        x += directionHorizontal;
        y += directionVertical;
    }, 10);
}

The canvas width and height are the first things to note: when I started playing with this, I made the mistake of trying to set this in CSS; if you do, it actually doesn’t change the size of the canvas, but stretches it across the screen; this was the only way that I could get the canvas to display full screen without that stretching effect (if you know another / better way, please say so in the comments!)

Next we get the context from the canvas – this allows us to render to it, and then we simply set-up an interval, and draw a rectangle 1px x 1px each iteration.

Summary

That it – as with previous posts, there’s not a whole lot to using the HTML canvas, but I do like to re-experiment every so often.