When I change my paddle class to extend sprite, everything breaks

When I change my Paddle class to extend playdate.graphics.sprite instead of nothing (and then change all the sprite references to self), it breaks my whole game.

Paddle class:

import "CoreLibs/sprites"
import "CoreLibs/graphics"

local gfx <const> = playdate.graphics

class("Paddle").extends(gfx.sprite)

function Paddle:init()
    Paddle.super.init(self)

    self.transform = {
        x = 200,
        y = 220,
        width = 32,
        height = 8,
        speed = 0
    }
    self.type = "paddle"

    self:setImage(gfx.image.new("images/paddle"))
    self:moveTo(self.transform.x, self.transform.y)
    self:setCollideRect(0, 0, self:getSize())
    self:add()
end

function Paddle:update()
    Paddle.super.update(self)

    local transform = self.transform
    local change = playdate.getCrankChange()

    if (transform.x - transform.width / 2 + change < 0) then
        transform.speed = 0
        transform.x = transform.width / 2
    elseif (transform.x + transform.width / 2 + change > 400) then
        transform.speed = 0
        transform.x = 400 - transform.width / 2
    else
        transform.speed = change / 2;
    end

    transform.x += transform.speed

    self:moveTo(transform.x, transform.y)
end

The console shows the following error:

Update error: ball.lua:42: attempt to perform arithmetic on a nil value (local '(temp)')
stack traceback:
	ball.lua:42: in function <ball.lua:27>
	[C]: in field 'update'
	paddle.lua:27: in method 'update'
	main.lua:26: in upvalue 'updateGame'
	main.lua:37: in function <main.lua:36>

and ball.lua:42 is transform.x += transform.xspeed which works perfectly fine if Paddle is not a sprite

This bug is driving me crazy. I need Paddle to be a sprite so I can access its type and transform easier when I'm doing collision checking.

Just for posterity, here's the full ball.lua file:

import "CoreLibs/sprites"
import "CoreLibs/graphics"

local gfx <const> = playdate.graphics

class("Ball").extends(gfx.sprite)

function Ball:init()
    Ball.super.init(self)

    self.transform = {
        x = 155,
        y = 110,
        xspeed = 2,
        yspeed = 2,
        width = 8,
        height = 8
    }
    self.type = "ball"

    self:setImage(gfx.image.new("images/ball"))
    self:moveTo(self.transform.x, self.transform.y)
    self:setCollideRect(0, 0, self:getSize())
    self:add()
end

function Ball:update()
    Ball.super.update(self)

    local transform = self.transform

    -- bounce the ball if it hits the left or right sides
    if (transform.x + transform.width / 2 >= 400 or transform.x - transform.width / 2 <= 0) then
        transform.xspeed = -transform.xspeed
    end

    -- bounce the ball if it hits the top or bottom sides
    if (transform.y + transform.height / 2 >= 240 or transform.y - transform.height / 2 <= 0) then
        transform.yspeed = -transform.yspeed
    end

    transform.x += transform.xspeed
    transform.y += transform.yspeed

    local _, _, collisions, collisionsLen = self:moveWithCollisions(transform.x + transform.xspeed,
        transform.y + transform.yspeed)

    -- check if there are any collisions
    if (collisionsLen > 0) then
        -- flip the direction depending on where the ball hit the other thing
        if (collisions[1].normal.x > 0 or collisions[1].normal.x < 0) then
            transform.xspeed = -transform.xspeed
        end

        if (collisions[1].normal.y > 0 or collisions[1].normal.y < 0) then
            transform.yspeed = -transform.yspeed
        end

        for i = 1, collisionsLen do
            if collisions[i].other.type == "brick" then
                collisions[i].other:remove()

                score += 1
            elseif collisions[i].other.type == "paddle" then
                print("xspeed: " .. transform.xspeed)
                print("yspeed: " .. transform.yspeed)
                local oldSpeed = math.sqrt(transform.xspeed * transform.xspeed + transform.yspeed *
                    transform.yspeed)
                local newXSpeed = collisions[i].other.transform.speed * 0.1
                local newSpeed = math.sqrt(newXSpeed * newXSpeed + transform.yspeed *
                    transform.yspeed)
                transform.xspeed = newXSpeed * (oldSpeed / newSpeed)
                transform.yspeed = transform.yspeed * (oldSpeed / newSpeed)
            end
        end
    end
end

function Ball:collisionResponse(other)
    return gfx.sprite.kCollisionTypeBounce
end

Am I doing something wrong??

Oh, and here is what my paddle.lua file looked like before changing it to a sprite:

import "CoreLibs/sprites"
import "CoreLibs/graphics"

local gfx <const> = playdate.graphics

class("paddle").extends()

function paddle:init()
    self.transform = {
        x = 200,
        y = 220,
        width = 32,
        height = 8,
        speed = 0
    }

    local image = gfx.image.new("images/paddle")
    local sprite = gfx.sprite.new(image)
    sprite:setCollideRect(0, 0, sprite:getSize())
    sprite:add()

    self.sprite = sprite;
    self.type = "paddle"
end

function paddle:update()
    local transform = self.transform
    local change, acceleratedChange = playdate.getCrankChange()

    if (transform.x - transform.width / 2 + change < 0) then
        transform.speed = 0
        transform.x = transform.width / 2
    elseif (transform.x + transform.width / 2 + change > 400) then
        transform.speed = 0
        transform.x = 400 - transform.width / 2
    else
        transform.speed = change / 2;
    end

    transform.x += transform.speed

    self.sprite:moveTo(transform.x, transform.y)
end

function paddle:draw()
    self.sprite.update()
end

You don't need Ball.super.update(self) and Paddle.super.update(self) inside of your update calls, as far as I know. Maybe that's not breaking it, but those aren't necessary in update, just in the init.

1 Like

Looks suspiciously like a bug somebody else reported recently. Does transform.x = transform.x + transform.speed work?

1 Like

I switched to that and it's now saying transform.xspeed is nil even though you can see I am setting it in the init function.

I think they are used to draw the sprites though

I removed all the "Class".super.update(self) and added gfx.sprite.update() in my main file and everything seems to work now. :thinking:

2 Likes

That’s how it’s intended to work :+1:t5: If you haven’t yet, take a peek at the sprite documentation for details on how the global sprite list works and how it gets updated.

2 Likes