🤑 Indie game store🙌 Free games😂 Fun games😨 Horror games
👷 Game development🎨 Assets📚 Comics
🎉 Sales🎁 Bundles

Pico-8 to PixelVision8 Tips

A topic by mhughson created 121 days ago Views: 779 Replies: 8
Viewing posts 1 to 4

I recently ported some games from Pico-8 to PV8, and here are the notes I kept to help ease the conversion:

  • PV8 doesn't have +=, -=, *=, /= operators, so do a find an replace of those:
    • eg. x+=1 => x=x+1
  • PV8 doesn't have the if() function (a special version which doesn't require "then...end"), so find and replace.
    • eg. if(x==1)x=2 => if x==1 then x=2 end
  • PV8 uses "print" for logging, so find and replace "printh".
    • eg. printh("hello") => print("hello")
  • Replace core functions names:
    • _init() => Init()
    • _update[60]() => Update(deltaTime)
      • Note: PV8 update runs at 60fps so you may need to adjust logic if not using _update60
    • _draw() => Draw()
  • PV8 uses standard Lua math functions, which you access with math.*
  • Tilemap collision is checked with ReadFlag.
    • eg. fget(mget(x,y,0) => apiBridge:ReadFlagAt(x,y) == 0
  • Clearing the screen is done in 2 steps:
    • eg. cls(0) => (in init) apiBridge:ChangeBackgroundColor(0) (then in Draw) apiBridge:Clear()
  • Drawing the map is also done in 2 steps:
    • eg. map(...) => (in Init) apiBridge:RebuildScreenBuffer() (then in Draw) apiBridge:DrawScreenBuffer()
  • "Colors per sprite" should be 16, so make sure to update your systems SpriteChip:
    • Open the game then navigate to (on windows):
    • C:\Users\<user name>\AppData\LocalLow\Pixel Vision 8\Game Creator Pro\Workspace\Game\data.json
    • Inside "SpriteChip" section change "cps":16
  • Button IDs are different in PV8 from Pico-8. Here is how you need to replace the id:
ID PICO8 PV8
0 Left Up
1 Right Down
2 Up Left
3 Down Right
4 C X
5 X C
6 A ("select")
7 S ("start")

Wanted to add some clarification to the above.


Clearing the screen is done in 2 steps:

Technically you just need to set the background color one time. You can do this in the Tilemap Tool or in your Init() method. If you don't use the Screenbuffer, then you can simply clear the screen with the apiBridge:Clear() call which uses the bg color.

Drawing the map is also done in 2 steps:

In order to use the Tilemap, you need to load it into the Screenbuffer. The buffer represents a "canvas" you can use to draw pixel data into and is optimized to allow scrolling. It's similar to how the Nes's background layer works. To load the Tilemap and prep the Screenbuffer, you'll need to call apiBridge:RebuildScreenBuffer(). You only want to call this once in the Init() method or whenever you make changes to the Tilemap and need to update the display. It's a very expensive operation so use it sparingly. Once the buffer has been built, you can draw it to the screen via apiBridge:DrawScreenBuffer(). This can be used instead of apiBridge:Clear(). DrawScreenBuffer() not only clears the display but draws the contents of the buffer to the display and any missing color data is replaced with the background color.

"Colors per sprite" should be 16, so make sure to update your systems SpriteChip:

To help with people porting over Pico games, I've increased the colors per sprite value in the Sprite Tool. You can now go as high as 16 colors, you don't need to manually modify the data.json.


Eventually, I'd like to add a Pico8 console template to the Game Creator. At a high level, this template will match the colors, resolution and system specs. Down the road, I would like to add an API mapper to better handle Pico8 -> PV8 calls. This way porting won't take as long as it does now. I'll look into the math additions and other logic suggestions above when I can.

Thank you all for this tips, very important for fellow Pico8 user.

(Edited 6 times)

List of pico8 functions ported to Game Creator. Rev.3

Incomplete, will update with new functions whenever I need to port them.

Rev.3 - rewritten rect and rectfill for better performance and clarity
It's purpose is to have easier time running your pico 8 code on Game Creator and show how you can replace pico8 functionality, not copy-paste and forget.
Ported functions:
  • foreach
  • rnd
  • flr
  • add
  • del
  • sub
  • min
  • max
  • abs
  • sin
  • cos
  • btn
  • btnp
  • spr
  • rect
  • rectfill
  • print
Unsupported (or only for specific conditions) functions:
  • fget (working only for tileX,tileY, no support for spriteID)
  • mget
  • line (working only for horizontal/vertical lines)
  • run (so far there is no api for restarting cart, your best bet is to reinstate all the variables and call Init())
Functions that aren't ported yet: (that doesn't mean they are supported, I just didn't take my time to port them)
  • clip
  • pget
  • pset
  • sget
  • sset
  • fset
  • cursor
  • color
  • cls
  • camera
  • circ
  • circfill
  • pal
  • palt
  • sspr
  • all
  • pairs
  • sfx
  • music
  • mset
  • map
  • peek
  • poke
  • memcpy
  • reload
  • cstore
  • memset
  • mid
  • atan2
  • sqrt
  • srand
  • band
  • bor
  • bxor
  • bnot
  • shl
  • shr
  • menuitem
  • cartdata
  • dget
  • dset
Code:
function foreach(t,func)    
    for i,v in ipairs(t) do func(v) end
end
function mget()
    --there is no get sprite id from x,y function in PV8
    return nil
end
function fget(tileX,tileY,flag)
    if tileY==nil then
        return nil
    --there is no get flag for specific sprite id function in PV8
    end
    local f=apiBridge:ReadFlagAt(tileX,tileY)
    if flag==nil then
        return f
    end
    return f==flag
end
function rnd(n)
    return math.random(n)
end
function flr(v)
    return math.floor(v)
end
function add(t,v)
    table.insert(t,v)
end
function del(t,v)
    local ids={}
    local n=0
    for i,val in ipairs(t) do 
        if v==val then
            add(ids,i)
            n=i
        end 
    end
    if n>0 then
        --table.remove(t,n)
        for y,id in ipairs(ids) do 
            table.remove(t,id)
        end
    end
end
function sub(text,startPos,endPos)
    return string.sub(text,startPos,endPos)
end
function min(a,b)
    return math.min(a,b)
end
function max(a,b)
    return math.max(a,b)
end
function abs(v)
    return math.abs(v)
end
function sin(a)
    return math.sin(-a*(3.1415*2))
end
function cos(a)
    return math.cos(a*(3.1415*2))
end
function btn(n)
    return apiBridge:ButtonDown(n)
end
function btnp(n)
    return btn(n) and apiBridge:ButtonReleased(n)
end
function spr(id,x,y,width,height,flipHor,flipVert)
    apiBridge:DrawSprite(id,x,y,flipHor,flipVert,true,0)
end
function InitRectColorArray()
    if lineFillTables~=nil and #lineFillTables>0 then
        return
    end    
    lineFillTables={}
    local len=apiBridge.displayWidth
    for n=1,len do
        lineFillTables[n]={n,{}}
    end
    foreach(lineFillTables, function(t)
        local size=t[1]
        for color=0,16 do
            local current={}
            for j=1,size do
                table.insert(current,color)
            end
            table.insert(t[2],current)
        end
    end)
end
function rect(x0,y0,x1,y1,color)    
    InitRectColorArray()
    local width=max(1,math.abs(x1-x0))
    local height=max(1,math.abs(y1-y0))
    local widthArray=lineFillTables[width][2][color+1]
    local heightArray=lineFillTables[height][2][color+1]
    local x=max(0,(x0<x1 and x0 or x1))
    local y=max(0,(y0<y1 and y0 or y1))
    apiBridge:DrawBufferData(widthArray,x,y,#widthArray,1)
    apiBridge:DrawBufferData(widthArray,x,y+height,#widthArray,1)
    apiBridge:DrawBufferData(heightArray,x,y,1,#heightArray)
    apiBridge:DrawBufferData(heightArray,x+width,y,1,#heightArray)
end
function rectfill(x0,y0,x1,y1,color)
    InitRectColorArray()
    
    local width=max(1,math.abs(x1-x0))
    local height=max(1,math.abs(y1-y0)
    local x=max(0,(x0<x1 and x0 or x1))
    local y=max(0,(y0<y1 and y0 or y1))
    local widthArray=lineFillTables[width][2][color+1]
    for n=0,height-1 do
        apiBridge:DrawBufferData(widthArray,x,y+n,#widthArray,1)
    end    
end
function line(x0,y0,x1,y1,col)
    local color = col or 0
    rect(x0,y0,x1,y1,color)
end
function print(str,x,y,col)
    local color = col or 0
    x = x or 0
    y= y or 0
    --Logger:Debug(x..";"..y..";"..str)
    apiBridge:DrawFontToBuffer(str,pixel_tile_conv(x),pixel_tile_conv(y), "small-font", fontSpacing)
end

Great start! I actually plan on doing something like this for the Pico 8 console template. I'm currently working on a cleaner API and there may be more overlap with Pico 8 in the future to make porting a bit easier.

Thank you! If I end up porting more functions I would like to release this as library as it is perfect example of what library should be. :)

Totally agree. Happy to include it in the Pico Template if you want

(Edited 1 time)

Yeah, I don't think I would release this as library anymore, as I am not feeling like porting just for sake of porting everything pico has, but feel free to use anything/everything from what I have written up here.

Agree, I need to really think through how I would want to support people porting over Pico games. Even with the latest changes, I'm about to push out the two engines have completely different rendering models.

Thanks for sharing though, it's a good start!