Part 1: Vectorising the background (see below)

Part 2: Vectorising the character/fonts (

link to post )

Part 3a: Vectorising some of the sprites (

link to post )

Part 3b: Vectorising Kong's sprites (

link to post )

Part 3c: Vectorising Jumpman's sprites (

link to post )

I'm having a go at vectorising Donkey Kong for MAME. I'll be hacking the original game, suppressing the normal pixel output completely and rendering high resolution vectors to the screen. The techniques i'll be using could be applied to other classic arcade games. I'm choosing Donkey Kong because i'm very familiar with the disassembled code and mechanics of the game.

I'm documenting my journey and hopefully you may find it interesting and follow along.

I took inspiration from this blog which considers if a Donkey Kong port would be feasible on the Vectrex.

http://vide.malban.de/november-21st-donkey-kong-wait-noMy plan is to use the simple vector drawing capabilities of MAME LUA scripting language (available in MAME from version 0.196 to current).

There are some limitations. All vectors must be drawn out every frame and there are 60 frames per second. Optimisation is key and i'll have to do some calculations before I get carried away. I'll work out how many vectors can be safely drawn each frame without compromising the emulation speed.

First we'll need to write some LUA code to instantiate the emulated screen device in MAME so we can draw to it.

`scr = manager.machine.screens[":screen"]`

Then we'll need a simple function to draw a vector. This function draws a line from x,y position to another x,y position. Anything we draw, including other shapes, will me made up of vectors and will use this function.

`function vector(y1, x1, y2, x2)`

scr:draw_line(y1+wobble(), x1+wobble(), y2+wobble(), x2+wobble(), intensity())

vector_count = vector_count + 1

end

We'll keep tally of the number of vectors we draw.

Notice the funky "wobble" and "intensity" options. These are used to make vectors appear less boring on screen.

We can make a polyline function to draw multiple vectors that are chained together like etch-a-sketch

`function polyline(data)`

-- draw multiple chained lines from a table of x, y data points

local _y, _x

for _i=1, #data, 2 do

if _y and _x then vector(data[_i], data[_i+1], _y, _x) end

_y, _x =data[_i], data[_i+1]

end

end

We can make a function to draw a box by chaining 4 lines with our polyline function. We can specify the height and width of the box.

`function box(y, x, h, w)`

-- draw a simple box at given position with height and width

polyline({y,x,y+h,x,y+h,x+w,y,x+w,y,x})

end

We can make a function to draw a circle by chaining lots of vectors together. I ran with 20 vectors per circle.

`function circle(y, x, r)`

-- draw a 20 segment circle at given position with radius

local _save_segy, _save_segx

for _segment=0, 360, 18 do

local _angle = _segment * (math.pi / 180)

local _segy, _segx = y + r * math.sin(_angle), x + r * math.cos(_angle)

if _save_segy then vector(_save_segy, _save_segx, _segy, _segx) end

_save_segy, _save_segx = _segy, _segx

end

end

We now have our 4 drawing functions so lets try to max out MAME to see how many vectors we can draw per frame.

I made a function for testing the limits. This function will cycle through 4 different drawing tests (lines, polylines, boxes and circles). We can increase the limit gradually and keep a check on MAME average emulation speed (Function key F11).

`function debug_limits(limit)`

local _rnd, _ins = math.random, table.insert

local _cycle = math.floor(scr:frame_number() % 720 / 180) -- cycle through the 4 tests, each 3 seconds long

if _cycle == 0 then

for _=1, limit do vector(256, 224, _rnd(248), _rnd(224)) end -- single vectors

elseif _cycle == 1 then

_d={}; for _=0,limit do _ins(_d,_rnd(256)); _ins(_d,_rnd(224)) end; polyline(_d) -- polylines

elseif _cycle == 2 then

for _=1, limit/20 do circle(_rnd(200)+24, _rnd(176)+24, _rnd(16)+8) end -- circles

else

for _=1, limit / 4 do box(_rnd(216), _rnd(200), _rnd(32)+8, _rnd(24)+8) end -- boxes

end

debug_vector_count()

end

Results show we can safely draw 1000 vectors per frame when tested in MAME versions 0.196 and 0.242 running on Windows 10 at 1920x1080. Reducing the resolution increases the limit. 1200 per frame is ok too but let's keep it safely inside the limits. I'll also test on Raspberry Pi 4 later.

This video shows our tests running on top of Donkey Kong attract mode.

I think 1000 vectors should be sufficient. I'll use vectors sparingly and keep tally of the numbers as I progress.

Let's start by building background graphics for the girders stage. Here are functions to draw girders and ladders.

A simple girder is made from 2 parallel lines. We can add detail later if our vector count allows.

`function draw_girder(y1, x1, y2, x2)`

-- draw parallel vectors (offset by 7 pixels) to form a girder. Co-ordinates relate to the bottom vector.

vector(y1, x1, y2, x2, intensity())

vector(y1+7, x1, y2+7, x2, intensity())

end

A ladder is a more complex object with legs and rungs. We'll need to deal with broken ladders too. We can treat broken ladders as 2 separate ladders that are set slightly apart.

`function draw_ladder(y, x, h)`

-- draw a single ladder at given y, x position of given height in pixels

vector(y, x, y+h, x) -- left leg

vector(y, x+8, y+h, x+8) -- right leg

for i=0, h-2 do -- draw rung every 4th pixel (skipping 2 pixels at bottom)

if i % 4 == 0 then vector(y+i+2, x, y+i+2, x+8) end

end

end

We now test drawing girders and ladders. In total we have used 139 vectors.

Now for some other background detail for the girders stage. The oilcan, hammers and the stacked barrels.

`function draw_oilcan(y, x)`

box(y, x, 1, 16) -- outline of oil can

box(y+1, x+1, 14, 14) -- bottom lip

box(y+15, x, 1, 16) -- top lip

box(y+7, x+4, 3, 3) -- "O"

vector(y+7, x+9, y+10, x+9) -- "I"

polyline({y+7,x+13,y+7,x+11,y+10,x+11}) -- "L"

vector(y+5, x+1, y+5, x+15) -- horizontal stripe

vector(y+12, x+1, y+12, x+15) -- horizontal stripe

end

`function draw_hammer(y, x)`

polyline({y+5,x,y+7,x,y+8,x+1,y+8,x+8,y+7,x+9,y+5,x+9,y+4,x+8,y+4,x+1,y+5,x}) -- hammer

box(y, x+4, 4, 1) -- bottom handle

box(y+8, x+4, 1, 1) -- top handle

end

`function draw_barrel(y, x)`

-- draw an upright/stacked barrel

polyline({y+3,x,y+12,x,y+15,x+2,y+15,x+7,y+12,x+9,y+3,x+9,y,x+7,y,x+7,y,x+2,y+3,x}) -- barrel outline

vector(y+1, x+1, y+1, x+8) -- horizontal bands

vector(y+14, x+1, y+14, x+8)

vector(y+2, x+3, y+13, x+3) -- vertical bands

vector(y+2, x+6, y+13, x+6)

end

Drawing all this stuff increases the vector count to 232. That's pretty good going.

We still have sprites for Jumpman, Pauline, Fireballs, Donkey Kong, hammer smashing, oilcan flames and barrels to deal with. We'll also need to make a vector font to show the scores, level, bonus timer, bonus points and other messages.

I'll look at adding vector sprites in the next part. The vector sprites will have to be cleverly linked to the game logic so they behave similarly to the pixel sprites.

Here are the functions responsible for varying the intensity and wobble of the vectors.

`function intensity()`

-- we can vary the brightness of the vectors

return ({0xbbffffff, 0xddffffff, 0xffffffff})[math.random(3)]

end

function wobble()

-- random change of the vector offset

return math.random(-40, 60) / 100 -- random change of the vector offset

end

Here's a video showing progress with the girders stage. I've hacked out the original background graphics for now so they don't interfere with our new vector graphics.

This is all monochrome for now, but maybe later we can think about adding splashes of colour.

My experimental code is on github at

https://github.com/10yard/vectorkongJon