FPS development


(Michael Gollnick) #1

I am currently working on a small first person shooter.

Till now the code is not much optimized and has plenty of room to do so. Texture rendering is currently a bit clumsy. Items and doors are basically done. The bad guys too but movement and AI is currently the main work. The world is already quite big without noticable size in flash thx to many ideas from TeamARG.

One thing I still need to solve is how to draw an image with a mask without actually storing the mask in RAM/ROM. What I would need is something that ensures the image outline is e.g. a black pixel and the followed by the image data. All pixels outside the outline are masked. Does anybody has a good idea?

Engine is about 12kb, rough measurements show 20fps. I believe 30fps is an achievable goal.


Dark&Under - A Dungeon Crawler
#2

Looks fantastic even at 20fps, great work


(Josh Goebel) #3

Not sure I follow what you are asking here.


#4

wow! Can’t wait to play this!

Can a Battlezone clone be made from this engine? watching the video, all the wide open spaces reminded me of all the hours I spent playing Battlezone!


(Michael Gollnick) #5

Hmm that got probably lost due to my poor translation to english. Take the following picture:

On the right side of the screen there is a barrel and in front of the barrel there is a key. The engine is a raycasting engine, so the screen is rendered column wise. For the sprites like the barrel and the key they are rendered farest first, closest last (key). Now as this is just a black and white screen and the key is hardly visible in front of the barrel I want to draw it with a black outline. So all black pixels outside of the outline around the key silhouette are transparent and inside the silhouette all pixels are drawn (black and white).
What I am looking for is an algorithm how to draw like this without storing a mask (e.g. for the silhouette) in memory. I want to detect the silhouette while drawing the sprite.

My initial idea is to take the neighbor columns (left, right) of the currently drawn column and logical or the bytes. If not zero, pixel will be drawn (either black or white). Not sure if this works out.


(Michael Gollnick) #6

The engine is a raycasting engine. I think you can make something like Battlezone from it. The difference might be that you would need to create pictures from the enemy tanks from different sides (e.g. every 45 degree) so the engine can show them depending on the angle the player looks at them. That might eat up some flash memory but as long as you do not plan to have many different enemies that should work.


(Josh Goebel) #7

That’s going to be a lot slower - why not just store the mask?


(Michael Gollnick) #8

It is mainly due to the small flash size. The sprites that I have currently in mind would push it to the limits. E.g.

8 wall textures each 32x32 pixel
2 door textures each 32x32 pixel
4 different enemies each 32x32 pixle (4 sides each 2 frames, 2 attack frames, death frame, hit frame)
~2-4 debris each 16x16 pixel
8 items each 16x16 pixel
some story text
maps (~3kb + 64 bytes per level 64x64 blocks)
hud 128x64 pixel + some minor images to update the hud (currently selected weapon)
mainscreen + pause screen
sound
font
4 weapons (idle + 2 shoot animations)
… probably the list is not complete :smiley:

Only for the sprites (items, enemies, weapons) i would need a mask. I just feel it would cost too much of flash and I need to reduce some features.


(Josh Goebel) #9

Yeah you would only mask what you needed… well good luck. let us know how the auto masking goes :slight_smile:


(Michael Gollnick) #10

Oh I will. Hope I find something soon.


(Holmes) #11

Couldn’t you draw the same sprite using BLACK at (x-1, y), then draw it using WHITE at (x, y) right afterwards? That will give it a black shadowy effect. I guess you could draw the same black version twice or 3 times if you wanted.


(Josh Goebel) #12

FOr most things if you drew it -1, -1… etc at all four corners it’d likely suffice… but now your render is 4x slower. If all you want is a shadow once would work, but he’s asking for a border.


(spinal) #13

Perhaps -1,-1 and +1,+1 would be good enough.


(Michael Gollnick) #14

That sounds like an interesting idea. I need to find a way how to integrate that in the rendering process as the sprites are rendered column-wise whenever the ray hits a sprite column.


(Michael Gollnick) #15

I had the following in mind:

E.g. the the image is 3 columns (six pixels high). The leftmost column is about to be rendered. I could use the pink operator. If all fields in the operator are empty, nothing is drawn, if not then the pixel will be drawn, no matter if black or white. This imposes some restrictions on the image itself, e.g. it should not have big wholes in it, otherwise it will get transparent at these parts.

Not sure if that works out.


(Josh Goebel) #16

Why wouldn’t you do the same thing but witth a 2x2 cursor vs 3x3?


(Michael Gollnick) #17

It is not fully thought-out by me :smiley:

The picture shows the rendering from left to right. I want the border also on the right side of the image (maybe in my example the rightmost pixel should not be set). The pixel of interest is the one with the ?. So when reaching the right side of the image I must also look at the pixels left to it.


(Michael Gollnick) #18

In my code I use division by 64 of an uint16_t very often. The max value of this value is 4096. I see that the compiler is not generating very nice code for it. So I was thinking about the following assembly code:

lsl high
sbrc low, 7
inc high
lsl high
sbrc low, 6
inc high

The result would be in the high part of the 16bit value. I am not very good at inline assembly. Does anybody have some hints how to implement this efficiently in inline assembly? Or even a smarter way of dividing a 16bit by 64?


(Kevin) #19

Both @Dreamer3 and @MLXXXp are the resident experts in such things if they’ve got some input


(Michael Gollnick) #20

I tried a little myself and came up with this:

vBlockY is an uint8_t
vY is an uint16_t whereas the upper 2 bits will be always zero. This is important for the below assembly code to work correctly.

Old code:

 vBlockY = vY / BLOCK_SIZE;

New code:

asm volatile (
" mov %[vBlockY], %B[vY]\n"
" lsl %[vBlockY]\n"           
" sbrc %A[vY], 7\n"           
" inc %[vBlockY]\n"           
" lsl %[vBlockY]\n"           
" sbrc %A[vY], 6\n"           
" inc %[vBlockY]\n"           
: [vBlockY] "=r" (vBlockY) /* output */
: [vY] "r" (vY) /* input */
:);                     

Not sure if this is really correct. Maybe some people can comment on this.

I used it in some tight loops in my code and it gave quite some boost. I am near to 30fps even in the open spaces.