Log In  

Cart #56623 | 2018-09-13 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
1

This is a simple graphic library for the user, "Scrub."

We got into a bit of a discussion last night, he wanted to erase the 3x5 font in favor of more sprites available. I told him he could do it with strings. This is a simple library with no compression to keep it basic.

That discussion forum can be found HERE:

https://discordapp.com/channels/215267007245975552/215267007245975552

USAGE OF FUNCTIONS:

Draw any sprites you want on the SPRITE page.

In immediate mode, command mode, type:

SPRTOCLIP(nn)

Where nn is the number of the sprite you want to convert to a string. Press ENTER.

Then in your source-code, press CTRL-V. Rename the variable from IMG to whatever you like.

To display this string-sprite, anywhere in your code use:

DRAWSTR(xx,yy,tt)

Where xx is x-coordinate, yy is y-coordinate, and tt is the string that got converted. In the case of this example, the string's name is BUNNY. Color 0 registers as transparent so it's suggested you use CLS() at the beginning of your _DRAW() function.

And that's it !

If you have any questions or suggestions, please let me know.

P#56625 2018-09-13 18:15 ( Edited 2018-10-27 17:15)

i am scrub conqueror of worlds

P#56627 2018-09-13 18:42 ( Edited 2018-09-13 22:42)

I suppose it would for you too, if you like. :)

P#56629 2018-09-13 18:45 ( Edited 2018-09-13 22:45)

Why don’t you use base 16 to encode the pixels??
You would get decompression for zero tokens with tonum().

P#56640 2018-09-14 00:58 ( Edited 2018-09-14 04:58)

Freds, how would you do that for source code ?

P#56664 2018-09-14 11:18 ( Edited 2018-09-14 15:18)

Assuming I want the most inefficient sprite rendering function:

— convert sprite # to pico-8
-- clipboard source-code
function sgethex(i,j)
 local s=tostr(sget(i,j),true)
 return sub(s,#’0x000’,1)
end
function sprtoclip(n)
local r=""
  for i=0,7 do
    for j=0,7 do
      r=r..sgethex(n*8+j,flr(n/16)*8+i)
    end
  end
  printh('img="'..r..'"\n',"@clip")
end--cvtsprtostr(.)

-- draw a converted string as
-- a pico-8 sprite, color zero
-- is transparent.
function drawstr(x,y,t)
local c
  for i=0,7 do
    for j=0,7 do
      c=tonum(sub(t,j+i*8+1,j+i*8+1))
      if (c>0) pset(x+j,y+i,c)
    end
  end
end
P#56677 2018-09-14 16:07 ( Edited 2018-09-14 20:07)

Oh, you mean to use HEX, Freds. I didn't know which would be faster. I always count on strings and their indices to bail me out of trouble.

In any case, this is just a simple program to extend graphics to string arrays, something I have always relied on in the past and will in the future.

P#56680 2018-09-14 16:52 ( Edited 2018-09-14 20:52)

If you were willing to sacrifice a part of sprite mem for a temporary buffer, e.g. sprite 0, you could do something like this:

function sprstr(n)
  local s,b="",flr(n/16)*512+n%16*8
  for a=b,b+448,64 do
    -- conveniently, peek4() returns 8 pixels' worth
    s=s..tostr(peek4(a),true)
  end
  return s
end

function strspr(s,x,y)
  local si=1
  for a=0,448,64 do
    poke4(a,sub(s,si,si+10))
    si+=11
  end
  spr(0,x,y)
end

(Pardon if that doesn't literally run, my PC is quite dead right now, and with it my copy of PICO-8.)

This is somewhat simplified from a regular spr() call, where you have w,h dimensions, but the concept could certainly be extended, or maybe hardcoded to at least one more size, e.g. spr8x8() and spr16x16().

The main point is to reduce the CPU workload, which is significant if you're iterating over a lot of pixels, and pass it off to the pseudo-GPU which can blit multiple pixels per cycle as long as they're coming direct from sprite ram.

Note that the code above is engineered for speed, not for string length, so it has entire 0xAAAA.BBBB values, which are overspecified by 3 characters really. If you wanted shorter strings (for, e.g., storing in source code) you could just store AAAABBBB in the string and then recreate the full version from that. It's more work at runtime but maybe it's the difference between fitting and not fitting.

Also, you could use a simpler table version if you don't mind using more Lua memory (or more initializer tokens), which may or may not be an issue:

function sprtab(n)
  local t,b={},flr(n/16)*512+n%16*8
  for a=0,448,64 do
    -- conveniently, peek4() returns 8 pixels' worth
    t[a]=peek4(b+a)
  end
  return t
end

function tabspr(t,x,y)
  for a,v in pairs(t) do
    poke4(a,v)
  end
  spr(0,x,y)
end

Finally, you could sort of combine the two, where you have sprites stored as pure hex (no 0x or decimal point) in source strings, but you spend a few tokens in _init() to convert those strings into the much-faster-to-use table representation for use in _draw(). I'll leave that as an exercise for the reader. :)

P#56712 2018-09-15 20:23 ( Edited 2018-09-16 00:35)

Sorry, your PC is dead, Felice. Backup battery anyone ? :) Your code runs at this time, however.

Yes, the first one is storing 88-characters per 8x8 sprite, as you mentioned an extra 3-characters per 8-pixels read. Hmm ...

Speed is really not that much of a problem. Pico is and apparently always will be REALLY fast.

The storing of an image to a string, however, should not be prioritized for speed as it is a one-time thing used only for the programmer of the cart.

The current compressor I'm using in the paint program I'm working on, for instance, is not fast by any means and is actually clumsy fairly ugly code - yet the compression is high.

Mind you, the routine I wrote to playback to extract the compressed data is considerably quick and streamlined to a T.

Playback of the string in this particular case SHOULD be prioritized for speed. The very best that can be.

Your concept of just moving memory from a string directly into sprite zero is a good idea indeed. Then you can use SSPR or SPR as you like for it.

P#56719 2018-09-16 00:15 ( Edited 2018-09-16 04:15)

PICO-8 is very fast for some applications, e.g. ones that tend to resemble those of the era PICO-8 is influenced by, but people don't always write to that spec.

As a general rule, many people will push the envelope as far as it will go, whatever envelope they may be in. So you'll find carts here on the BBS that do a great deal of sprite work (well, these aren't really sprites, they're blits, but whatever), to the point that they do use up the entire CPU budget. This is common for things like demos, bullet-hell games, Outrun-style games, etc.

Thus, it's not always reasonable to say "speed is really not that much of a problem." It's a problem when it is, and sometimes it is.

Premature optimization is, of course, the bane of programming, but there are some things you know for certain will be your bare minimum, like the worst-case number of bullets in a bullet-hell shooter. In fact, I'll use that as an example...

If you already know you're budgeting for, say, up to 1024 bullets on-screen at a time, at 60fps, and for some reason they all need to come out of one of these string-packed sprites, then you can do a little math and work out some basic requirements for code speed:

  • PICO-8 runs its CPU, or maybe we should say its LPU (Lua Processing Unit) at 4MiHz.

  • At 60fps, that's just under 70K Lua operations(-ish) per frame.

  • If worst case is 1024 sprites, they have to be able to draw in about 68 cycles each, and that's if there's no other code running at all.

  • Given that there are 64 pixels in a sprite, and pset() is at least 1 cycle (can't remember how many it actually is), you need 64 of those 68 cycles just to draw the sprite if you use pset()s to draw individual pixels. That leaves no room for loop overhead.

Given the above, you can plan ahead, either deciding to lower your target FPS, lower your target bullets/frame, or change your algorithm to take advantage of built-in acceleration (e.g. an spr() call from a rapid-fill cache, as I did).

But, as I said, if you're doing an app with low perf requirements, sure, PICO-8 is stupid fast relative to your requirements. I guess it depends on what you're trying to get done. Most of my professional work was on a multi-platform console game engine, so my goal was always to provide the maximum possible bandwidth to the content creators (which they would always fritter away, sigh), but if you're in more direct control of the scope of your project, then you can obviously make whatever tradeoffs you like, e.g. coding time vs. framerate vs. token count vs. content delivered vs. complexity vs. etc. etc. etc...

Edit: PS: Sorry for the long blahblahblah post, I just wanted to explain why I often see things from a different perspective. I realize this is kinda OT and I don't expect responses.

P#56722 2018-09-16 03:54 ( Edited 2018-09-16 09:19)

No, that's fine, Felice.

I always appreciate your input (and output for that matter). :)

I've always been a kind of "Get 'er done" programmer. Which is good. I remember working for American Church Lists years ago, a database company that kept track of all the churches in the world.

Their "senior programmer" left and I was to take over.

I was about 24-years old when I did. And to me I could answer any question or code with QBasic 1.0. I was that confident.

Well they gave me the Lattice C source code from the last programmer and I realized immediately that while he had entered code for INPUT and OUTPUT, he deliberately left out the code for calculating how to handle the data.

I pointed this out to the boss and she grumbled that - well - he did leave a little angry. But wanted to know if I could do the code he left out.

I asked them what they wanted to do with the code. She said better to show me than explain it.

You would have raw data from churches entered here. It would take 2-days to calculate before a hardcoded output could be achieved.

I wrote down everything in my notebook, physical paper tablet back then, then sat down to the computer in Q-Basic 1.0.

I went ahead, plonked down, and started to write it. I got the genius idea of taking the input and converting it straight to output.

I think I got the idea from the fact that the last code did not have a calculation for the data.

There was a conversion between that I wrote, but instead of keeping it in memory, the "work" table was already the output.

The results ? I wrote what they wanted in super-slow QBasic 1.0. It calculated and printed everything necessary in 2-hours, not 2-days. It also had bells, whistles, lights, and a screen-saver made of colored "*" :)

I had a ball ...

And yes, I didn't program intelligently. I programmed with the idea in my head, "Get 'Er Done." and it worked marvelously.

Also worked when I wrote Doomsday BBS years ago, running in QBasic 4.5. I also wrote some Door games, "Vampire Hunter" comes to mind.

"Orbs of Ankhar" another, where callers would add their own adventure rooms for other players to try out. Good times.

But I digress. Once again my method is to "get 'er done" and usually I don't program for finesse, but speed, yes, is always of interest.

I'm curious, though for my current coding. How would you handle or optimize for speed something like this ?

function trx(x,y,t,typ)
local c,d,u=0,0,0
  if (col==nil) col=7
  if x==-1 then
    x=64-vlenx(t,typ)/2
  elseif x==-2 then
    x=128-vlenx(t,typ)
  end
  if y==-1 then
    y=64-vleny(t,typ)/2
  elseif y==-2 then
    y=128-vleny(t,typ)
  end
  for i=1,#t do
    c=fnca(t,i)
    if c!=95 then
      if (u!=1 and c>=97 and c<=121) c-=32
      d=c-32
      if typ==nil then
        for j=0,7 do
          for k=0,psize[c] do
            if btst(fnca(iprop,flr(d/16)*128+j*16+d%16+1),k)==1 then
              pst(x+k,y+j,col)
            end
          end
        end
      end
      x+=psize[c]
    end
    if (u==0) u=1
    if (c==46 or c==63 or c==58) u=0
    if (c==95) u=2-u
  end
end--trx(...)

Don't worry about the functions that aren't there. It should be self-explanatory - or I can show the code or describe if you like.

P#56737 2018-09-16 13:17 ( Edited 2018-09-16 19:40)

It's not 100% clear what fnca() does. The name isn't very mnemonic, though it's hard to blame you for short function names with PICO-8's screen size.

Does it return the bitmask for a row of a glyph?

If that's the case then I think I'd probably pre-calc a table of 256 entries (assuming glyphs don't exceed 8 pixels wide) of the hex version of the bits, and then poke that into sprite mem and do an spr() call, as before.

Like, let's say the table looks like this:

bit2pix =
{
  [0]=   --start with 0 rather than 1
  0x0000.0000,
  0x0000.0001,
  0x0000.0010,
  0x0000.0011,
  0x0000.0100,
  -- ...
  0x1111.1111
}

Obviously we'd want to calculate it, rather than declare it. I just want to show how it converts 8 bits into 8 nybbles.

Then I'd replace your "for j=" loop with something like this:

        for j=0,7 do
          local bits=fetch_glyph_row_bits(c,typ,j)
          poke4(j*64, col*bit2pix[bits])
        end
        spr(0,x,y)

I'm not sure what fnca() does, so I inserted my own call that simply returns the appropriate MSB-justified bits for a given character in a given typeface at a given row.

I should note that I think nybbles in memory are pair-swapped from what's on screen. I can't remember for sure, but I think 0x12 in memory is 2,1 on screen. If so, the table would need to be similarly nybble-swapped.

If you need glyphs that are more than 8 pixels wide, then you'd need to be able to handle that with, perhaps, two poke4() calls per row and a w=2 arg to spr(). Given that poke4() is very speedy, I'd probably just do two of them unconditionally, rather than trying to choose 1/2 conditionally.

If you need a background color other than 0, you could add fgcol*bit2pix[bits]+bgcol*bit2pix[255-bits].

The reason why I'd do it this way is that, even though a character like "l" might typically only require a few pixels to be set, the loop and bit-test overhead is significant per pixel, while PICO-8's pseudo-GPU is unnaturally speedy for the era it harks back to. I don't remember what I found when I tested it, but I seem to recall that it will do at least two pixels per cycle, possibly 4 or 8. Sorry, I can't remember and my notes are on my dead PC. :) So overall those constant, mandatory cycles for the full-width sprite add up to fewer than iterating conditionally over fewer pixels.

As an aside, while I like abstraction, on this tiny platform I would probably embed some of the code that's in your fnca() or my fetch_glyph_row_bits(), because I'm pretty sure that j=0,7 loop could run a lot faster if some of the typeface header reading done inside such a call were to be cached outside the loop and then dereferenced directly. Depends on your willingness to trade off abstraction for speed in leaf functions like this one.

P#56768 2018-09-17 02:55 ( Edited 2018-09-17 07:00)

Hi, I'm on the move, Felice. Going to reply proper to this later. Here is FNCA. It's pretty straight-forward.

-- return ascii # from string
function fnca(a,b)
  return asc[sub(a,b,b)]
end--fnca(..)

ASC[] being generated from the sample you gave me earlier:

asc,chr,tab={},{},"\0\1\2\3\4\5\6\7\8\9\10\11\12\13\14\15\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31\32\33\34\35\36\37\38\39\40\41\42\43\44\45\46\47\48\49\50\51\52\53\54\55\56\57\58\59\60\61\62\63\64\97\98\99\100\101\102\103\104\105\106\107\108\109\110\111\112\113\114\115\116\117\118\119\120\121\122\91\92\93\94\95\96\65\66\67\68\69\70\71\72\73\74\75\76\77\78\79\80\81\82\83\84\85\86\87\88\89\90\123\124\125\126\127\128\129\130\131\132\133\134\135\136\137\138\139\140\141\142\143\144\145\146\147\148\149\150\151\152\153\154\155\156\157\158\159\160\161\162\163\164\165\166\167\168\169\170\171\172\173\174\175\176\177\178\179\180\181\182\183\184\185\186\187\188\189\190\191\192\193\194\195\196\197\198\199\200\201\202\203\204\205\206\207\208\209\210\211\212\213\214\215\216\217\218\219\220\221\222\223\224\225\226\227\228\229\230\231\232\233\234\235\236\237\238\239\240\241\242\243\244\245\246\247\248\249\250\251\252\253\254\255"
for i=0,255 do
  c=sub(tab,i+1,i+1) chr[i]=c asc[c]=i
end tab=nil -- 256 chars

BTW, notice anything unusual from the table above ? :)


I'm back. Okay, no FNCA does not do what you wrote above. Since your reply is about thinking it does I may not be able to make use of that.

FNCA to break the name apart is two sections. "FN" for old QBasic where you could use DEFFN to define a small function, "C" for character, and "A" for ASCII.

in QBasic I could do easy functions:

DEFFN CA(A$,A)=ASC(MID$(A$,A,1))

Usage: FNCA(string,number), where "FN" is always required as the prefix.

The TRX routine above is used primarily for displaying 2-bit proportional text. The storage of IPROP, single string, is very compact indeed.

"TR" for transmit. "X" for anything and everything.

-- b+w 128x128 system images
--  3242 chars (128x128)
cprop="#031050a042306010401#0f1051f1e13090102020404#061#041050a0508090101041504#078#031001f0e0406#031040e1f001f#034#031#03a143215#031041504#072#09f3109#03202040402#051#031#0540016#03401#051#031$0236040606080f0c0f0606#058#0310e090609090c0102080909#054#032110d0508080a07010809090102021f04100b04040609080704060e#051#03808090402080f08090209080102021f040409040109080909010904#03204#032#0360e0f06080606010603#03108#03104$021e060706030f0f06090706090#130906110909090501010109020409011b09091d090901090101010902040501150b09150907010907070d0f0204030#130d091d0f090109010109090204050#130909010909090501010909020509091109090e090706030f010e090703090f110906$02170607061f09#180f0f#03f04#0390909090409#180803010c0a#0390909010409#140a110403020c11#03909050604090a15040a0203040c#05709030804090a150a040103080c#0510505090409041511040103100c#0510a09060406040a11040f0f#03f001f$0211#031#038#036#03101040101#072#031#038#032#031#05101#09607060e06070607010609010b0706#0380901090902090901040501150909#03e0901090f02090901040301150909#03909010901020e090104050#130909#03e07060e060208090105090#130906#0d306#053$0131#0dc0303007f#091#0d603062641070e060e0709#16090f060306197d090901010109#140a090403#03c006d090901060109#1350409020603060065070e010801090a150a0e01060306007d010801070606040a11080f0c0303007f0108#0f6$025204#0b202010401040103030507#034#03205#03102020202020102#0310707070702070102010407070102#072#03205#031020202#05102#05707#0720201040107070303#071$0407f001c#0582a06221c0804001c03407f7f3e1c1c491c0933220872002203507f7f7f7f142a#038cc226831004104547f7f7f7f1c6b7f0855225f51085108557f7f7f7f142a#038#33e6841003110547f7f3e1c14491c48cc221422007260507f001c#0582a3088221c1c#034604$021501c493e40047f1808181c1c450e2001261c2a22380a41141c30#26#13005571c1c7b2c1d5d2a2a6f6b7b1511081501087f733616#53e7f556b7308110455757f1c7b1d2c5d3b2a156b7b542e021571002a220b3041131c15223a727063057f7f493e06407f01081f1c1c21606901$0261608681600680f0055#048001f8ff1f1614681600680706#06c002fcff3f16226836006c0f0e11#04e0040e0070164968e6ff671d5c#06f008e6ff671622680e0070387855#04f81f36006c161468fcff3f307#07fc20160068160868f8ff1f007811#04fe40160068160068$01c18$0181866367c#03e18600c0818#066#033c667f0#631b1818302a18#063#033c00363c366e#03c601c7e#06180018007f60183b#03c602a18007e#03c#06363e6c330018300818180018060018#0418#63e00600c#0418001803$019c$0263e1c3c7e707e387e3c3c18186#0463c6318#63018060c60#641818187e18#63b1830386c3e36303c66#0560060606b181860#6306618#63c0018187e183863180c607e#640c663018186#046003e3c7e3e603c3c0c3c1c1808#0618$0174#081c$01e22787e7c1e7e7c3c663c78660663663c596866063606060#6318603606776e66556c360#633636067e18601e067f7e663d64660#6306067#6318601e066b7#63027#630c66060#651866364663#641c6238783e7c067c663c3c667e63663c$0403e3c3e707e667673#647e3c003c#04#661800#6533c66320c00300800#6618186636631866180c18301c00363c1e1818661e6b183c0c0c183036#03618361818760e7f3c18460c003063#0363c660e186c067#63187e3c003c007e$0214$01e6c#036006#037#0461830061c#063c3c363c6c3c187c3e#04661837363c1e60660#657c#641c3836187f6e667f6c660#637618#6418301e186b#640c66360#6306186c661830361863#64047c1e3c7c3c1860661830663863663c#0de3c#041e$033c#0cfefefe1c003e3e36703c#653#647e9282fe220066336e180c6636633c66329282fec100663306180c661e6b186618fe82fe#04363606180c760e7f3c6c4c9282fe7#04630060e386c067#6407e9282fe88#03678#0e3c00fefefe0700"
iprop=decompress(cprop) cprop=nil

Decompressing this string yields 2048 bytes in an 8-bit single string total for a 128x128 2-color area. The size of each normal character is calculated early in the program, found in the PSIZE[] array range of 32-159. > 127 as I want a few system images to also be proportionate whereas the remainder can be default spacing of 8x8.

P#56785 2018-09-17 11:32 ( Edited 2018-09-17 17:12)

Also, can't seem to get your code to work correctly:

function main()

t=sprtab(1)

cls()
tabspr(t,64,64)

end

function sprtab(n)
  local t,b={},flr(n/16)*512+n%16*8
  for a=0,448,64 do
    -- conveniently, peek4() returns 8 pixels' worth
    t[a]=peek4(b+a)
  end
  return t
end

function tabspr(t,x,y)
  for a,v in pairs(t) do
    poke4(a,v)
  end
  spr(0,x,y)
end

main()

Cart #56789 | 2018-09-17 | Code ▽ | Embed ▽ | No License

P#56790 2018-09-17 12:56 ( Edited 2018-09-17 16:56)

The code in the cart doesn't match the code you're quoting.

The code in the cart calls tabspr(1,64,64).

It needs to call tabspr(t,64,64), as in the quote.

P#56821 2018-09-17 21:58 ( Edited 2018-09-18 01:58)

As an aside, setting tab=nil in your code would be a good idea in a system limited by memory, but most people end up confined by tokens, so they'd probably rather sacrifice an obsolete 256-byte string on the 2MB heap than the three tokens needed to clear its last remaining reference.

In small programs, no big deal either way.

And yes, I noticed you swapped cases. If you need interop with regular ascii, it's a useful trick, but I personally wouldn't bother. Sometimes it's better to work with the platform for what it is, rather than what it ought to be. "When in Rome," and all that.

P#56822 2018-09-17 22:04 ( Edited 2018-09-18 02:10)

Question:

> The TRX routine above is used primarily for displaying 2-bit proportional text.

vs.

> Decompressing this string yields 2048 bytes in an 8-bit single string total for a 128x128 2-color area.

This is not clear. Is the bitmap for the glyphs one-bit or two-bit?

I'm assuming it's one-bit and you meant to say 2-color in the first quote.

P#56823 2018-09-17 22:09 ( Edited 2018-09-18 02:09)

The code runs now but won't show an image, and there is one in sprite slot # 1

Cart #56829 | 2018-09-18 | Code ▽ | Embed ▽ | No License

function main()

t=sprtab(1)

cls()
tabspr(t,64,64)

end

function sprtab(n)
  local t,b={},flr(n/16)*512+n%16*8
  for a=0,448,64 do
    -- conveniently, peek4() returns 8 pixels' worth
    t[a]=peek4(b+a)
  end
  return t
end

function tabspr(t,x,y)
  for a,v in pairs(t) do
    poke4(a,v)
  end
  spr(0,x,y)
end

main()

It looks neat, I wanna see it run.

P#56830 2018-09-17 23:42 ( Edited 2018-09-18 04:02)

Okay, I realized my license wasn't tied to my dead PC and installed PICO-8 on this machine. Now I can run it and see the problem. I was multiplying by 8 pixels instead of 4 bytes in sprtab(). Use this instead:

function sprtab(n)
  local t,b={},flr(n/16)*512+n%16*4
  for a=0,448,64 do
    -- conveniently, peek4() returns 8 pixels' worth
    t[a]=peek4(b+a)
  end
  return t
end

Sorry, off-the-top-of-one's-head code is always subject to this kind of thing. :)

P#56833 2018-09-18 00:42 ( Edited 2018-09-18 04:42)

Ah ! There it goes. Okay, now my routine would take 32-bytes of a string to plot a single 8x8 sprite. How many bytes is this table taking and ... how can I view the size of it ?

I tried:

print(#t)

And it comes back as zero.

As for the ASC() swapping. If you try:

print(chr[65])

By default you get that lowercase looking "A," not the uppercase "A" as it should be. I can't have that as TRX() in -3 mode means to use ZEP's default font, thus the table swapping.

> The TRX routine above is used primarily for displaying 2-bit proportional text.

vs.

> Decompressing this string yields 2048 bytes in an 8-bit single string total for a 128x128 2-color area.

This is not clear. Is the bitmap for the glyphs one-bit or two-bit?

I'm assuming it's one-bit and you meant to say 2-color in the first quote.

Heh- I'm always doing something and never having an easy way to explain it. What I mean to say is the data is 1-bit (from 8) and 2-colors (from 16). 2048-bytes being the total of a 128x128 2-color field.

. . .

As for running out of tokens first instead of memory. Really ? Wow, I am not expecting that. I think I had oodles left over in that Haunted House game. I just code the way I do and it seems to work out. I =DO= know that my current paint program has already used up 1114 tokens with no actual running paint program up yet, just the functions for it, so maybe that's a concern.

How much RAM are we given to code in ?

P#56849 2018-09-18 12:22 ( Edited 2018-09-18 16:31)

The Lua garbage-collected heap is 2MB, but that doesn't necessarily mean you'll have 2MB of data before it's full. Lua data all comes with some overhead.

You might think that a table of five numbers should be 5 num * 4 bytes/num = 20 bytes, but it's a lot more than that. Every variable has some overhead, and every table has a lot of overhead, since it's internally represented as a hash map, not a simple array.

It's probably best to think of having maybe half a meg and seeing how it works out. It's not good to fill the heap anyway, because it causes more and more garbage collections to occur.

As for why #t comes back 0, #t is only meant to return the maximum index of an array-style table, e.g.:

> t = {5,6,7,8,9}
> ?#t
5

But if you add elements with specific indices that fall outside of the current 1..(#t+1) range, or are not integers, they won't be counted, so:

> t = {x=5,y=6,z=7,[1.5]=8,[0]=9}
> ?#t
0

If you want to iterate over all members of a not-strictly-array-style table, you have to use pairs(). If you want the total count of those members, iterate with pairs() and increment a counter. I think in full-blown Lua there's a way to query that information, but not on PICO-8.

P#56861 2018-09-18 15:54 ( Edited 2018-09-18 20:56)

Is PAIRS() used for a 2-dimensional array or ... something else, Felice ?

AFAIK I've never run into garbage collection. Could you please post an example program that engages it ?

P#56933 2018-09-20 11:37 ( Edited 2018-09-20 15:37)

The pairs iterator returns every key,value pair in the table:

function test()
  local t=
  {
    x=3,y=4,
    5,
    bat="man",
    6,7,
    [false]=0xfa15.e
  }
  for k,v in pairs(t) do
    print(tostr(k)..": "..tostr(v))
  end
end

> test()
1: 5
2: 6
3: 7
false: -1514.125
bat: man
x: 3
y: 4

Notice though that there is no guaranteed order when iterating with pairs(), because it simply walks the underlying hash map directly, and hash maps are geared towards lookup speed, not ordering.

As for garbage collection, all you have to do is keep creating new variables, tables, strings, etc. When a new one is created, it's basically added to the end of the heap. When you run out of space after the heap, garbage collection is done to delete all orphaned objects (strings, tables, values, etc. which no variable actually references anymore) and compact the remaining objects to make room for the next new one. As you can imagine, if the heap is nearly full even with orphaned objects removed, it's not going to be long before the next GC.

A GC will be somewhat painless on a normal speedy PC, but on something like a PocketChip it might cause the virtual machine to slow down. Some of these small system-on-a-chip solutions can barely run PICO-8 to begin with, so if you start making them shuffle a lot of memory around every frame to make room, they'll start to stutter. If you don't strictly need to make new objects, it's best to re-use old ones, which is kind of a constant across all programming, even with non-GC allocations. It's just a good idea.

I don't think I've seen you do anything that would cause excessive GC's. That note was more of a future hazard warning than a needed fix for you.

P#56934 2018-09-20 13:12 ( Edited 2018-09-20 17:20)

Really useful stuff, I was wondering how people generated those. Thanks !

P#58123 2018-10-18 20:07 ( Edited 2018-10-19 00:07)

Scrub, I lost the forum we were chatting in. Computer problems. Please let me know how to return and what the name of it is. Thanks.

P#58484 2018-10-27 13:14 ( Edited 2018-10-27 17:14)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-03-28 15:47:23 | 0.031s | Q:53