How to constantly check the location of an object in lua - lua

I am working on a game that involves drawing lines to guide a physics ball into a bucket at the bottom of the screen. However, sometimes the user might draw the lines poorly, and the balls will become stuck. To rid this I would like to check the position of the balls every 3 seconds.
This is what I thought would work:
function checkBallVelocity(event)
startX = event.x
startY = event.y
timer.performWithDelay( 5000, function()
endX = event.x
endY = event.y
print(startX..","..startY.." || "..endX..","..endY)
if startX == endX or startY == endY then
if event.other.name == "1" then
circle1:removeSelf( )
circle1 = nil
ballsMissed = ballsMissed + 1
elseif event.other.name == "2" then
circle2:removeSelf( )
circle2 = nil
ballsMissed = ballsMissed + 1
elseif event.other.name == "3" then
circle3:removeSelf( )
circle3 = nil
ballsMissed = ballsMissed + 1
end
return 1
else
checkBallVelocity()
end
end
)
end
Sadly, it doesn't. Any help/advice would be welcome

There are many ways to do this sort of thing, but Corona provides an enterFrame event which fires each time a frame is drawn (30 or 60 frames per second). It can be used as a main loop to perform periodic actions, but keep in mind that it is run quite often so it helps to define a period and limit your actions to certain phases within the period.
local period
local phase = 0
function mainLoop(event)
phase = phase + 1
if phase == 1 then
checkBallPosition(...)
elseif phase == period
phase = 0
end
end
...
function scene:enterScene( event )
period = display.fps * 3 -- # of seconds
Runtime:addEventListener("enterFrame", mainLoop)
...
end

Related

How to integrate computing time in AI reaction?

My program is an implementation of Pong where one of the paddles is to be moved by the computer and the other by the user.
The program works fine with errors being made by the AI for realism. However, the movement of my paddle on screen is stuttering and it seems to be skipping one or two frames.
The program is in Lua(+Love2D).
function Paddle:comp_move(dt)
error = math.random(3) == 2 and true or false
start_time = os.time()
diff = 0
if ball:collides(self) == false then
if ball.y > self.y + self.height then -- Ball is below paddle
-- Ball is moving up and difference is more than 20 pixels
if ball.dy < 0 and (ball.y - self.y - self.height) > 20 then
-- move down
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.min(VIRTUAL_HEIGHT - 20, self.y + PADDLE_SPEED*(dt))
end
end
-- Ball is moving down
if ball.dy > 0 then
-- move down
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.min(VIRTUAL_HEIGHT - 20, self.y + PADDLE_SPEED*(dt))
end
end
elseif ball.y + ball.height < self.y then -- Ball is above paddle
-- Ball is moving down
if ball.dy > 0 and (self.y - ball.y - ball.height) > 20 then
-- move up
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.max(0,self.y - PADDLE_SPEED*(dt))
end
elseif ball.dy < 0 then -- Ball is moving up
-- move up
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.max(0,self.y - PADDLE_SPEED*(dt))
end
end
end
end
I am calculating the time taken to compute by the PC but what operation should i do with it normalize my paddle's movement.
From your code it seems the paddle speed is constant; it might be an idea to replace this by a function, so that initially the paddle moves more slowly, then speeds up, and then slows down again as it arrives at the desired location.
This would make it more realistic in terms of movement behaviour, and it might also solve your problem of stuttering.
Your constant PADDLE_SPEED would need to be replaced by some function that takes a step and returns a value (probably between 0.0 and PADDLE_SPEED) at the appropriate time.

LUA - Unable to make the game restart after the timer finishes

I have to make a small game for my college work and at the end of the game I would like it so if you click the game over button it restarts it, however every time I have tried to do this it hasn't worked with either errors or just the game restarting and the game over image not being removed.
I know that the current code exits the app when the gameover sign is clicked due to the closeDownApp function (this is the code from before I tried to add the ability to restart)
Any help on how to do it will be greatly appreciated
--Define the variables
rotateAmt = 5
rotateMin = -90
rotateMax = 90
cannonRotation = 0
cannonForce = 1200
playerPoints = 0
targetHit = false
timeLeft = 600
--Define the functions
function closeDownApp(event)
os.exit ( )
end
function update( )
--decrease the time counter
timeLeft = timeLeft - 1
scoreDisplay.text = 'Kills: ' .. playerPoints .. ' Time: ' .. timeLeft
--check if the time has run out
if (timeLeft <= 0) then
--display the final score
scoreDisplay.text = 'Your score was ' .. playerPoints
--remove all of the screen objects
display.remove(cannonBarrel)
display.remove(target)
display.remove(upButton)
display.remove(downButton)
display.remove(fireButton)
--display the 'game over' sign
gameOver = display.newImage('gameover.png', 150, 75)
gameOver:addEventListener('tap', closeDownApp)
-- make a group
end
--check if the target has been hit
if (targetHit == true) then
targetHit = false
playerPoints = playerPoints + 1
Hit = display.newImage('Hit.png', target.x, target.y)
--replace the target with the hit picture
transition.dissolve(Hit, target, 1000, 0)
display.remove(bullet)
--put the target back to a random position
target.x = math.random(280, 500 )
target.y = math.random(100, 290 )
end
end
function onCollide(event)
targetHit=true
end
function fire(event)
--only fire at the beginning of a touch event
if (event.phase == 'began') then
media.playSound('bang.wav')
bullet = display.newImage('bullet.png')
--move the image
bullet.x, bullet.y = cannonBarrel:localToContent(70, 0)
bullet.rotation = cannonRotation
--apply physics to the cannonball
physics.addBody( bullet, { density=2.0, friction=0.2, radius=15 } )
--determine the appropriate ratio of horizontal to vertical force
force_x = math.cos(math.rad(cannonRotation)) * cannonForce
force_y = math.sin(math.rad(cannonRotation)) * cannonForce
--fire the cannonball
bullet:applyForce( force_x, force_y, bullet.x, bullet.y )
end
end
function moveDown(event)
--only move the barrel if the touch event started
if (event.phase == 'began') then
cannonRotation = cannonRotation + rotateAmt
if (cannonRotation >= rotateMax) then
cannonRotation = rotateMax
end
cannonBarrel.rotation = cannonRotation
fireButton.rotation = cannonRotation
end
end
function moveUp(event)
--only move the barrel if the touch event started
if (event.phase == 'began') then
cannonRotation = cannonRotation - rotateAmt
if (cannonRotation <= rotateMin) then
cannonRotation = rotateMin
end
cannonBarrel.rotation = cannonRotation
fireButton.rotation = cannonRotation
end
end
function makeTarget( )
target = display.newImage('target.png')
target.x = math.random(280, 450)
target.y = math.random(100, 290)
physics.addBody(target,{density=1.0, friction=0.5, bounce=0.05, radius=15})
target.bodyType = 'static'
target:addEventListener('collision', onCollide)
end
function makeInterface( )
--up button
upButton = display.newImage('up_button.png')
upButton:translate(3, 37)
upButton:addEventListener('touch', moveUp)
--down button
downButton = display.newImage('down_button.png')
downButton:translate(3, 176)
downButton:addEventListener('touch', moveDown)
--fire button
fireButton = display.newImage('fire_button.png')
fireButton:translate(19, 124)
fireButton:addEventListener('touch', fire)
--display cannon parts
cannonBarrel = display.newImage('cannon_barrel.png')
cannonBarrel:translate(73, 109)
--display score
scoreDisplay = display.newText( ('Points: ' .. playerPoints), 70, -18, native.systemFont, 20 )
scoreDisplay:setTextColor( 255,255,255 )
scoreDisplay:translate(display.contentHeight /2, 30)
end
--Define control structure
function init( )
audio.stop (1)
display.setStatusBar(display.HiddenStatusBar)
background = display.newImage('bg.jpg')
physics = require('physics')
physics.setDrawMode('normal')
physics.start( )
makeInterface( )
makeTarget( )
Runtime:addEventListener('enterFrame', update)
local backgroundMusic = audio.loadStream("theme.mp3")
local backgroundMusicChannel = audio.play( backgroundMusic,{channel=2, loops=-1, fadein=10 })
audio.setVolume( 0.5, { channel=backgroundMusicChannel } )
end
function introScreen( )
local backgroundMusic1 = audio.loadStream("intro.mp3")
local backgroundMusicChannel1 = audio.play( backgroundMusic1,{channel=1, loops=1, fadein=10 })
-- Display the background
background = display.newImage ("IntroScreen.png")
-- Display the play button at the top right
playBtn = display.newImage ("playBtn.png", 350, 10)
playBtn:addEventListener('tap', init)
end
--Call the code
introScreen( )
This seems like it was written for Corona SDK. Their forums (https://forums.coronalabs.com/) seem very active.
I'm sure that you'd be able to solve this issue a lot faster by asking for help there.

How would I get my script to reset its score upon button press?

I need some help understanding how would I reset the score of numMiss, numHit, and numPercent back to 0 as soon as I tap the "reset.png" button and while doing so will also start the game from the beginning again.
Also, let me know if there are any corrections to be made within my code.
Heres what I have of the code so far
--width and height
WIDTH = display.contentWidth --320
HEIGHT = display.contentHeight --480
--display background
local p = display.newImageRect("park.png" ,500, 570)
p.x = WIDTH/2
p.y = HEIGHT/2
--display bouncing dog
local RADIUS = 5
local d = display.newImageRect("dogeball.png", 70, 70)
d.x = 50
d.y = 100
--display treat
local t = display.newImageRect("treat.png", 50, 50)
t.x = 245
t.y = math.random(HEIGHT)
--displays the reset button
local r = display.newImageRect("reset.png", 100,100)
r.x = 280
r.y = 480
--starting value of gravity and bounce(will change)
local GRAVITY = 0.3
local BOUNCE = 0.75
--downward force
local velocity = 0
--Tells the score to reset when true
local reset = false
--shows number of hits
local numHit = 0
--shows number of misses
local numMiss = 0
--Gets Percentage score
local numPercent = 0
--make hits and misses display
scoreHits = display.newText("Hits = " .. numHit, WIDTH/7, 1, native.systemFont, 18)
scoreMisses = display.newText("Misses = " .. numMiss, WIDTH/2.1, 1, native.systemFont, 18)
scorePercent = display.newText("Hit % = " .. numPercent, WIDTH/1.2, 1, native.systemFont, 18)
function enterFrame()
d.y = d.y + velocity
velocity = velocity + GRAVITY
local HIT_SLOP = RADIUS * 8 -- Adjust this to adjust game difficulty
if math.abs(t.x - d.x) <= HIT_SLOP
and math.abs(t.y - d.y) <= HIT_SLOP then
numHit = numHit + 1
scoreHits.text = "Hits = " .. numHit
--count 1 hit once dog and treat hit eachother
if (t.x - d.x) <= HIT_SLOP and (t.y - d.y) <= HIT_SLOP then
t.x = 400 --resets treat postioning
t.y = math.random(HEIGHT) --gives treat a random y coordinate
end
end
--puts the barrier at the bottom of the screen and tells dog to bounce from there
if (d.y > HEIGHT) then
d.y = HEIGHT
velocity = -velocity * BOUNCE
end
t.x = t.x - 5 --speed treat goes
if t.x < -350 then--position of the treat
t.x = 400
scoreMisses.text = "Misses = " .. numMiss
else if t.x < -100 then
t.y = math.random(HEIGHT) --random height after treat goes past dog
else if t.x < -99 then
numMiss = numMiss + 1 --calculates misses when goes past screen
scoreMisses.text = "Misses = " .. numMiss
end
end
end
--calculate percentage hits
numPercent = 100 * numHit / (numHit + numMiss)
scorePercent.text = "Hit % = " .. math.round(numPercent) --prints and rounds percentage
function tapped(event) --when tapped on reset, score gets reset
--reset function goes here
end
end
r:addEventListener( "tap", tapped )
end
function touched(event)
-- print(event.phase)
if event.phase == "began" then
velocity = velocity - 6 -- thrusts dog
end
return true
end
Runtime:addEventListener( "enterFrame" , enterFrame )
Runtime:addEventListener( "touch", touched )
To make your image a button you need to add an event listener that responds to touch or tap events.
see http://docs.coronalabs.com/api/event/touch/index.html
Or you use the widget library which gives you the possibility to use a blank button background and set only the label for each button what will be very handy when you include translations for other languages.
see http://docs.coronalabs.com/api/library/widget/newButton.html
In my game I have a function gameInit() that sets the hole game and all variables. This function is called when the game starts and also when the player wants to do a reset as it over writes the old variables. (there are other technics, depending on the complexity of your game and if you for example want to store the game settings for the next time the player starts the game)

Corona Lua display object y property out by three ten-millionths of a pixel (!)

When I run this code (which is based on the "DragMe" sample app) I get very unexpected results in very particular circumstances.
My "snapToGrid" function sets x and y to the nearest round 100 value after a drag.
The "examine" function outputs the x and y values after an object is created, moved or rotated (by clicking on it).
If you place an object in row 5 (so y = 500) and rotate it you will see that the y value changes to 499.99996948242 but this does not happen in any other row.
How can this be explained? I notice this does not happen if the physics bodies are not added to the display object.
I know I could call snapToGrid after rotation or round the value before I use it for anything else but I think there is an important opportunity here for me to learn something useful about Corona.
local function snapToGrid(t)
modHalf = t.x % t.width
if modHalf > t.width/2 then
t.x = t.x + (t.width-modHalf)
end
if modHalf < t.width/2 then
t.x = t.x - modHalf
end
modHalfY = t.y % t.height
if modHalfY > t.height/2 then
t.y = t.y + (t.height-modHalfY)
end
if modHalfY < t.height/2 then
t.y = t.y - modHalfY
end
display.getCurrentStage():setFocus( nil )
t.isFocus = false
return true
end
function rotatePiece(target)
if target.rotation == 270 then
target.rotation = 0
else
target.rotation = target.rotation + 90
end
end
local function dragBody(event)
local target = event.target
local phase = event.phase
local halfWidth = target.width/2
local halfHeight = target.height/2
--get tileX and tileY relative to tile centre
local tileX = event.x-target.x
local tileY = event.y-target.y
local modHalf = ""
local snap = 15
if phase == "began" then
-- Make target the top-most object
display.getCurrentStage():setFocus( target )
-- Spurious events can be sent to the target, e.g. the user presses
-- elsewhere on the screen and then moves the finger over the target.
-- To prevent this, we add this flag. Only when it's true will "move"
-- events be sent to the target.
target.isFocus = true
-- Store initial position
target.x0 = event.x - target.x
target.y0 = event.y - target.y
elseif target.isFocus then
if phase == "moved" then
-- Make object move (we subtract target.x0,target.y0 so that moves are
-- relative to initial grab point, rather than object "snapping").
target.x = event.x - target.x0
target.y = event.y - target.y0
if target.x > display.contentWidth - (target.width/2) then target.x = display.contentWidth - (target.width/2) end
if target.y > display.contentHeight - (target.height/2) then target.y = display.contentHeight - (target.height/2) end
if target.x < 0 + (target.width/2) then target.x = 0 + (target.width/2) end
if target.y < 0 + (target.height/2) then target.y = 0 + (target.height/2) end
modHalf = target.x % target.width
if modHalf < snap then
target.x = target.x - modHalf
end
if modHalf > ((target.width) - snap) then
target.x = target.x + ((target.width)-modHalf)
end
modHalfY = target.y % target.height
if modHalfY < snap then
target.y = target.y - modHalfY
end
if modHalfY > ((target.height) - snap) then
target.y = target.y + ((target.height)-modHalfY)
end
hasMoved = true
return true
elseif phase == "ended" then
if hasMoved then
hasMoved = false
snapToGrid(target)
--tile has moved
examine(target)
return true
else
--rotate piece
rotatePiece(target)
display.getCurrentStage():setFocus( nil )
target.isFocus = false
--tile has rotated
examine(target)
return true
end
end
end
-- Important to return true. This tells the system that the event
-- should not be propagated to listeners of any objects underneath.
return true
end
local onTouch = function(event)
if event.phase == "began" then
local tile = {}
img = display.newRect(event.x,event.y,100,100)
img:addEventListener( "touch", dragBody )
snapToGrid(img)
--top right corner and top middle solid
topRight = {16,-16,16,50,50,50,50,-16}
--top left and left middle solid
topLeft = {-16,-16,-16,-50,50,-50,50,-16}
--bottom right and right middle solid
bottomRight = {16,16,16,50,-50,50,-50,16}
--bottom left and bottom middle solid
bottomLeft = {-16,16,-16,-50,-50,-50,-50,16}
physics.addBody( img, "static",
{shape=topRight},
{shape=topLeft},
{shape=bottomLeft},
{shape=bottomRight}
)
--new tile created
examine(img)
return true
end
end
function examine(img)
print("--------------------------------------------------")
if img ~= nil then
print("X: "..img.x..", Y: "..img.y)
end
print("--------------------------------------------------")
end
local img
local physics = require( "physics" )
physics.setDrawMode( "hybrid" )
--draw gridlines
for i = 49, display.contentHeight, 100 do
local line = display.newLine(0,i,display.contentWidth,i)
local line2 = display.newLine(0,i+2,display.contentWidth,i+2)
end
for i = 49, display.contentWidth, 100 do
local line = display.newLine(i,0,i,display.contentHeight )
local line2 = display.newLine(i+2,0,i+2,display.contentHeight )
end
--init
physics.start()
Runtime:addEventListener("touch", onTouch)
There several possibilities. Firstly, since there are no integers in Lua, all numbers are double floating point values. According to FloatingPoint on Lua wiki,
Some vendors' printf implementations may not be able to handle
printing floating point numbers accurately. Believe it or not, some
may incorrectly print integers (that are floating point numbers). This
can manifest itself as incorrectly printing some numbers in Lua.
Indeed, try the following:
for i=1,50,0.01 do print(i) end
You will see that a lot of numbers print exactly as you would expect, by many print with an error of 10^-12 or even 2 x 10^-12.
However, your x prints fine when you don't give it to physics module. So surely the physics module does some computation on object position and changes it. I would certainly expect that for dynamic objects (due to collision detection), but here your object seems to be "static". So it must be that physics adjusts x even for static objects. The adjustment is so small that it would not be visible on the screen (you can't perceive any motion less than a pixel), but you're right that it is interesting to ask why.
The SO post Lua: subtracting decimal numbers doesn't return correct precision is worth reading; it has some links you might find interesting and gives the neat example that decimal 0.01 cannot be represented exactly in base 2; just like 1/3 can't be represented exactly in base 10 (but it can in base 3: it would be 0.1 base 3!). The question shows that, although Lua (or possibly, the underlying C) is smart enough to print 0.01 as 0.01, it fails to print 10.08-10.07 as 0.01.
If you really want to shake your understanding of floating point values, try this:
> a=0.3
> b=0.3
> print(a==b)
true
> -- so far so good; now this:
> a=0.15 + 0.15
> b=0.1 + 0.2
> print(a,b)
0.3 0.3
> print(c==d)
false -- woa!
This is explained by the fact that 0.15 in binary has a small error which is different from that of 0.1 or 0.2, so in terms of bits they are not identical; although when printed, the difference is too small to show. You may want to read the Floating Point Guide.
Actually this problem only occurs if you add the physics, so I think it is fair to say that the issue is caused by transferring control to box2d, not by Corona handling of the number alone. I asked on Corona forums and got this answer.
http://forums.coronalabs.com/topic/46245-display-object-that-has-static-physics-body-moves-very-slightly-when-rotated/

Corona SDK Lua- Collision with coin

I want to make a sprite image have a collision with the coin.
The coin is a image and it is always moving because of this code:
local tPrevious = system.getTimer();
local function move(event)
local tDelta = event.time - tPrevious;
tPrevious = event.time;
local xOffset = (0.3 * tDelta );
grass.x = grass.x - xOffset;
grass2.x = grass2.x - xOffset;
coin.x = coin.x - xOffset;
if (grass.x + grass.contentWidth) < 0 then
grass:translate( 480 * 2, 0);
end
if (grass2.x + grass2.contentWidth) < 0 then
grass2:translate( 480 * 2, 0);
end
if (coin.x + coin.contentWidth) < 0 then
coin:translate( 480 * 2, 0);
coinRect.x = coin.x
end
local i;
end
Does anyone know how I can have a collision with the coin image?
Thanks in advance.
Add collision event listener to the coin like this,
local function onLocalCollision( self, event )
if ( event.phase == "began" ) then
print("on collision began")
elseif ( event.phase == "ended" ) then
print( "on collision ended")
end
end
coin:addEventListener( "collision", onLocalCollision)
if you wants the specific collision with the coin and sprite object,you should use collision filter.

Resources