Basic Crank rotate sprite

I have a sprite that I can move around with the d-pad but what I want is to rotate the sprite with the crank, struggling to understand the basics of the crank in that regard I can grab values and such but I am not sure how I attach that to the sprite ?

I had a look at the crank.lua file and the docs but was not sure where to start...

function playdate.update()
    playdate.timer.updateTimers()
    gfx.sprite.update()  

    if playdate.buttonIsPressed( playdate.kButtonUp ) then
        playerShip:moveBy( 0, -2 )
    end
    if playdate.buttonIsPressed( playdate.kButtonRight ) then
        playerShip:moveBy( 2, 0 )
    end
    if playdate.buttonIsPressed( playdate.kButtonDown ) then
        playerShip:moveBy( 0, 2 )
    end
    if playdate.buttonIsPressed( playdate.kButtonLeft ) then
        playerShip:moveBy( -2, 0 )
    end


end

This appears to setRotation (when I print it out) but the sprite isn't rotating on screen

import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"
import "CoreLibs/easing"
import 'CoreLibs/crank'

local gfx <const> = playdate.graphics
local playerShip = nil
local r = 50
local centerX = 200
local centerY = 120
local angle = 0
local crankRads = math.rad(angle)
local x = math.sin(crankRads)
local y = -1 * math.cos(crankRads)


function myGameSetUp()
    playerShip = gfx.sprite.new()
    playerShip:setSize(32, 32)
    playerShip:moveTo(200,120) 
    playerShip:add() 
       
    function playerShip:draw()
        gfx.drawPolygon(16,0, 24,16, 32,16, 16,32, 0,16, 8,16)
	end 

    function normalizeAngle(a)
        if a >= 360 then a = a - 360 end
        if a < 0 then a = a + 360 end
        return a
    end

    function degreesToCoords(angle)
        x = (r * x) + centerX
        y = (r * y) + centerY
        return x,y
    end

    local x,y = degreesToCoords(angle)
    
end

myGameSetUp()

function playdate.update()
    playdate.timer.updateTimers()
    gfx.sprite.update()  
    local change = playdate.getCrankChange()

    if change ~= 0 then
        angle += change
        angle = normalizeAngle(angle)
        -- print(change, angle)
    
        x,y = degreesToCoords(angle)
    
      end

      playerShip:setRotation(angle)

end

function playdate.cranked(change, acceleratedChange)
 	print(playerShip:getRotation())
end

sprite:setRotation will rotate the sprite’s image if it has one. Yours doesn’t - it’s drawing a polygon directly to the screen instead:

function playerShip:draw()
        gfx.drawPolygon(16,0, 24,16, 32,16, 16,32, 0,16, 8,16)
	end 

If you want to use sprites with images and use sprite:setRotation, you could do something like:

function myGameSetUp()
    local ship_image = gfx.image.new(32, 32)
    gfx.pushContext(ship_image)
    gfx.drawPolygon(16,0, 24,16, 32,16, 16,32, 0,16, 8,16)
    gfx.popContext()
    
    playerShip = gfx.sprite.new()
    playerShip:setImage(ship_image)
    playerShip:moveTo(200,120) 
    playerShip:add() 
    
    function playerShip:update()
        playerShip:setRotation(playerShip:getRotation() + playdate.getCrankChange())
        -- to keep things tidy, shift the rest of your input handling from playdate.update to here
    end
end

Or if you want to retain the polygon drawing (it'll look cleaner and run faster):

function myGameSetUp()
    
    playerShip = gfx.sprite.new()
    playerShip.polygon = geom.polygon.new(16,0, 24,16, 32,16, 16,32, 0,16, 8,16, 16,0)
    playerShip.polygon:translate(184, 104)
    playerShip.rotate_transform = geom.affineTransform.new()
    playerShip:moveTo(200,120) 
    playerShip:add() 
    
    function playerShip:update()
        -- rotate the transform
        playerShip.rotate_transform:rotate(playdate.getCrankChange(), playerShip.x, playerShip.y)
        -- apply the transform to the ship polygon
        playerShip.rotate_transform:transformPolygon(playerShip.polygon)
        -- reset the transform so that crank change doesn’t accumulate over time
        playerShip.rotate_transform:reset()
    end

    function playerShip:draw()
        gfx.drawPolygon(playerShip.polygon)
    end 
end

But this is half in and half out of the sprite system (you need playerShip:draw() in playdate.update()).

So it might be better to combine the two - give the sprite an image, then draw the ship polygon to that image instead of directly to the screen:

function myGameSetUp()
    playerShip = gfx.sprite.new()
    playerShip.polygon = geom.polygon.new(16,0, 24,16, 32,16, 16,32, 0,16, 8,16, 16,0)
    playerShip.rotate_transform = geom.affineTransform.new()
    playerShip:moveTo(200,120) 
    playerShip:add() 
    
    function playerShip:update()
        -- rotate the transform
        playerShip.rotate_transform:rotate(playdate.getCrankChange(), 16, 16)

        -- apply the transform to the ship polygon
        playerShip.rotate_transform:transformPolygon(playerShip.polygon)

        -- reset the transform so that crank change doesn’t accumulate over time
        playerShip.rotate_transform:reset()

        -- draw the ship polygon to the sprite's image
        local img = gfx.image.new(32, 32)
        gfx.pushContext(img)
        gfx.drawPolygon(playerShip.polygon)
        gfx.popContext()
        playerShip:setImage(img)
    end
end
2 Likes

Awesome.... I like the final idea and the moment I am getting

Attempt to index a nil value (upvalue 'geom')

Oops, sorry!

geom is an alias for playdate.geometry, like gfx is for playdate.graphics, so put it at the start of main.lua:


local gfx <const> = playdate.graphics
local geom <const> = playdate.geometry

1 Like

thank you. slowly getting to grips with SDK etc :slight_smile: