Regression of Sprite:copy() in 1.13.2

SDK 1.13.2 - Windows and Linux

Hi Team,

I had recently realised that my game originally build and published in 1.12.3 has started breaking as soon as I updated my Playdate to 1.13.2.

I had issues with collision detection resolution and had originally found this thread (1.13 firmware regression: collidingSprites ignores collisions) as it looked like a similar issue. However, after rebuilding the game in the latest SDK and applying the suggested fixes, it was still broken.

I had also previously written related threads on the copy function as seen here and here as I do use copy quite frequently.

So after much digging and debugging (BTW shoutout Panic for getting the debugging adapter to work in Windows and Linux :playdate_heart:), I was in the end able to fix my issue via workarounds, but it came as a result of realising that Sprites' copy() function seemed to have regressed.

I haven't been able to go deep into exactly how much of a problem it is, but the 2 main issues that I had encountered are:

  1. If a sprite of a class name X is verified using isa(X), it will evaluate to true. However if you copied X and tested the copy with the same function, it will return false instead of true.
  2. Sprite's custom assigned variables do not copy over. E.g. self.a = 'a' will result in the copy having self.a = nil.

I was able to replicate this with the following example below:


function Arm:init()
    self.size = "Normal"

function Leg:init()
    self.size = "Long"
import "CoreLibs/graphics"
import "CoreLibs/sprites"

import "Arm"
import "Leg"

local gfx <const> =

local arm = Arm()
local leg = Leg()

function startExample()
    print("is arm an Arm?", arm:isa(Arm))
    print("is leg a Leg?", leg:isa(Leg))
    print("is arm a Leg?", arm:isa(Leg))
    print("is leg an Arm?", leg:isa(Arm), '\n')
    print("arm className", arm.className)
    print("arm size", arm.size)
    print("leg className", leg.className)
    print("leg size", leg.size, '\n')

    local armCopy = arm:copy()

    print("is arm copy an Arm?", armCopy:isa(Arm))
    print("is arm copy a Leg?", armCopy:isa(Leg), '\n')
    print("arm copy className", armCopy.className)
    print("arm copy size", armCopy.size, '\n')

function playdate.update()
    gfx.drawText("Running example, see logs!", 50, 50)


When executed it produces the following output:

is arm an Arm?	true
is leg a Leg?	true
is arm a Leg?	false
is leg an Arm?	false	

arm className	Arm
arm size	Normal
leg className	Leg
leg size	Long	

is arm copy an Arm?	false <-- should be true
is arm copy a Leg?	false

arm copy className	Arm
arm copy size	nil	<-- should be 'Normal'

This worked as expected in 1.12.3, but the results above are from 1.13.2.

For those interested, my workarounds for these are:

  1. I replaced the use of isa() with comparing className. I have not been able to see what the actual implementation of isa(), I am not sure if it uses className or if it uses something else, or the same attribute from somewhere else.
  2. I created extra functions to plug the missing attributes that I needed. I simply executed them after running :copy(). One can also implement their own copy functionality and override one provided by the library, but I decided against it.


1 Like

This happened as a result of my "cleanup" of the sprite code, which turned out to be a really bad idea--besides weird regressions like this, it had a sizeable impact on performance that we didn't see until after we'd released it. We have a fix in beta now and I've confirmed that it fixes your problem here. Barring any surprises we should have that out.. early next week I hope? I'm not sure if we have any Catalog fixes we also need to get out that might delay that.

No problem @dave, it happens :playdate_upside_goofy:

Thanks for confirming the fix in the beta.

1 Like