Corona: Double tap / single tap - coronasdk

I'm attempting to detect both single and double taps in Corona SDK. I have utilised:
system.setTapDelay(0.5)
...in my main.lua file which I understood would ensure a single tap event was not passed unless a further tap event had not been received in 0.5 seconds. According to the docs the delay is specified in seconds not milliseconds hence the 0.5.
I have then utilised this:
function mapGroup:tap(event)
if event.numTaps >= 2 then
print "double tap"
else
print "single tap"
end
return true
end
This, as expected returns single and double tap. However it is firing twice for double taps:
single tap
double tap
How can I prevent Corona firing the single tap until we know it is not going to be a double tap?

It's more tricky than it seems at first.
I think system.setTapDelay() does work. At least it seems to.
But my understanding is that is not meant to exclude single taps. Only to accept slower double taps. Or maybe the documentation of this function is just misleading. In any case let's hope the Corona dev team clarifies that in the future.
Now in your app you have objects that must react to single taps, and objects that must react to double taps, correct?
And maybe even objects that must react to one, or the other, but not to both. But you have to be aware that with this third design choice, in case of a single tap your object will have to "wait" until it is sure it wasn't the first hit of a double tap. You follow? In other words, to distinguish single and double taps on the same object might result in single taps that "lag" a bit.
To implement all of this in a simple way I would go like this:
function onSingleTap( event )
if event.numTaps == 1 then
print("received a single tap")
end
end
objectThatRespondsToSingleTapOnly:addEventListener( "tap", onSingleTap )
function onDoubleTap( event )
if event.numTaps >= 2 then
print("received a double tap")
end
end
objectThatRespondsToDoubleTapOnly:addEventListener( "tap", onDoubleTap )
Now if you also want objects that distinguish the single from the double you could add this:
local doubleTapTimeout = 600 -- values under 300 become hard to use
-- BTW make this a little higher than the tap delay, if you set it manually
function onExclusiveSingleTap( event )
print("received an exclusive single tap")
end
function onExclusiveDoubleTap( event )
print("received an exclusive double tap")
end
function onExclusiveTap( event )
if event.numTaps == 1 then
event.target.singleTapWaiting = timer.performWithDelay(
doubleTapTimeout,
function(e) onExclusiveSingleTap( event ) end
)
elseif event.numTaps >= 2 then
timer.cancel( event.target.singleTapWaiting )
onExclusiveDoubleTap( event )
end
end
objectThatRespondsToBothTaps:addEventListener( "tap", onExclusiveTap )
I only wonder if you shouldn't copy the values of the event object in the Lua closure instead of passing its reference, which might result in some memory leaks.
function(e) onExclusiveSingleTap( {
name = "tap",
x = event.x,
y = event.y,
numTaps = 1,
target = event.target
} ) end

Related

Is there a way to add time to a Wait class inside an If statement?

I started learning LUA a few days ago, started my own project inside Tabletop Simulator game, but I've hit a brick wall. I can't add time to a Wait class.
This is an example of what I tried:
function state_check()
--This function checks if the state of obj_1 and obj_2 is 0 or 1
end
function press_button()
--This function activates other functions based on the state of obj_1 and obj_2
i = 1 --Function starts with a wait of 1 second
if obj_1_state == 1 then --If state is 1, then the function is triggered and 1 second is added to i
Wait.time(func_1, i)
i = i + 1
end
if obj_2_state == 1 then
Wait.time(func_2, i)
end
end
I need for the function to check the first part and if true, do the second part 1 second later. If not, do the second part normally and skip the "i = i + 1".
My problem is that the function does everything at the same time. I know I'm doing something wrong, but I can't figure out what. Is there a way to create some for of gate to do everything in order or anything similar?
Your code seems correct.
I don't know what is the problem.
But I know that one of possible solutions is to follow the "callback hell" style of programming:
local function second_part(obj_2_state)
if obj_2_state == 1 then
Wait.time(func_2, 1)
end
end
local function first_part(obj_1_state, obj_2_state)
if obj_1_state == 1 then
Wait.time(
function()
func_1()
second_part(obj_2_state)
end, 1)
else
second_part(obj_2_state)
end
end
function press_button()
local obj_1_state, obj_2_state = --calculate states of obj_1 and obj_2 here
first_part(obj_1_state, obj_2_state)
end

How do I update a variable as the game runs?

You see, I'm used to Pygame and the like and there was always the concept of a "main loop". Corona seems to work like a GUI, handling events more implicitly. The thing is, I have a score value:
local score = 0
And when I do something like:
while running do
score = score + 1
end
Corona just crashes. I guess this is not the way to do this (The score gets updated as the game runs). How do I update the variable all the time? (Without touching or tapping or whatever).
The "game loop" in Corona is created by adding a listener to the Runtime for enterFrame events. This listener is called once per frame and can be used to update anything you like.
You could read this guide to interactivity and event detection for more information.
I agree with #GoojajiGreg. Try
local function loop( event )
print( "enterFrame called at time: " .. event.time )
end
Runtime:addEventListener( "enterFrame", loop)

How do I make os.pullEvent not yield?

I'm trying to create a while true do loop, that reacts to clicks, using os.pullEvent, and also updates a monitor.
Problem being, it only updates the screen when I press one of the on screen buttons, and I've found out that's because pullEvent stops the script, until an event is fired.
Is it possible to make it so pullEvent doesn't stop me updating the monitor?
function getClick()
event,side,x,y = os.pullEvent("monitor_touch")
button.checkxy(x,y)
end
local tmp = 0;
while true do
button.label(2, 2, "Test "..tmp)
button.screen()
tmp++
getClick()
end
You can easily use the parallel api to run both codes essentially at the same time. How it works is it runs them in sequence until it hits something that uses os.pullEvent and then swaps over and does the other side, and if both stop at something that does os.pullEvent then it keeps swapping between until one yields and continues from there.
local function getClick()
local event,side,x,y = os.pullEvent("monitor_touch")
buttoncheckxy(x,y)
end
local tmp = 0
local function makeButtons()
while true do
button.label(2,2,"Test "..tmp)
button.screen()
tmp++
sleep(0)
end
end
parallel.waitForAny(getClick,makeButtons)
Now if you notice, first thing, I've made your while loop into a function and added a sleep inside it, so that it yields and allows the program to swap. At the end you see parallel.waitForAny() which runs the two functions that are specified and when one of them finishes, which in this case whenever you click on a button, then it ends. Notice however inside the arguments that I'm not calling the functions, I'm just passing them.
I don't have computercraft handy right now or look up the functions but i know that you can use the function os.startTimer(t) that will cause an event in t seconds (I think it is seconds)
usage:
update_rate = 1
local _timer = os.startTimer(update_rate)
while true do
local event = os.pullEvent()
if event == _timer then
--updte_screen()
_timer = os.startTimer(update_rate)
elseif event == --some oter events you want to take action for
--action()
end
end
note: the code is not tested and I didn't use computercraft in quite a while so pleas correct me if i did a mistake.

Corona Active Battle Scene in Composer

I am currently working on a card battle game. In the main battle scene I am trying to show the cards actively battling one another with their health bars decreasing and eventually weapons moving around the scene as well.
At present, when it enters the battle loop, the display freezes, but I have been logging what is happening and the battle is still happening, just behind the scene. I have separated the battle loop into its own function at the top of the code and call that function with a tap event.
I verify that it is running by using the print statements within the while loop which prints out the current health of the card and the name of the card to the console. The current health of the cards are changing, it's just not changing scenes, but instead freezing on the old one, without actively displaying what is happening.
Here is the code for the entire scene:
function battleScene(playerCards, enemyCards, allCards, cardHealth)
while not checkIfDead(playerCards) and not checkIfDead(enemyCards) do
for i=1, 6 do
if allCards[i]~=0 then
allCards[i]:battle()
end
print( allCards[i]:getCurHealth().." "..allCards[i]:getName() )--The test to see current health of card
cardHealth[i]:setHealth(allCards[i]:getCurHealth(),allCards[i]:getHealth())
if checkIfDead(playerCards) or checkIfDead(enemyCards) then
break
end
usleep(2000)
end
end
end
---------------------------------------------------------------------------------
-- "scene:show()"
function scene:show( event )
local sceneGroup = self.view
local phase = event.phase
if ( phase == "will" ) then
-- Called when the scene is still off screen (but is about to come on screen).
elseif ( phase == "did" ) then
--The current health of each card is set to max
--and then the card is rendered along with health bars
local card1=test1:render()
card1.x=display.contentCenterX-100
card1.y=display.contentCenterY-100
sceneGroup:insert(card1)
local card1Health=HealthBar:new()
card1Health.x=display.contentCenterX-100
card1Health.y=display.contentCenterY-40
card1Health:setHealth(test1:getCurHealth(), test1:getHealth())
sceneGroup:insert(card1Health)
playerCards={test4, test5, test6}
enemyCards={test1, test2, test3}
for i=1, 3 do
if playerCards[i]:getClass()=="Tank" or playerCards[i]:getClass()=="Damage" then
playerCards[i]:setBattleSet(enemyCards)
else
playerCards[i]:setBattleSet(playerCards)
end
end
for i=1, 3 do
if enemyCards[i]:getClass()=="Tank" or enemyCards[i]:getClass()=="Damage" then
enemyCards[i]:setBattleSet(playerCards)
else
enemyCards[i]:setBattleSet(enemyCards)
end
end
local allCards={test1, test2, test3, test4, test5, test6}
bubbleSort(allCards)
local cardHealth= {card1Health,card2Health,card3Health,card4Health,card5Health,card6Health}
local startBattleButton=display.newText( "Start Battle", 0, 0, globals.font.regular, 18 )
startBattleButton.x = display.contentCenterX
startBattleButton.y = display.contentCenterY
local function onTap(event)
startBattleButton.isVisible=false
battleScene(playerCards, enemyCards, allCards, cardHealth)
end
startBattleButton:addEventListener( "tap", onTap )
sceneGroup:insert(startBattleButton)
if checkIfDead(playerCards) then
win=false
end
end
end
The problem is that your battle scene function is looping and modifying the scene, however the scene engine updates the scene only between event handling calls. I.e., if your tap function gets called and you modify it, you will see the changes only after the tap function returns and the scene engine has processed the new scene state.
So instead of doing this:
function battleScene(args)
while condition do
do stuff
end
end
do instead
function battleScene(args)
if condition then
do stuff
timer.performWithDelay(2, function() battleScene(args) end)
end
end
This executes "do stuff" when the condition is true, and schedules a call to battleScene for later. The battleScene will return immediately after that, given the display engine the chance to update GUI, and 2 ms later, will call battleScene again, until eventually the condition is false and no re-call will be scheduled. Note that I had to create a temp anonymous function because battleScene takes arguments whereas performWithDelay does not pass any arguments to the scheduled function, but they can be given implicitly as upvalues via anonymous function. To be clear, if battleScene had taken no args, you could have just done this:
function battleScene()
if condition then
do stuff
timer.performWithDelay(2, battleScene)
end
end

Having trouble with some computercraft/lua code

Hi I want my lua code in Computercraft to allow the user to turn the redstone signal on/off by right clicking on a monitor on top, but I can't get it to work.
monitor = peripheral.wrap("top")
monitor.clear()
monitor.setTextColor(colors.red)
monitor.setCursorPos(1, 1)
monitor.setTextScale(1)
monitor.write("Hello")
function rubber()
monitor.setCursorPos(1, 2)
monitor.clearLine()
if rs.getOutput("right", true) then
monitor.write("Rubber farm is on")
elseif rs.getOutput("right", false) then
monitor.write("Rubber farm is off")
end
local event = { os.pullEvent() }
if event == "monitor_touch" then
if rs.getOutput("right") == true then
rs.setOutput("right", false)
else
rs.setOutput("right", true)
end
else
write("test")
end
rubber()
end
Right now all it displays is 'hello' and I don't know how to fix it, anyone know how? Also I'm a beginner at Lua so I've probably made some pretty simple mistakes. Thanks
local event = { os.pullEvent() }
if event == "monitor_touch" then
os.pullEvent returns a tuple. In your code, you're packing this tuple into a table. That's fine, but you then compare that table to a string. Tables can't be equal to strings - they're a table. Either don't pack the tuple into a table, and keep the first return value (the type):
local event = os.pullEvent()
if event == "monitor_touch" then
Or extract the first element when comparing
local event = { os.pullEvent() }
if event[1] == "monitor_touch" then
The problem is you wanted to have that function infinitly looping, but you have not called your function outside your function.... also you should look into using while loops
while true do
//stuff here
end
just add
rubber()
to the last line after your last end tag.
You have to call the function.
rubber()
You need to close your function
function rubber()
monitor.setCursorPos(1,1)
monitor.clearLine()
end
The end is it you need to make this little word
this is a simple fix, simply add rubber() after you finish the function rubber, cause while you have created the function rubber, you have not called for it to start yet.
The "monitor_touch" event is what you should be using. Also, make sure the monitor you are using is an advanced monitor (the one with the yellow border).
If you need help in understanding the event, check out this page: http://computercraft.info/wiki/Monitor_touch_(event)

Resources