Super Mario Bros.
At the party CAFe'2019 was presented
demo version of the gameSuper Mario Bros. 128Kfrom
Gogin (Sergei Smirnov). This demo
supports the main features of the original
games, including swimming, eating mushrooms,
battles with Bowser and, of course, the "prin" scene
cession in another castle." At the same time, it’s wonderful
operates at a frequency of 50 fps. And according to Wed─
in addition to the 2002 version from the same
author's engine was completely rewritten -
now in color.
This is not just a game, but also a specialty─
regulated resource preparation system.
In particular, there is an option "COVID
edition" with different graphics.
Our editors were interested in how the word─
the game engine was built from the inside, andSergeyagree─
I wanted to give an interview.
* * *
Alone Coder>
Hello! Tell us how your engine works─
jok Mario?We wrote withShiru
a similar engine, but without attributes, there are no─
only what was not empty was redrawn.
As I understand, you have some products─
wigs both in terms of attributes and in terms of inner─
loops, and in terms of managing all this.
In the original SMB (whichported
inNedoOS) in RAM there is a map of visible meta─
tiles exactly like metatiles, what about you?
list of objects? How is the level stored? B
original SMBthere are standard columns
and large objects.
At one time I thought about how to store
a map entirely made up of objects that are allowed─
scrolls in all directions. But in the end it’s like this
and didn’t decide how to do it quickly, and returned
to metatiles...
Gogin>
My map is initially presented
grid 256x16. Next I will compile this grid─
It is in two blocks of bytes.
-The first block is a byte
stream for drawing the map on the screen. In it
datalaid out so that it is immediately clear─
It’s clear which sprite (tile) to print in which
line of the screen, and how many are identical
tiles in a row need to be printed. Format
storage byte card was developed by several
only months, in fact, it began with him
second reincarnation of the engine in 2016
Due to the fact that byte stream decompression
cards are implemented only in one direction,
then you can only scroll to the right. If so─
women scroll in both directions, then this method
doesn't fit.
But it allows for a very small number
clock cycles prepare all register pairs
to print tiles. Enemies that appear
(created) on the right side of the screen - also
are directly programmed into this card.
-The second block byte is a regular table
256x16,which stores physical properties
tiles, and is used only for processing─
keys of logic and collisions in the game. Image
(sprite) tile and its physical properties -
these are different things. They contact at the stage
level descriptions from tiles, through properties
the tiles themselves.
Essentially, any block in Mario is a certain─
second “sign” of activity/inactivity +
sprite name. And the palette is set for
the entire location, in connection with the name
block.
So I can have four times─
blocks: brick1, brick2, brick3, brick4.
They may look the same (using─
the same sprite), but the first one will be
"kicked out", the second will be the background, the third
will contain a prize or coins, and four─
it can be colored or striped, because
that the palette is registered only for the block
brick4.
One of the most difficult places in everything
tile engine is removing a block from the map,
restoring a block in the card or replacing it
one block to another. These operations are
there are spot changes in two cars at once─
tah - in bytecode for rendering and in tab─
personal card for logic processing.
And if with a table map, in principle,
everything is clear, then to change the byte stream
for rendering - in the end, quite non-trivial─
The algorithms turned out to be perfect.
Alone Coder>
Is it possible to implement on your engine
for example, the gameNorth Star ?
Gogin>
Yes, it's easy to do. Don't change anything
necessary. You just have to rewrite the seal
tiles 2x2 for printing tiles3x3.Work
logic and all calculations in the coordinate system
world - will not change.
Alone Coder>
And what does the output innerloop look like, including
metatile selection?
(Ed.: innerloop - the innermost loop.)
Gogin>
In a loop, we print tiles into the screen one by one─
nkam from left to right, tiles are “cleaned” themselves
yourself, so the overlap goes left and right.
The tile printing innerloop looks like this:
- read the bytecode from the card, directly in it
it says: what tile to print where and how─
only once in a row.
In bytecode, every bit there has a meaning:
1 bit - command sign: or print
tile, or create an enemy;
4 bits -tile number;
3 bits - number of tile repetitions;
4 bits - vertical familiarity number;
2 bits - offset of the first block in the chain,
relative to the current column;
1 bit -color clearing marker after blocks
(if required).
Bits in bytecodelocated in such a way
at once, what not to dorrcaorrlca,
to drag them to a “comfortable” position for
use.
Next:
- received all the information;
- display several tiles of one at once
procedure; there are different options for procedures
for different numbers of identical ones in a row
tiles;
- color the first column on the left
(vertical strip 2 familiar places);
- clear the last column on the right, 2
familiar places, if the corresponding one is set
flag;
- if the bytecode "next" is encountered
column", we move two places and
repeat...
- until we hit the right one
screen border.
Alone Coder>
How many bits of subpixel coordinates
Does the hero have enemies? In the original SMB
the hero's subpixels are somehow strange and
seem to affect speed.
(Ed.: subpixels - fractional part of coordinates
(smaller than a pixel).)
Gogin>
Everything is calculated in the system fixed point
12.4: 12 integer bit, 4 fractional bit.
If we take the hex coordinate #abcd in
world, then:
- #ab is the tile coordinate,
- c - pixel number in the tile,
- d - subpixel number
Alone Coder>
How does Mario enter the pipe? In the original y
each sprite had a mode - print by─
top of the background or below the background.
Gogin>
All sprites are printed in my engine
always on top of tiles, by OR. Mario enters
pipe exactly the same as the Piranha Plant from
this pipe comes out. Decreases in cycle
height of the runtime object, and moves down
vertical coordinate. Prints on the screen─
This is a sprite cut off from the bottom.
Alone Coder>
How to enter the pipe on the side?
Or just forcounting the output order, and when
this should be a guarantee that the pipe is dug.
not familiar?
Gogin>
We don’t know how to enter side pipes :) This
not implemented. Basically, if we note
zones where this is possible, and implement it in
engine, then:
- only at the time of the animation of entering the work─
boo - change the printing order (chapters first─
new hero, then tiles),
- tiles are printed head-on, without any
OR, so they will completely block the part
hero,
- for beauty, so that there are 100% no clashes─
ha, you can align the map to familiarity in
moment of entry,
- or you don’t have to level it, since the poppy─
the hero's maximum escape to the right and so you─
matched by familiarity, and to get
clashing, you will need to run to the right first
a little further than the pipe, then return
back, and then climb into it. Here's a chance
DO NOT get into a familiar place - high.
Alone Coder>
How side clipping works
top, bottom?
Gogin>
There is no left-right clipping at all.
Top and bottom clippingit works like this
the same as going into a pipe and like plants. Simply
Part of the sprite is printed, cut off at the top
or below.
Alone Coder>
How memory is allocated for sprites and
tiles?
Gogin>
Distribute memory for tiles and sprites─
is carried out fully automatically, and upon completion
build, I basically don’t know where the sprites are
are lying.
The compiler is responsible for this,
which automatically calculates everything and
distributes according to free memory.
When the sprites for a level are compiled,
then at the input: descriptors of all
entities in the game world (resources department─
but, sprites separately), world map and differences─
These settings are also resources.
It turns out a kind of relational mo─
del games. Having loaded the world model into memory,
you can directly calculate which sprites are needed─
are being built, and in what objects they will be used─
used.
All this is automatically converted into necessary─
format suitable for the engine, folds into pa─
memory, displacement tables are built. If the object
can move pixel by pixel and slowly, then
8 copies of the sprite with sme are created for it─
grievances. If the speed of the object
is a multiple of two, then 4 copies are created with sm─
lenitions, etc.
As a result, within one location at the exit─
where we have readyjars with sprites and go─
general tables for all tile sprites and
all objects. Sprites, of course, are used─
reused if possible.
Alone Coder>
If the graphics are on the page, then how
is it displayed on two screens?
Gogin>
Graphics for the 5th screen lie in the 5th
page and partially in the 4th page. Graphics
for the 7th screen lies in the 7th page and cha─
sticheskiy in the 2nd page.
Alone Coder>
How does the engine get the required sprite? Pros─
then by number, by generated label or
there is a multi-level system of object class -
animation number - phase number?
Gogin>
When the level is compiled, the entire hierarchy─
the chemistry of objects unfolds as much as possible
into flat structures, down to the physical
sprite addresses. For enemies, prizes and others
objects - sprite addresses (physical)
lie openly in immutable structures,
like static properties in a class. For Thai─
catching sprite addresses is easytable,
in which the sprite address depends on the number
tile and the current position of the world map on
screen.
Here is a piece of the object processing pipeline.
DY = DY + Gravity;
Y = Y + DY;
X = X + DX;
and then animation by timer.
;Process Y = Y + dY
wpf_process_y:
ld e,a ;A = (ix+RO_Y)
ld d,0
bit 7,e
jr z,wpf_process_y_2
dec d ;DE = object dY (12.4)
wpf_process_y_2:
ld hl,(ix+RO_Y) ;HL=object Y (12.4)
add hl,de
ld (ix+RO_Y),hl
;check out of screen below
ld a,h
cp #fe ;Y = -32px
jp z,wpf_delete_object
;Process X = X + dX
wpf_process_x:
ld hl,(ix+RO_X) ;coord X (12.4)
ld c,l ;C = coord X low -> will
;be used below for animation
ld e,(ix + RO_DX) ;dX (4.4)
ld b,e ;B = dX -> will
;be used below for animation
ld d,0
bit 7,e ;move left or right
jr z,wpf_process_x_2
dec d ;D = #ff
wpf_process_x_2:
add hl,de ;X = X + dX
;check out of map at left
ld a,h
inc a ;cp #ff
jp z,wpf_delete_object
ld (ix+RO_X),hl
;Process animation
wpf_process_animation:
ifdef OPTIMIZE_RUNTIME_OBJECTS
ld a,(internal_timer)
and %00000011
jr nz,wpf_process_updown
endif
;B = dX (4.4), set above
;B<=0 - move left, B>0 - move right
ld d,RDEF_SPRITE_NO_LEFT_1
ld a,b
dec a
rla
ifdef BACKWARDS
ccf
endif
jr c,wpf_pa_1
inc d
inc d ;D = RDEF_SPRITE_NO_RIGHT_1
wpf_pa_1:
;check dX == 0 ?
ld a,b
and a
jr nz,wpf_pa_2
;use timer if dX == 0
ld a,(internal_timer)
rlca
rlca
rlca
rlca
ld c,a
wpf_pa_2:
;C = coord X, set above
;bit 7 of C = sprite 1/2
bit 7,c
jr nz,wpf_pa_3
inc d
wpf_pa_3:
ld a,d ;A = sprite cell index
;in definition
ld hl,(ix+RO_DEF_ADDR)
or l
ld l,a
ld a,(hl)
ld (ix+RO_SPRITE_NO),a
;Process 'updown slide' bit
wpf_process_updown:
Alone Coder>
А почему в версии,которая была предста─
влена на CAFe, иногда оставалось белое
familiar when little Mario hit from below
by brick?
Gogin>
This restoration does not work correctly
tile background color if the player first took
running, then hit his head on the block, and
then slowed down.
If during this time the world map has time
go too far to the left, then the middle of the first
block remains unpainted.
The position of the knocked out block will be strongly
differ slightly from the position of the same block
earlier, and the attributes are restored not in
in the place where they should.
The bug only appeared in motion and with
takeoff run There was a similar bug with the right side─
tile damage when you need to clean the attribute─
you are after the tiles.
This can be corrected by redrawing the stripe
attributes in their entirety, in length equal to the offset
cards during the “bouncing” of the block.
* * *
Now Gogin is writing a new game for the world─
brand new engine. We are waiting for something no less
interesting!
Share your thoughts about the article