I have some odd menus i'm playing with - i'd like to use the crank to navigate within the menu. is this possible? It would also be useful if i could programmatically control the cursor a bit, for example I might want to open a menu with the cursor at the most recently used position rather than at the top. Perhaps i'd like to move it elsewhere too.
I think all of the above could be accomplished if i could emit button events. for the crank i would simply decide the angular delta i'd like to use as the equivalent to an "up" or "down" button press; and for default menu cursor location i could record an offset for the most recently used menu item and simply emit the corresponding movements (perhaps in the player.draw handler so they happen after the menu is created? or can i drop code inside the menu itself but not within the menu options?)
Have a look at the crank event and the event.ra variable. The event will be called every frame if the crank has been moved, and event.ra will be the amount of degrees it has moved in that frame. You can keep track of the total in a variable, and then use that to select your menu options.
Thanks, but what I'm missing here is how to select the menu options.
Assume for a moment that I define 30 degrees as the trigger to move to the next menu option. I listen to the crank using the event handler as you described, I see event.ra equals 30 or more... so I want to bother move the menu cursor. but how?
Let's say you have a variable menuSelected which is the number of the current option selected in your menu. To change this every time the crank is turned 30 degrees, you could use the following code:
on crank do
// Keep track of the total degrees
crankDegrees += event.ra
// Change the selected option
if crankDegrees>=30 then
crankDegrees = 0
menuSelected++
elseif crankDegrees<=-30 then
crankDegrees = 0
menuSelected--
end
// Lets say you have 5 menu options
if menuSelected>5 then
// Loop back to the beginning
menuSelected = 0
elseif menuSelected<0 then
// Loop to the end
menuSelected = 5
end
end
Thanks for replying again, but I think I've still failed to articulate the question properly.
We have a native menu widget which we call on using [menu] followed by a number of options. When it is displayed there is a cursor which is drawn by default at the first option. During dpad interaction a single tap up or down moves that cursor. I'd like to move that cursor using the crank.
I understand how to read the crank and how to convert from relative angle changes to my own variables tracking which option I'd like to see the cursor point to, but I don't know how to move the cursor itself
thanks, yeah i'm thinking the same. though it feels like something worth adding to the native menu widget.
feeling through the implementation, it seems that a call to menu does a few things (just an educated guess, but it aligns well with observation):
defines a menu structure (the list of options)
registers that structure to be drawn by a system task
registers 1 callback function for each option
returns immediately - that is, your script runs to completion before the menu is drawn
this isn't what i expected initially, but in hindsight it makes sense. i had originally scripted re-entrant menus in a while loop with the exit condition set inside the menu options - that didn't work as it looked like an infinite loop to the runtime engine (it looped hundreds of times without drawing a menu onscreen, then the runtime reported an error flag). This means the user script that requests a menu isn't itself active while the menu is on-screen... i would need some asynchronous method to acquire crank updates and emit messages to the running menu code.
i think the idea i shared in the original post would be a reasonable way to implement this - very little "new code" and no real change to the current architecture (as i understand it anyway). I originally thought i would submit this as a feature request, but then it felt so obvious to me that folks would want to use the cranks to scroll through menus that i must be missing some existing solution.
i'll submit as a feature request later today. thanks to you both for chiming in all the same
-bit
I heartily encourage crank use for menus. Keep playdate weird!
Though I have no idea on how to go about it. Crank events are a mystery so far for me. The doc coverage of it is pretty lightweight for a ... lightweight like myself.
The docs explain it pretty well actually! You just have to read it carefully. It's called on the player. So make sure to do the following in the player script.
on crank do
log "cranking!"
end
If you want to hook into that from another script just make sure to emit a command from the player script that can be called from another script. Like so:
Player Script:
on crank do
emit "isCranking!"
end
Other scripts for instance a level script:
on isCranking do
log "Im cranking!"
end
And then you could check for the degree it is turned and use that to do other things with.
on isCranking do
crankDegrees += event.ra
log "{crankDegrees}"
end
Ah like that. Ok, so to answer your question, the native menu currently does not support input from the crank no. That however does not mean it is not possible. I have for instance created Pokedex where you can scroll through the items with the crank. A simplified version can be found in the code below.
It combines all of the scripts we have above.
Player:
on crank do
emit "isCranking"
end
on draw do
hide
emit "updateInterface"
end
Room:
on enter do
menuSelected = 0
end
on updateInterface do
currentIndex = menuSelected
currentIndex++
loopIndex = 0
dexPosition = 1
while loopIndex<6 do
currentItemName = "???"
call "item_{currentIndex}"
if currentItemName!="" then
label "{currentIndex}: {currentItemName}" at 1,dexPosition
else
label "{currentIndex}: ???" at 1,dexPosition
end
currentIndex++
loopIndex++
dexPosition += 3
end
end
on item_1 do
currentItemName = "The first item"
end
on item_2 do
currentItemName = "The second item"
end
on item_3 do
currentItemName = "The third item"
end
on item_4 do
currentItemName = "The aaand another item"
end
on item_5 do
currentItemName = "Last one ok? for sure!"
end
on isCranking do
// Keep track of the total degrees
crankDegrees += event.ra
// Change the selected option
if crankDegrees>=30 then
crankDegrees = 0
menuSelected++
elseif crankDegrees<=-30 then
crankDegrees = 0
menuSelected--
end
totalMonsterCount = 50
// Lets say you have 5 menu options
if menuSelected>totalMonsterCount then
menuSelected = totalMonsterCount
elseif menuSelected<0 then
menuSelected = 0
end
end
And also, here is a fully working example so people won't have try to get it working themselves Hope this actually solves your issue. CranktTest.json.zip (3.2 KB)
I'll mark this as the solution because it's as close as we can get. I still think a better approach for devs here would be to enable us to emit events equivalent to d-pad button presses.
something like:
on load do
posIncr=30
negIncr=-30
menuOnScreen=0
end
on drawMenu do
// draw the menu in question
menu at x,y,len,items then
option "optionOne" then
// do one thing
end
option "optionTwo" then
// do another
end
end
// set a flag indicating that a menu is drawn, therefore accept crank input as up/down equivalent
menuOnScreen=1
end
on crank do
if menuOnScreen==1 then
accumCrank += event.ra
if accumCrank >= posIncr then
emit "dpad_up"
accumCrank=0
end
if accumCrank <= negIncr then
emit "dpad_down"
accumCrank=0
end
else
// use the crank for something other than replicating dpad up/down behavior to navigate a menu
end
end
for that to work we'd need to know what event to fire for the dpad movements... something i'm guessing is actually really easy and might be nearly implemented already (somehow or another dpad events make there way into the runtime... and we already have events fired for A, B, and Crank... so dpad is just one more button source to connect with the same system).