Playdate Nim bindings - C performance, Python like syntax

I’ve made a lot of progress however one of the simplest things has still got me stuck. I can’t seem to scale a sprite like below with setSize(). VScode tells me it wants

setSize(this: ptr PlaydateSprite; sprite: LCDSprite; width: float;
height: float)?

# create bitmap
my_Bitmap = try: playdate.graphics.newBitmap(MY_IMAGE_PATH) except: nil

# create sprite
my_sprite = playdate.sprite.newSprite()
my_sprite.add()
my_sprite.moveTo(200, 120)
my_sprite.setSize(2, 1)

I don’t know what the difference is between LCDsprite and playdateSprite?

I think you found an oversight in the bindings. setSize signature is supposed to be setSize(this: LCDSprite, width: float, height: float), so that will be fixed in the next version!

The difference between the two types is that LCDSprite is the object that represents an individual sprite, PlaydateSprite is instead an SDK global object that provides essential functions related to sprites (such as creating a new sprite) but not bound to a single sprite in particular.

Another thing: setSize on a sprite doesn't scale it, to do that in the C/Nim API I suspect you should use LCDBitmap's drawScaled and use setDrawFunction on a sprite to use it as the sprite drawing method.

No worries I'll stay away from scaling for the moment.

Sorry to be a pest but another issue that I can't solve is after I compile for playdate simulator I get the following error. It all seems to work but the error worries me.

15:23:37: SDK: C:\PlaydateSDK
15:23:37: Release: 2.2.0
15:23:37: CMD: playdate.pdx
pdx directory not found: playdate.pdx
15:23:37: Loading: E:\playdate\Games_nim\test_05_Player_sprite\test_05_Player_sprite.pdx
Loading C API game: E:/playdate/Games_nim/test_05_Player_sprite/test_05_Player_sprite.pdx/pdex.dll
pc_sprite_free: non-NULL value required for argument 's'
15:23:37: Loading: OK

It seems to only happen when I use this code that is imported to create the player:

type
Player* = LCDSprite

proc initPlayer*(xPos: float, yPos: float, image: LCDBitmap): Player =
result = new(Player)
result = playdate.sprite.newSprite()
result.setImage(image, kBitmapUnflipped)
result.moveTo(xPos, yPos)
result.add()

Also the pdx directory not found has been like that from day one but I just ignore it and load the file manually in the playdate simulator.

That warning is probably caused by this line:

result = new(Player)

Initializing a LCDSprite this way is not supported, then you deallocate it (warning here, because result didn't have a valid handle to a LCDSprite) to replace it with a valid playdate.sprite.newSprite().

This error:
pdx directory not found: playdate.pdx
I'm not sure what's causing it without the project at hand.

Are we able to extend the sprite class like in lua? Or is it better to create a object that tracks all the info then gets the sprite to read that info at another point.

This works , not sure if this is a bad idea but I'm not getting any errors so happy days.:slight_smile:

type
MySprite* = ref object of RootObj
image*: LCDBitmap
sprite*: LCDSprite

initialize player and properties

proc init_my_Sprite*(image: LCDBitmap): MySprite =
result = new(MySprite)
result.image = image
result.sprite = playdate.sprite.newSprite()
result.sprite.setImage(result.image, kBitmapUnflipped)
result.sprite.add()

Yes, using composition like that is fine.

Regarding directly subclassing LCDSprite or similar, for now it's not completely supported, you could hack something like your attempt above (that works but is not an intended way to to it) but I guess it is going to be supported sometime soon.

I haven't tried a build for device until now and I'm getting the below error any clues on how I fix this. I've seen similar errors in discord but it's not clear to me how to fix the problem.

Error: invocation of external compiler program failed. The system cannot find the file specified.
Additional info: Requested command not found: 'arm-none-eabi-gcc.exe -c ...

Additional info: Requested command not found: 'arm-none-eabi-gcc.exe`

Seems like you need to install the arm compiler.

I thought this was meant to be done in MSYS2 tool but it keeps saying no gcc is installed. Is there another way to install the compiler?

I'm a bit out of my depth here, not on windows. Tiny bit of experience with build environments there. Do you use mingw inside msys2? Would say you need this: Base Package: mingw-w64-arm-none-eabi-gcc - MSYS2 Packages

Thanks Nino that got me on the right track. I've solved the problem. It seems on windows 11 the gcc files didn't get installed correctly. This is how I got it working if anyone else has issues with the gcc files not found:

-reinstall MSYS2 from https://www.msys2.org/
-run/open MSYS2
-in the command line type:
pacman -S mingw-w64-ucrt-x86_64-gcc

-in the command line type:
pacman -S mingw-w64-x86_64-arm-none-eabi-gcc

-in the command line type:
pacman -S mingw-w64-clang-x86_64-arm-none-eabi-gcc

-in the command line type:
pacman -S mingw-w64-ucrt-x86_64-arm-none-eabi-gcc

This might be overkill to install all the package but it saved me stuffing around. Building in VS code with the "nimble -device" now works with no errors and builds superfast.

4 Likes

I'm still using nim just haven't had much time lately. Here is a silly experiment I did to compare lua vs nim and well nim rocks! No sprites were used.

lua 17 fps
500 raindrops no collisions

nim 50 fps
500 raindrops with collisions
playdate-20240320-215423

5 Likes

This is great! Happy to see that.
Also, cute experiment/game!

Regarding the bindings, we are working on the 0.14 release.
Nim 2+ support and more on the way!

1 Like

It's very impressive (and cute)! I assume that you are not using sprites for raindrops?

Thanks Samdze and Daeke,

That's great to hear about Nim2 bindings Samdze!

That's correct Daeke no sprites were used. Probably not the best for battery life as it's a full screen redraw. I haven't figured out how to redraw dirty areas for bitmaps and geometry like they do with sprites just yet, though in this case it probably would make much difference.

2 Likes

Right. The screen works in dirty rows so I don't think you'd gain much.

Yeah, all lines are probably changing every frame indeed.

Note that your comparison might actually be selling Nim short, considering it's being limited by the system's 50 fps. You could also post the cpu usage for a clearer picture

Very nice scene, BTW

Why dirty rows? I have almost no experience with this stuff, but the one time I worked with HTML Canvas doing pixel level rendering, marking for redraw was done in rectangular regions.

Edit: I'm guessing it's just less overhead/simpler to treat the problem as 1D since you don't have to track overlapping regions and is closer to how fundamentally the image is drawn in general.

Would it be possible to switch it to draw by column? Given the screen dimensions statistically you are more likely to run more efficiently that way (I mean, still not much to be gained from this example as there's like maybe 5-8% of vertical drawlines being untouched, but you get what I mean)

That's how the screen hardware receives data. So even if you work more granularly you still have to send whole rows to be updated.