Unexpected error when using drawRotated()

I'm trying to rotate a sprite to match the current crank angle. But I'm getting an unexpected error.

player.lua:143: calling 'drawRotated' on bad self (playdate.graphics.image expected, got table)

This error comes as a surprise as I'm pretty sure gfx.image is not a table?

Here's my Player:init.

function Player:init(x,y)
	playerImage = gfx.image.new("images/amadeus_head")
	self:setImage(playerImage)
	self:setZIndex(100)
	self:moveTo(x,y)
	self:add()

	self.states = {
		idle = self.idle,
		move = self.move,
		goBack = self.goBack,
		retract = self.retract
	}
	self.state = self.states.idle

	self.speed = 3
	theButt = Butt(x - (startLength * segmentSpacing), y)
	playerParts[0] = theButt
	for i=1, startLength do
		playerParts[i] = Body(theButt.x + (segmentSpacing * i), y)
	end
end

Working in VS Code on Mac.

What’s line 143 look like?

1 Like

Excellent question, I thought I had included everything. Here it is,

playerImage:drawRotated(x, y, crankAngle)

There's still more code that is relevant.

How is player defined and instantiated?

Also you'll need something like this line in your init. See the docs https://sdk.play.date/inside-playdate/#_object_oriented_programming_in_lua

    Player.super.init(self)

If you don't want to share your code (quite understandable, it's not expected) then it's best to create a minimal reproduction of the problem. I often find that as I'm creating these I spot and fix the problem so it's time well spent.

2 Likes

Okay I've isolated the situation as much as I can!

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

local pd <const> = playdate
local gfx <const> = pd.graphics

class('Player').extends(gfx.sprite)

function Player:init(x, y)
	self:setZIndex(99)
	playerImage = gfx.image.new("images/amadeus_head")
	self:setImage(playerImage)
	self:moveTo(x, y)
	self:add()
end

function Player:update()
	local crankPosition = pd.getCrankPosition()
	gfx.image:drawRotated(self.x, self.y, crankPosition)
end

And I initiate the player like this in the main script.

import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"

import "player"

local pd <const> = playdate
local gfx <const> = pd.graphics

Player(200, 90)

function pd.update()
	gfx.sprite.update()
	pd.timer.updateTimers()
end

I've tried to add that "Player.super.init(self)" line to the player's init function, but it doesn't seem to make any difference. I'm honestly not sure what it's supposed to do, the documentation doesn't really help me either. :- /

I'm still getting the hang of basic Lua stuff.

1 Like

A few things (updated):

  • You're declaring a global playerImage variable and I'm not sure that's what you want. If the image is only needed inside the player script, you can use a local variable so it's available in the file but doesn't pollute global namespace. If the image never changes you could also make it const: local playerImage <const> = gfx.image.new("images/amadeus_head"). I'd put this under your pd and gfx declaration
  • Functions called with the : operator pass a self in as an invisible first parameter. So when you call gfx.image:drawRotated it's passing in gfx.image (a table) as the first argument, when it should be passing the image. This can be fixed by calling playerImage:drawRotated(...) instead
  • As matt suggested, you typically want to call your super method when implementing classes from an extended base. The syntax is MyClass.super.myFunction(self) (you can't use : syntax because of how lua handles "objects")
2 Likes

Hmm okay, lots to unpack there...

I feel like I'm really missing some core knowledge to be able to grasp the idea of 'super.' I would probably benefit from learning that, but it's not exactly what I was trying to do now. So I'll research that myself later.
Feel free to correct me if you think it's necessary to get this working.

Now I understand why I was getting the error when I called the function using gfx.image.
I tried to call it using playerImage instead like you said and it got rid of the error! But for some reason the sprite doesn't rotate. I added a print function, so I can see that it is supposed to be getting the crank position.

Am I missing something?

You're right, that was a lot! I rearranged it to make a little more sense. As to your current issue... the sprite system is clashing with how you expect it to work. Sprites know their images and handle clearing/drawing themselves, so trying to draw images or geometry in the sprite's update function won't work. I forgot about that, sorry!

There are a bunch of ways you could fix this, but the easiest is probably to call self:setRotation(crankPosition) in your :update() function instead of trying to draw the rotated image yourself.

1 Like

NICE! self:setRotation(...) works! I didn't expect there to be more than one function for rotating sprites, so I didn't think to look for it. I'm glad it was so simple to set it in the end.

If I interpret your explanation of drawRotated() correctly, then that function is actually trying to draw a new sprite instead of adjusting the sprite of the current player..?

Thank you so much for the help!

1 Like

that function is actually trying to draw a new sprite instead of adjusting the sprite of the current player..?

Not exactly, there was only ever one sprite and one image. The issue was that you can’t draw to the screen in a sprite’s :update method because of how the sprite handles redrawing itself.
Since a sprite is an easy way to draw an image to a screen, they have some convenience methods to manipulate the sprite’s set image.

You can learn more about sprites here: https://youtu.be/8OCebUVKlb4

1 Like