How do I update a variable as the game runs? - lua

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)

Related

Why does My teleporter part Not teleport to another part

I am currently working on a game teleporter in Roblox (If you are unfamiliar with this, you touch a part and It takes you to a waiting area to soon be teleported to the game) Right now I am having trouble with the part that teleports you to that waiting area. (the name of the part teleporting you is called Teleporter. The name of the part you are being teleported to is called Lob)
The code I had put In was `
local Lob = game.Workspace.Lob
function Teleport()
game.Workspace.Teleporter.Touched:Connect(game.Players.LocalPlayer:MoveTo(Lob))
end
game.Workspace.Teleporter.Touched:Connect(Teleport)
What i got wasServerScriptService.Script:4: attempt to index nil with 'MoveTo'`
You're connecting the touched event inside of the function and outside of it.
Could you possibly do something like
function Teleport()
game:GetService("Players").LocalPlayer.Character.HumanoidRootPart.CFrame = Lob.CFrame)
end
workspace.Teleporter.Touched:Connect(Teleport)

How do I make this code function correctly?

I want to make a sort of Lobby system in Roblox Studio where if you have 4 people on a part you get sent to another place. I tried to set up a system for it, but it didn't work; can you help me through this?
I've tried making it so it says .Value at the end.
local TeleportService = game:GetService("TeleportService")
player_amount = script.Parent.Parent.Parent.Player_Count
local placeID_1 = 4119652438
local function onPartTouch(otherPart)
local player = game.Players:GetPlayerFromCharacter(otherPart.Parent)
if player then
player_amount.Value = player_amount.Value + 1
end
if player_amount == 4 then
TeleportService:Teleport(placeID_1, player)
end
end
script.Parent.Touched:Connect(onPartTouch)
I expected the output to be 0 then if one person steps on it, it would update the sign to say 1. But it only stays at 0.
This is not a viable solution as .Touched fires every frame the player touches a part, and only when they move. I suggest creating a hitbox, as I have done here

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

Corona: Double tap / single tap

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

Resources