This isn't really a game - unless you consider it a short "Walking simulator" - it's more of a tech demo.
The engine is a basic Wolfenstein-3D like 3D engine. It has floor and ceiling textures and render reasonably sized and complex rooms at 60 frames-per-second, in a 128x96 viewport.
- Arrow keys = move
- X = toggle map mode
If anyone feels like something out of it, it's fairly easy to get started with (details below).
impressed by rasterization precision 👍 - I know some wall rendering code that might need a pixel clean up 😜
minor perf comment:
local i=x-flr(x) -- can be replaced by : local i=x%1 -- 2x faster |
@freds72 thanks. %1 feels obvious now you've mentioned it :-), much cleaner.
Regarding the walls, I think sspr just truncates the y and height params before using them. Which means the bottom pixel can jump up and down a bit as they cross integer boundaries.
Calculating the top and bottom, explicitly truncating/rounding them, then subtracting to get the height keeps it a lot more stable.
distance based dimming of colors would also sell the 3d world better.
Might have a play with it.
It depends how expensive the pal(table, 0) command is.
ah yes - need a richer ground texture.
Otherwise you got this strong cut off :/
I'm just sitting here reading your comments and I'm just very confused. (I dunno anything about making a Pico 8 game.)
Making a decent looking noisy ground texture is a bit beyond my pixel art capabilities unfortunately.
I've changed it to exponential fog with a better palette sequence.
At least the hard edge is a bit further away (?)
I love the map. To me this is like Castle Wolfenstein with a 3D view.
So, can you explain how the sprite-to-sprite mapping works? Because I'm trying to write my own game with this engine, and one of the "items" (actually intended to be a moving entity) has to be twice the width of the other items, so I rotated the sprite horizontally and put it in the section with the other items. I've figured out how to get the program to display the two items side-by-side, but as this entity is roughly shaped like a person, I need to display the two squares VERTICALLY. Is there any sort of way to rotate the sprite while rendering in code, or is this engine just Not Designed For portrait-oriented objects?
@IMLXH I'll do my best :-)
So when the game starts it scans the map for tiles between index 32 and 47, in the "scanmapforobjects" function.
When it finds one, it makes an "object"
local ob={pos={x+.5,y+.5},typ=m-32,rel={0,0}} |
So "pos" is the position and "typ" is a number indicating what type of object it is, which it gets from subtracting 32 from the tile index. ("rel" is a working variable that it needs later when rendering).
The "typ" will be used to determine which part of the spritesheet (tab 3) to draw.
It adds the object to a 2D grid called "objgrid", so that it can find the objects near the camera efficiently when rendering.
It draws the objects in the "drawobjs" function.
It basically scans the "objgrid" array around the camera to determine which objects to draw, calculates their position relative to the camera and draws them from back to front.
It draws them one vertical pixel column at a time, so that it can tell when parts of the object are hidden by walls.
It uses the ob.typ field to decide which part of the sprite map to draw. So typ=0 will draw the first 2x2 sprite on tab 3 of the spritemap.
Once it has calculated all of this, the actual drawing is:
sspr(sx,sy,1,16,x,y0,1,y1-y0) |
For example, lets say you want to replace the vase with the red wizard from Sorcerer.
[128x32] | |
This is a 2x4 sprites, whereas the engine only handles 2x2 sprite objects.
But we can fix this by changing the rendering code in "drawobjs", by changing:
-- use z buffer to determine if -- column is occluded by a wall if z<zbuf[x+1] then sspr(sx,sy,1,16,x,y0,1,y1-y0) end |
to
-- use z buffer to determine if -- column is occluded by a wall if z<zbuf[x+1] then if ob.typ==6 then sspr(sx,sy,1,32,x,y0,1,y1-y0) else sspr(sx,sy,1,16,x,y0,1,y1-y0) end end |
Basically we've changed it to draw a 32 pixel vertical strip from the spritesheet instead of 16 pixels when drawing object type 6.
We also need to tell the engine that it should be taller on screen (otherwise it will squash the wizard down to the size of the original vase object).
This is done by editing the otyp array (tab 1), and changing the 7th line from:
{ .3,.35, .4, true}, |
to
{ 0,.8, .4, true}, |
Hopefully that helps a bit? Basically you can use the object "typ" to override the drawing code to do whatever you need it to, as long as you draw one vertical column of the sprite at a time.
I can't think of any way to make it work with rotated sprites though. I think you need to find a way to fit them into the spritesheet upright.
Regarding making it move..
you basically have to:
- Remove it from the "objgrid" 2D array.
- Update its position.
- Add it back into the "objgrid" 2D array.
For example, we could create a global array for tracking our wizard objects (I've called them "ghosts" in the code):
-- gameplay ghosts={} |
Then create some 'ghosts' in the _init function.
-- add some ghosts for i=1,10 do local x,y=i*1.5+20,7.5 local ob={pos={x,y},typ=6,rel={0,0}} add(objgrid[x\objgridsize+1][y\objgridsize+1],ob) add(ghosts,ob) end |
The idea is to add the object to the "objgrid" and our "ghosts" array, so that the rendering code will draw them, but we can also access them easily for gameplay.
Then you can move them in _update60:
-- move ghosts for ob in all(ghosts) do -- remove from grid x,y=ob.pos[1],ob.pos[2] del(objgrid[x\objgridsize+1][y\objgridsize+1],ob) -- update position x+=0.02 if(x>35.5)x=20.5 -- add back to grid ob.pos={x,y} add(objgrid[x\objgridsize+1][y\objgridsize+1],ob) end |
Which should result in this:
Here's an example cart with these changes:
Let me know if that made any sense :-)
That wasn't exactly the problem I have now, but I bet it's one I'll run into at some point, so thanks!
EDIT: dumb noob questions deleted, turned out to TOTALLY be my bad lol
[Please log in to post a comment]