Author: stevedunn

TL;DR: I did something stupid and realised my mistake – what follows is mainly for future reference and for those that are making the same mistake.

Win2D is great! So is the concept of UWP and the Windows Store. It means you can write a game for the Windows Store (for Windows 10), and with some minimal work, it can run on the XBox One (since that’s based on Windows 10 too).

Here’s the opening screen – note the text looks naff and the sprites have lost their intentionally pixelly look:

… compared to the TypeScript version:

I spent quite some time trying to fix this – thinking it was some nuance of Win2D and offscreen bitmaps. I didn’t get anywhere, so I set out to create a small reproducible project that I could use in a StackOverflow question (as an aside, I often find that just typing a StackOverflow question creates enough clarity of the problem that the answer just magically appears – as was the case here!). So with just a few lines of code, I had this monstrosity:

Even though I know how tolerant the StackOverflow community is (</sarcasm>), for completeness and to show that I did my homework before asking, I tried the same drawing (DrawImage) with multiple overloads and included a screen-shot for each.

So, I’m about 45 minutes into writing the StackOverflow question, and then it struck me:

I’m only scaling the main drawing session; what I really wanted to do is scale the off-screen drawing session too: in Win2D, when drawing, each instruction is queued and then blitted to the GPU asynchronously, so my flow is:

1. Scale up the main DrawingSession to make things looks bigger
2. Create an offscreen DrawingSession
3. Draw text and a line to the offscreen session
4. Draw the offscreen session to the main drawing session
5. Draw text and a line to the main session

Essentially, the offscreen canvas created in step 2 has its instructions drawn at 1x scale but blitted at 4x scale, rather than the instructions being drawn at 4x scale and blitted at 1x scale.

Probably of no interest to anybody (apart from my son who I did it for), but here’s a timelapse video converting Pac-Man from TypeScript (play it here) to C# for the Windows 10 Store. When it’s finished, I’ll put the source up and describe what I learnt during the process. Watch it here.

I’ve been working with C# for many years now and the new additions to the language are very exciting. They’re focused on immutability. Well, actually, the Microsoft documentation seem to suggest they’re more focused on Performance, but they go hand-in-hand; the less you can modify, the faster things are.

The benefits of immutability are often overlooked especially by those who have only experienced programming in C#. But coming from a C++ background, I long for the immutability that C++ has. For example, In C++, you can:

Declare a method as const, meaning the method cannot modify any fields of the class (or call any other non-const method)

Declare a parameter as const, meaning the parameter can’t be modified in that method, and if you passed that parameter around to other methods, those methods would also have to declare that parameter as const and follow by the same rules

Declare a return type as const, meaning that you can’t change what you’re given

Declare a field as const, meaning that only the constructor can set the field

(these bullet points are generalisations)

const is great. There’s even been 80’s pop songs written on the virtues of const:

The new features in C# 7.2 are losely related to the above. This post describes one of them; in parameters

We called Conculate, which we know mutates the state (by setting Id to 42), but it turns out that a copy was made and the ID was set on that copy.

Here’s an excerpt from the Microsoft page on reference semantics with value types:

So, it turns out that readonly parameters aren’t as powerful as C++ const parameters. They are, technically, read-only (you can’t mutate them), but they’re not const as in C++ const.

I also don’t like the fact that you can legally call methods that mutate the instance, but don’t know about it. If const methods existed in C#, the compiler would’ve disallowed the call to Conculate because it couldn’t be declared as const (as it mutates a field).

Perhaps in a future version of C#, we’ll get const methods…

But there’s more…

Even though readonly parameters are a step in the right direction, I was disappointed. I was dissapointed because when I read about readonly parameters, I wanted to immediately jump into every single method I’ve ever written and splash in keywords on every parameter. But the majority of things passed around are classes and not structs. Notice in the earlier example that Thing was a struct. In time, with the performance benefits of reference semantics with value types, I’m sure struct will be the go-to type instead of class.

Anyway, in parameters can be either struct or class. Here’s what happens when you change Thing to be a class rather than a struct; remember the earlier example where the compiler stopped you setting a property? Well, now it lets you do whatever you want:

Oh the disappointment!

Here’s another excerpt from the documentation:

‘The benefits are minimal‘. I personally think minimal is being generous. I would’ve use the word Dangerous. If Thing is a class rather than a struct, then what is passed is a reference to a reference. This means that the value that the method is dealing with can be changed to point to something else at any time! (even from outside of that method)

Here, we start off a task to do something with a Thing. We wait 1 second and set the reference to null. Assuming the user hasn’t pressed Enter yet, a NullReferenceException will happen when they do. This is because we’re passing a reference to a reference and then setting the reference to null.

I can only imagine all the subtle bugs that this will cause if spread throughout a codebase.

ReSharper to the rescue?

I don’t even know if it’s possible, but I’d have liked the compiler to disallow in parameters that aren’t value types. I’m sure the ReSharper team are working on a new code-inspection to warn when passing in references.

I never intended to make the source code available, but a few people asked for it, so I’ve tidied it up a bit and put it on GitHub (it’s far from tidy though, so go easy – plus it’s my first attempt at TypeScript!)

I’ve described the major bits of the code below. I’ve described:

the startup – how scripts and assets are loaded

the game-loop – what bits of code are called 60 times per second

the game flow – how the code flows from one screen to another

the graphics – spritesheets, sprites, and Canvas and how they fit together

the maze – how things interact with the maze

ghosts – most of the logic in the game is associated with the ghosts

timing and difficulty – getting the game to play like the real arcade game

If there’s anything I’ve missed, please let me know.

I hope you find this useful. Please be aware that this is not a shining example of TypeScript or the best patterns to use in TypeScript. The style leans heavily towards C# as that’s my day-to-day language. It barely scrapes the surface of TypeScript features and I’m sure there are many things in it that could be made more elegant (readable) by using other TypeScript features. I’d love to get feedback on the code as I’d like to evolve it over time. So please free to provide feedback, pull-requests, etc. etc.

Build and Run

The following should download and run the game (assuming you’ve got git and npm installed):

Shell

1

2

3

4

5

git clonehttps://github.com/SteveDunn/Pacman.git

cdpacman

npm install

tsc

start http://localhost:8080

Game Startup

index.html loads the JavaScript scripts for howler (sound), hammer (touch), the loading screen, the control panel, and require.js.

When the page loads, it loads all of the sound files and then all of js files. require.js fires an event (load), when a script is loaded. We subscribe to this event and tell the loading screen that a script is loaded (loadState.scriptLoaded(moduleName)).

The main game is held within a div named gameDiv. Within that div is a canvas:

Game Loop

The game-loop updates and draws everything (60 times per second). It calls MainWindow.Update (MainWindow.ts) with the time elapsed since the last call. The time elapsed is important as it allows timers to be run accurately. MainWindow is the, er, main window. It handles:

updating the current act (see below)

drawing the current act

updating and drawing the score and status panels

handling game events, such as ‘pacManEaten’, ‘ghostEaten’ etc.

Game Flow

Everything in the game is an Act:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

import{Canvas,GameContext}from"../Core/_exports";

import{ActUpdateResult}from"./ActUpdateResult";

/**

* An 'act' is something that's run in a loop. The main window continaully updates and draws whatever

The welcome screen (or the ‘attract screen’ as they call it in arcade circles) is called the AttractAct. You can see it being set as the main Act in MainWindow.ts:

JavaScript

1

2

3

4

MainWindow.currentAct=newAttractAct();

// POINTER: You can change the starting Act by using something like:

//MainWindow.currentAct = new TornGhostChaseAct(new AttractAct());

When the update method returns Finished, the game-loop starts to run the Act returned by nextAct.

Graphics

The graphics are drawn onto an HTML Canvas. A sprite-sheet is loaded in index.html:

XHTML

1

<img hiddenid="spritesheet"src="img/spritesheet.png" />

It looks like this:

It contains all of the graphics in one image. The sprites then reference a particular rectangle of this image and are drawn on the canvas. All sprites derive from Sprite:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

exportabstractclassSprite{

loadContent():void{// nothing

};

abstractget position():Point;

abstractupdate(context:GameContext):void;

abstractdraw(canvas:Canvas):void;

abstractget origin():Point;

abstractget size():Vector2D;

abstractget spriteSheet():HTMLImageElement;

abstractget spriteSheetPos():Point;

}

Each sprite has the following facets:

position specifies where in the ‘game world’ the sprite currently is

spriteSheetPos specifies the point in the sprite-sheet where the image for this sprite begins

origin specifies the offset from the top left of the sprite that acts as the origin. The origin is used to calculate the top left position of the sprite and can be used for rotation (rotating something whos origin is top left will have a different effect that animating something whos origin is center)

size specifies the pixel extent of the sprite. Everything drawn in this game is zoomed in by 3 times, so the size here is the pixel size, and not the output ‘screen size’

The Maze

The maze (as shown above) is drawn to the canvas every frame. The ‘pills’ (normal pills and ‘power pills’) are removed from maze (well, a copy of each as there’s one for each player) when the pill is eaten.

The maze is broken down into ’tiles’ that are 8×8 pixels in size. Sprite positions are converted to the associate ’tile’. The game then refers to a lookup that says what’s in the current tile. The lookup looks like this:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

privatestaticreadonly map:string[]=[

// 0,0 29,0

" ",

" oooooooooooo oooooooooooo ",

" o o o o o o ",

" * o o o o * ",

" o o o o o o ",

" oooooooooooooooooooooooooo ",

" o o o o o o ",

" o o o o o o ",

" oooooo oooo oooo oooooo ",

" o + + o ",

" o + + o ",

" o ++++++++++ o ",

" o + + o ",

" o + + o ",

"++++++o+++ +++o+++++++",

" o + + o ",

" o + + o ",

" o ++++++++++ o ",

" o + + o ",

" o + + o ",

" oooooooooooo oooooooooooo ",

" o o o o o o ",

" o o o o o o ",

" *oo ooooooo++ooooooo oo* ",

" o o o o o o ",

" o o o o o o ",

" oooooo oooo oooo oooooo ",

" o o o o ",

" o o o o ",

" oooooooooooooooooooooooooo ",

" "

];

o represents a cell containing a pill

* represents a cell containing a power-pill

+ represents a cell containing nothing

[space] represents a wall

A tile is represented by the Tile class. Some of the main methods on here are:

isInCenter – is the sprite’s position near the center of the tile?

nextTile – it is common to get the next tile, based on the direction that an actor is headed

nextTileWrapper – the next tile, but taking into account ‘wrapping’ (the two tunnels at either side of the maze)

Ghosts

Ghosts move around the maze and either chase pacman or run away from him. Here’s the various states of a ghost:

JavaScript

1

2

3

4

5

6

7

8

9

10

exportenumGhostState{

// heading towards pacman or their home corner (scatter)

Normal,

// blue - running away from pacman (in a random pattern)

Frightened,

// heading back to the 'House'

Eyes

}

The state of a ghost can differ from the ‘movement mode’ of a ghost:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

exportenumGhostMovementMode{

Undecided,

// the ghost is chasing pacman

Chase,

// the ghost is heading back to his 'home corner'

Scatter,

// the ghost is heading back to the house (after he's been eaten)

GoingToHouse,

// the ghost is in the house

InHouse,

// the ghost is blue

Frightened

}

I mentioned that the ‘ghost state’ and ‘movement mode’ can differ; an example is that a ghost can be ‘blue’ (Frightened) while still being in the ghost house

There are a number of types responsible for moving ghosts. They all implement GhostMover:

The general logic of a ghost comprises of ‘head to the home corner for X seconds, chase pacman for X seconds’. The time spent in each phase varies throughout the level. Each level specifies different patterns.

Timing and Difficulty

Getting the difficulty to match that of the arcade game was tricky. There are many variables used throughout each level. These variables are described in the type LevelProps:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

exportclassLevelProps{

constructor(

publicreadonly introCutScene:IntroCutScene,

publicreadonly fruit:FruitItem,

publicreadonly fruitPoints:number,

publicreadonly pacManSpeedPc:number,

publicreadonly pacManDotsSpeedPc:number,

publicreadonly ghostSpeedPc:number,

publicreadonly ghostTunnelSpeedPc:number,

publicreadonly elroy1DotsLeft:number,

publicreadonly elroy1SpeedPc:number,

publicreadonly elroy2DotsLeft:number,

publicreadonly elroy2SpeedPc:number,

publicreadonly frightPacManSpeedPc:number,

publicreadonly frightPacManDotSpeedPc:number,

publicreadonly frightGhostSpeedPc:number,

publicreadonly frightGhostTime:number,

publicreadonly frightGhostFlashes:number){

}

}

At a glance, there are different speeds for:

Pacman – depending on whether he’s eating pills or he’s in an empty cell (a very minor difference, but it makes all the difference if you’ve got a ghost 2 pixels away!)

ghosts – depending on whether they’re on cells that contain pills or empty cells, and also whether they’re ‘frightened’ (blue)

the duration the ghosts are blue for (in later levels, this is one frame (1/60th of a second!))

‘Cruise Elroy‘ speed – Blinky is the only ghost that has different speeds throughout the level

tunnel speed – ghosts travel slower through tunnels

There’s a rather large array created in LevelStats that contains all of these variables for the first 21 levels.

Want to learn a new programming language? Tired of the usual Line-of-Business tutorials? Then write a game! I’ve always said that the best way to learn a new language is to write a game with it. You’ll come across many problems when writing a game that you just won’t experience by writing an app that selects product items from a customer’s order.

That’s what I did recently when I wanted to learn TypeScript. I decided to write a web-based version of PacMan. This post describes a bit about my goals. There’s nothing technical here; I may, at some point, describe the internals of the game and share the code.

My goals were to learn TypeScript and end up with a game that looked and played similar the original.

Here’s some comparison screenshots:

Arcade (Mame)

TypeScript

ArcadeTypeScript

Whilst learning the language, I didn’t want the overhead of also learning a game framework, so I chose to use the HTML canvas directly. This worked out well as the canvas is very simple to use.

The feedback cycle with the canvas is really quick: do the code changes and refresh the browser to see the results.

I used a combination of Visual Studio Code & node, and Visual Studio (2017) with IIS. I found debugging with Visual Studio 2017 easier than with Visual Studio Code (and of course, I also had ReSharper which works quite nicely with TypeScript although it’s not quite as fast and robust as it is with C#).

I found it quite easy to pick up TypeScript, probably because of its similarities with other C based languages. Like most languages, it’ll probably take years of use to master all of its nuances (my familiarity with the nuances of C# came from using it for years, and also from reading the fantastic More Effective C#).

In fact, I found that it was very quick and easy to get up and running in TypeScript. This was both good and bad: good because I could concentrate on the game more and worry less about the language, and bad because I could concentrate on the game more and worry less about the language!

There’s a fair bit to PacMan and I wanted mine to be as close to the original as possible. This included things such as:

Everything has a different speed and those speeds vary depending on level (and where you are in the level). PacMan travels at a different speed to the ghosts (initially, a very small difference), but later levels the difference increases. PacMan’s speed changes when he’s eating pills, which is barely noticeable unless a ghost is 5 pixels from your rear!

Cornering – Related to the different speeds of things is the technique of ‘cornering’: PacMan can gain a very slight speed increase over the ghosts by selecting the direction a couple of pixels before the turn:

This is very subtle but is critical in later levels; to evade faster ghosts, head to as many corners as possible until the ghost pattern changes (see below) – each corner gives PacMan a couple of pixels advantage of the ghost

Ghost patterns: ghosts are either chasing PacMan or returning to their ‘home corner’. Every level has a different duration to the patterns. I used this page for all the timings in the game and this page to understand where the ghosts should go.

BUGS! There are a few bugs in the original PacMan. As far as I know, I replicated all but one bug (the kill screen bug) (but I’m sure I introduced a few of my own to make up for it!). Bugs deliberately programmed in include the ghost house bug (where it’s possible to keep 3 ghosts from entering the maze for the whole level), and the bug where ghosts can (very rarely) change direction just before they eat PacMan.

Ghost eyes: ghost’s eyes look at where they’re going next. If they’re heading for a T-junction, they’ll either look left or right just before they get there. If you’re very quick, you can use this to your advantage. This is very subtle. I’ve played PacMan for years and only recently discovered this.

Cutscenes: These are intermission screens that appear after certain levels. They tell a ‘story’ that reveals the true identity of the ghosts!

I found that the game itself was only half the work. I wanted it to run on as many platforms and devices as possible (PCs, phones, tablets). A fair amount of work, after the game was finished, went into things like:

The loading screen to show the progress of script and asset loading

Audio issues – sound played on the PC but didn’t play on mobile devices. I ended up using the super Howler library

Control Panel – needed a control panel where users could ‘insert coins’ and select the number of players by clicking/touching (during development it was all keyboard based)

Overall, I think it was a good idea to write a game to learn TypeScript. Aside from the extra tasks involved after finishing writing the game, it was a quick way to learn the language with the added bonus of being able to visualise progress. Click here to play.

I’ve spent some time over the Summer and Autumn of 2011 rewriting the Gleed 2D tool. This is a tool for editing levels for 2D games and is a very popular tool in the XNA community for games running on XBox and Windows Phone.

Most of the changes in the new version are under-the-hood. The biggest change has been to make it have a plug-in architecture. There has also been a few UI changes though; here’s some screen-shots.

The original tool before being re-written:

and here’s the new version:

The main reason for rewriting the tool was that I wanted to add more features to it but found that it wasn’t easy. It wasn’t easy because it was originally written to just handle the basics needed for creating and editing levels.

The features that I wanted to add were for the next version of my game (video here). I wanted to include lighting and shadows and I wanted to design these on the canvas.

Instead of shoe-horning my changes into the original Gleed 2D source, I decided it’d be best to rewrite it and change it to a plug-in based tool.

So, now everything is a plug-in. The basic shapes (rectangle, circle, path) and textures are now plug-ins. Lighting (lights and shadows) is now a plug-in. There’s also a plug-in for simple ‘behaviour’.

Here’s a quick video showing how to use the basic shapes and textures:

Basic shapes and textures

Here’s a short video showing lighting:

Lights and shadows

and lastly, here’s a short video showing simple behaviours:

Simple behaviours

The tool is still currently a bit rough. There’s various bugs that need to be fixed, but none of them stop the tool from doing what it was designed to do. The project is now quick big, so I’m hoping that the community will jump in and add/fix stuff. I’d like to see plug-ins for physics and particle systems.