Corona SDK Attempt to compare number number to nil - lua

Hi I am converting this simple "catch the egg" game from Godot engine to Corona. I am very new to programming and am using this project as a learning exercise.
I have run into a hurdle with it though. I keep getting the following error msg:
**
ERROR: Runtime error
C:\Users\kdoug\Documents\Corona Projects\cathchtheegg\main.lua:19: attempt to compare number with nil
stack traceback:
C:\Users\kdoug\Documents\Corona Projects\cathchtheegg\main.lua:19: in function
?: in function
**
What I am trying to do is see if the egg will delete when it goes beyond a certain point, without having to use a collision with a physics object.
any help would be appreciated!
thanks
Here is the code (a little less discombobulated):
local physics = require "physics"
physics.start()
local h = display.actualContentHeight
local w = display.actualContentWidth
local cx = display.contentCenterX
local cy = display.contentCenterY
local dnir = display.newImageRect
local dnr = display.newRect
local mr = math.random
--local egg
local bask
local idx = 0
local eggs = {}
---------BACKGROUND---------------
local bg = dnir("bg.png", w,h)
bg.x = cx
bg.y = cy
----------DISPLAY BASKET------------
bask = dnir("basket.png", 100,50)
bask.x = cx
bask.y = cy
physics.addBody(bask,"kinematic")
bask.myName = "bask"
----- BASKET MOVE W/ MUSE FUNCTION -----
local function baskMove (e)
bask.x = e.x
bask.y = e.y
end
Runtime:addEventListener("mouse", baskMove)
----------------GROUND---------------
local grd = dnr(cx,h-470,w+50,10)
grd:setFillColor(.1, .8, .15,0)
grd.myName = "ground"
physics.addBody(grd, "static")
grd.collision = collision
grd:addEventListener("collision", grd)
----------****DELETE EGG FUNCTION****------------
--function loop ()
-- if egg and egg.y > 100 then
-- print("Delete")
-- display.remove(egg)
-- end
--end
--
--Runtime:addEventListener("enterFrame", loop)
-----------COLLISIONS FUNCTIION-------------
local function collision ( s, e )
if e.phase == "began" then
if e.target.myName == "bask"
and e.other.myName == "egg" then
display.remove(e.other)
table.remove(eggs, idx)
end
if e.target.myName == "egg"
and e.other.myName == "bask" then
display.remove(e.target)
table.remove(eggs, idx)
end
if e.target.myName == "ground"
and e.other.myName == "egg" then
display.remove(e.other)
table.remove(eggs, idx)
end
if e.target.myName == "egg"
and e.other.myName == "ground" then
display.remove(e.target)
table.remove(eggs, idx)
end
end
end
--
--------------EGG---------------------
function theEgg ()
egg = dnir("egg.png", 50,50)
physics.addBody(egg,"dynamic")
egg.myName = "egg"
idx = idx + 1
egg.x = mr(w)
egg.y = - 100
transition.to (egg, {y = h + 50, time= mr(1000,8000)})
eggs[idx] = egg
eggs[idx].idx = idx
print(eggs[idx])
--------EGG COLLISIION CB-------------
egg.collision = collision
egg:addEventListener("collision", egg)
end
--
-----------Spawn EGG-----------
function spawner()
theEgg()
print(#eggs)-- PRINT AMT IN TABLE
end
timer.performWithDelay(2000, spawner, 0)

I don't know when you remove enterFrame listener. It is important. After you delete egg object loop may be called again. So when egg.y is not defined (=nil) comparision (in if statment) can not be done.
My solution:
function loop ()
if egg and egg.y > 100 then
print("Delete")
display.remove(egg)
end
end
Information from https://www.lua.org/pil/3.3.html
all logical operators consider false and nil as false and anything
else as true
Or (use local variable index instead global variable egg) It is not clear for my purpose use of varaible egg in your code so it may be wrong.
local index
...
function loop ()
if eggs[index] and eggs[index].y > 100 then
print("Delete")
local egg = table.remove(eggs, index)
display.remove(egg)
egg = nil
end
end

Related

Lua error attempt to index nil value while checking if player has the correct amount of a item

This script allows people to produce meth, starting production will succeed when you have the right amount of objects in your inventory.
Only when you don't have the right amount you should get a notification and you don't get that notification..
Following error pops up: SCRIPT ERROR: #ns-meth/server.lua:18: Attempt to index a nil value.
Code:
RSCore = nil
Citizen.CreateThread(function()
while RSCore == nil do
TriggerEvent('RSCore:GetObject', function(obj) RSCore = obj end)
Citizen.Wait(0)
end
end)
RegisterServerEvent('RSCore_methcar:start')
AddEventHandler('RSCore_methcar:start', function()
local src = source
local Player = RSCore.Functions.GetPlayer(src)
local amount = 0
if Player.Functions.GetItemByName('acetone').amount >= 5 and Player.Functions.GetItemByName('lithium').amount >= 2 and Player.Functions.GetItemByName('methlab').amount >= 1 then
TriggerClientEvent('RSCore_methcar:startprod', src)
Player.Functions.RemoveItem('acetone', 5)
Player.Functions.RemoveItem('lithium', 2)
else
if Player.Functions.GetItemByName('acetone').amount <= 4 and Player.Functions.GetItemByName('lithium').amount <= 1 and Player.Functions.GetItemByName('methlab').amount <= 0 then
RSCore.Functions.Notify("Je hebt niet de juiste benodigdheden!", "error")
end
end
end)
RegisterServerEvent('RSCore_methcar:stopf')
AddEventHandler('RSCore_methcar:stopf', function(id)
local src = source
local Players = RSCore.GetPlayers()
local Player = RSCore.Functions.GetPlayer(src)
for i=1, #Players, 1 do
TriggerClientEvent('RSCore_methcar:stopfreeze', Players[i], id)
end
end)
RegisterServerEvent('RSCore_methcar:make')
AddEventHandler('RSCore_methcar:make', function(posx,posy,posz)
local src = source
local Player = RSCore.Functions.GetPlayer(src)
if Player.Functions.GetItemByName('methlab').amount >= 1 then
local Players = RSCore.Functions.GetPlayer(src)
for i=1, #Players, 1 do
TriggerClientEvent('RSCore_methcar:smoke',Players[i],posx,posy,posz, 'a')
end
else
TriggerClientEvent('RSCore_methcar:stop', src)
end
end)
RegisterServerEvent('RSCore_methcar:finish')
AddEventHandler('RSCore_methcar:finish', function(qualtiy)
local src = source
local Player = RSCore.Functions.GetPlayer(src)
print(qualtiy)
local rnd = math.random(-5, 5)
TriggerEvent('KLevels:addXP', src, 20)
Player.Functions.AddItem('meth', math.floor(qualtiy / 2) + rnd)
end)
RegisterServerEvent('RSCore_methcar:blow')
AddEventHandler('RSCore_methcar:blow', function(posx, posy, posz)
local src = source
local Players = RSCore.GetPlayers()
local Player = RSCore.Functions.GetPlayer(src)
for i=1, #Players, 1 do
TriggerClientEvent('RSCore_methcar:blowup', Players[i],posx, posy, posz)
end
Player.removeInventoryItem('methlab', 1)
end)
RegisterServerEvent('ns-meth:server:callCops')
AddEventHandler('ns-meth:server:callCops', function(streetLabel, coords)
local msg = "Er is een verdachte situatie op "..streetLabel..", mogelijks drugs productie."
local alertData = {
title = "Verdachte situatie",
coords = {x = coords.x, y = coords.y, z = coords.z},
description = msg
}
for k, v in pairs(RSCore.Functions.GetPlayers()) do
local Player = RSCore.Functions.GetPlayer(v)
if Player ~= nil then
if (Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty) then
TriggerClientEvent("ns-meth:client:robberyCall", Player.PlayerData.source, msg, streetLabel, coords)
TriggerClientEvent("rs-phone:client:addPoliceAlert", Player.PlayerData.source, alertData)
end
end
end
end)

Receiving "Attempt to index global 'sprite' (a nil value)" in Lua

Im attempting to make a simple game app and keep running into this problem. I stopped programming in Lua for a few years so I don't exactly remember how to fix this. Anyway, my code is as follows:
EDIT: Here is the entire file. Still trying to figure out the formatting of Stack Overflow. Error occurs at line 76.
module(..., package.seeall)
-- Main function - MUST return a display.newGroup()
function new()
local localGroup = display.newGroup()
---------
local Rad = math.rad
local Sin = math.sin
local Cos = math.cos
local Pi = math.pi
local Atan2 = math.atan2
local radD = 180 / Pi
local DegR = Pi / 180
local touchPoint = display.newCircle(localGroup, -50, -50, 20)
touchPoint.isFocus = false
touchPoint.alpha = 0
function GetDistanceFromObjects(obj1, obj2)
local xDist = obj1.x - obj2.x
local yDist = obj1.y - obj2.y
local dist = Sqrt((xDist * xDist) + (yDist * yDist))
return dist
end
function getAngleDeg(inX1, inY1, inX2, inY2)
local xDist = inX2 - inX1
local yDist = inY2 - inY1
local angRad = Atan2(yDist, xDist)
return angRad * radD + 90
end
require "sprite"
function VectorFromAngle(inAngle, inVelocity)
local vx = Cos(Rad(inAngle-90))
local vy = Sin(Rad(inAngle-90))
if(inVelocity ~= nil)then
vx = vx * inVelocity
vy = vy * inVelocity
end
return vx,vy
end
require ( "physics" )
physics.start()
physics.setGravity( 1, 1 )
--( x, y )
--physics.setDrawMode ( "hybrid" )
math.randomseed(os.time())
local background = display.newImage("yazd.jpeg")
localGroup:insert(background)
--width of image divided by # of pics lined up from left to right (in the sprite) = the first #
--height of image divided by # of pics lined up from top to bottom (in the sprite) = the second #
local birdSheet = sprite.newSpriteSheet( "enemy.jpg", 59, 50 )
local birdSet = sprite.newSpriteSet(birdSheet, 1, 1)
-- images 1-14
sprite.add( birdSet, "bird", 1, 1, 200, 0 )
-- play 1-14, each image every 200 ms, 0 = loop count, which is infinite
local bird1 = sprite.newSprite( birdSet )
bird1.x = 40 -- starting point
bird1.y = 40 -- starting point
bird1.xScale = 0.5 --scale down x
bird1.yScale = 0.5 --scale down y
bird1:prepare("bird") --prepare sprite sequence
bird1:play() --play sprite
localGroup:insert(bird1)
--only local to this group
local killSheet = sprite.newSpriteSheet("explosion.png", 100, 100)
local killSet = sprite.newSpriteSet(killSheet, 1, 9)
sprite.add(killSet, "kill", 1, 9, 200, 1)
local birdCount = 1
local transDirection12
local function transDirection1()
bird1.xScale = 0.5
transition.to(bird1, {time=math.random(200,500), x = math.random(200,490), y = math.random(10,310), alpha = (math.random(9,100))/100, onComplete = transDirection12})
end
transDirection12 = function()
bird1.xScale = 0.5
transition.to(bird1, {time= math.random(200,500), x = math.random(200,490), y = math.random(10,310), alpha = (math.random(9,100))/100, onComplete = transDirection1})
end
transDirection1()
-- local transDirection1 declares what will be used (local function)
-- transDirection1 = function
-- following it are the function qualities
-- declares it will use object/image called bird1 and scales it to .5
-- time = ____ means it will take a certain time, between ____ and ____ to complete the transition
-- x=____ means that is where it will move to on the x axis
-- y=____ means that is where it will move to on the y axis
-- alpha = ___ means the is how transparent it will be
-- onComplete = ________ means that when the action is complete, it will call another function
-- The next function has the same qualities as transDirection1, but the onComplete part calls transDirection1 and they continue to loop
-- transDirection1() declares transDirection1 so the app knows about it and can use it
-- the other trans do not need to be declared because they are part of transDirection1, which is already declared
--(x, y, size.x, size.y)
local player = display.newImage( "mk11.png" )
player.x = 240
player.y = 260
player.xScale = .5
player.yScale = .5
localGroup:insert( player )
-- add physics to all the objects wanted: (object wanted, "static" or "dynamic")
physics.addBody(player, "static", {radius=30, isSensor = true})
physics.addBody(bird1, "static", {radius=23})
local function shoot(inPointX, inPointY)
-- (start at the x of the player + 10, also start at the y of the player, the radius of the circle is 5)
local bullet = display.newImage( "bullet2.png" )
bullet.x = player.x
bullet.y = player.y
-- add physics to the object, which is the bullet.
-- Make the bullet "dynamic" or moving
physics.addBody(bullet, "dynamic")
bullet.isFixedRotation = true
localGroup:insert( bullet )
local velocity = 300
local vx, vy = VectorFromAngle(player.rotation, velocity)
bullet.rotation = player.rotation
bullet:setLinearVelocity(vx, vy)
end
function RotateToTouchPoint(inPointX, inPointY)
local ang = getAngleDeg(player.x, player.y, inPointX, inPointY)
player.rotation = ang
end
local function ScreenTouchListener(event)
local phase = event.phase
if(phase == "began")then
if(touchPoint.isFocus == false)then
touchPoint.alpha = 1
touchPoint.x = event.x
touchPoint.y = event.y
display.getCurrentStage():setFocus(touchPoint, event.id)
touchPoint.isFocus = true
RotateToTouchPoint(event.x, event.y)
shoot(event.x, event.y)
end
elseif(touchPoint.isFocus)then
if(phase == "moved")then
touchPoint.x = event.x
touchPoint.y = event.y
RotateToTouchPoint(event.x, event.y)
elseif(phase == "ended" or phase == "cancelled")then
display.getCurrentStage():setFocus(touchPoint, nil)
touchPoint.isFocus = false
touchPoint.alpha = 0
end
end
return true
end
local function gotShot (event)
event.target:removeSelf()
event.other:removeSelf()
local explosion = sprite.newSprite(killSet)
explosion.x, explosion.y = event.target.x, event.target.y
explosion:prepare("kill")
explosion:play()
localGroup:insert( explosion )
birdCount = birdCount - 1
-- when there are no more birds, remove the runtime event listener and perform the
-- function with a delay of 500 m.s. The function changes the scene to test.lua
if "ended" then
if birdCount == 0 then
Runtime:removeEventListener("touch", ScreenTouchListener)
timer.performWithDelay(500, function()
director:changeScene("mainPage") end, 1)
end
end
end
bird1:addEventListener("collision", gotShot)
Runtime:addEventListener("touch", ScreenTouchListener)
---------
-- MUST return a display.newGroup()
return localGroup
end
Any help is appreciated!
The error message is perfectly clear -- the variable sprite used at this line:
local bird1 = sprite.birdSheet( birdSet )
has a nil value, meaning it has not been initialized or was set to nil. You need to show the earlier code where you should have set it up.
(After OP updates)
I think this line
require "sprite"
should actually be
sprite = require "sprite"
You can read more in modules tutorial here:
http://lua-users.org/wiki/ModulesTutorial

Corona SDK Level: Can't integrate my level into Storyboard - Assertion Failed error

I hope someone can help me because this problem has been driving me crazy the last few days. So I just started with corona - made a few tutorials and really having fun discovering the possibilities.
Problem: I want to integrate a simple game into a storyboard. I somehonw can't manage to find the right combination and always get this error pop up.
File: assertion failed!
Assertion failed!
stack traceback:
[C]: ?
[C]: in function 'assert'
?: in function 'get0rCreateTable'
?: in function 'addEventListener'
?: in function 'addEventListener'
...ik/Desktop/_DummyProjekt2/Fruit Fliesv10/level01.lua:51: in function
<. . . ik/Desktop/_DummyProjekt2/ Fruit Fliesv10/level0l. lua:44>
?: in function 'dispatchEvent'
?: in function <?:1096>
(tail call): ?
?: in function <?:466>
?: in function <?:218>
It's probably a rookie mistake, but I have been stuck for a few days now and definitely need some help. I've gone thru most of the tutorials and documentation I could find with no luck. So any help would be greately welcome :) So this is my level 1
local storyboard = require( "storyboard" )
local scene = storyboard.newScene()
local widget = require ("widget")
local playfile = require ("play")
function scene:createScene(event)
local screenGroup = self.view
local spawnEnemy
local gameTitle
local scoreTxt
local score = 0
local hitBowl
local planet
local speedBump = 0
background = display.newImage("images/background.png")
background.y = centerY
background.x = centerX
bowl = display.newImage("images/bowl2.png")
bowl.x = centerX
bowl.y = centerY
scoreTxt = display.newText( "Score: 0", 0, 0, "orange juice", 22 )
scoreTxt.x = centerX
scoreTxt.y = 10
scoreTxt:setFillColor(1,1,1)
scoreTxt.y = 255
end
function scene:enterScene(event)
enemypics = {"images/blue.png","images/pink.png", "images/green.png"}
enemy = display.newImage(enemypics[math.random (#enemypics)])
enemy:addEventListener ( "tap", shipSmash )
if math.random(2) == 1 then
enemy.x = math.random ( -100, -10 )
else
enemy.x = math.random ( display.contentWidth + 10, display.contentWidth + 100 )
enemy.xScale = -1
end
enemy.y = math.random (display.contentHeight)
enemy.trans = transition.to ( enemy, { x=centerX, y=centerY, time=math.random(2500-speedBump, 4500-speedBump), onComplete=hitBowl } )
speedBump = speedBump + 50
end
function startGame()
text = display.newText( "Tap here to start. Protect the Fruit Bowl!", 0, 0, "orange juice", 24 )
text.x = centerX
text.y = display.contentHeight - 30
text:setFillColor(0, 0, 0)
local function goAway(event)
display.remove(event.target)
text = nil
display.remove(gameTitle)
spawnEnemy()
scoreTxt.alpha = 1
scoreTxt.text = "Score: 0"
score = 0
bowl.numHits = 10
bowl.alpha = 1
speedBump = 0
end
text:addEventListener ( "tap", goAway )
end
local function bowlDamage()
bowl.numHits = bowl.numHits - 2
bowl.alpha = bowl.numHits / 10
if bowl.numHits < 2 then
bowl.alpha = 0
timer.performWithDelay ( 1000, startGame )
--audio.play ( sndLose )
else
local function goAway(obj)
bowl.xScale = 1
bowl.yScale = 1
bowl.alpha = bowl.numHits / 10
end
transition.to ( bowl, { time=200, xScale=1.2, yScale=1.2, alpha=1, onComplete=goAway} )
end
end
function hitBowl(obj)
display.remove( obj )
bowlDamage()
--audio.play(sndBlast)
if bowl.numHits > 1 then
spawnEnemy()
end
end
local function shipSmash(event)
local obj = event.target
display.remove( obj )
--audio.play(sndKill)
transition.cancel ( event.target.trans )
score = score + 5
scoreTxt.text = "Score: " .. score
spawnEnemy()
end
function scene:exitScene(event)
end
function scene:destroyScene(event)
end
scene:addEventListener( "createScene", scene )
scene:addEventListener( "enterScene", scene )
scene:addEventListener( "exitScene", scene )
scene:addEventListener( "destroyScene", scene )
return scene
So LUA is fun in lots of regards and this is one you'll learn quickly. So I only think this might be the problem, if not it could lead you on the correct trail.
In your top error message you'll see that it gives you this:
...ik/Desktop/_DummyProjekt2/Fruit Fliesv10/level01.lua:51: in function
That level01.lua:51 signifies a line number, so line 51 (or around there). And the lines above it in the error mention addEventListener. Those two things are our clues.
Here's some code from around there:
enemy = display.newImage(enemypics[math.random (#enemypics)])
enemy:addEventListener ( "tap", shipSmash )
if math.random(2) == 1 then
Looks like there's an addEventListener there, one parameter is a string, the other is a function. Where is that function? It's on line 117 which is after line 51 which is most likely the problem. In LUA (and some other languages) you have to declare things in order. So you could do two things. You could move the shipSmash function up above line 51 or you could forward declare that variable. So up in the top part of your file you could do local shipSmash. This assures line 51 that that name exists (what it's trying to check) and then you can declare it later (sometimes I do this for code structure).
Hopefully that helps. I've been using LUA/Corona SDK since about July, I'm still getting used to a lot of the small things like this that I've spent hours ripping my hair out over.

Removing specific objects on Collision

removing the coin im colliding whit seems a bit of a problem im got this :
local screenGroup = self.view
local options2 =
{
--required parameters
width = 16,
height = 16,
numFrames = 8,
--optional parameters; used for dynamic resolution support
sheetContentWidth = 128, -- width of original 1x size of entire sheet
sheetContentHeight = 16, -- height of original 1x size of entire sheet
}
local imageSheet1 = graphics.newImageSheet( "items/coin.png", options2 )
-- Example assumes 'imageSheet' is already created using graphics.newImageSheet()
-- non-consecutive frames
local sequenceData1 =
{
name="normal",
frames= {1,2,3,4,5,6,7,8}, -- frame indexes of animation, in image sheet
time = 600, --700 -- Optional, in milliseconds ; if not supplied, the animation is frame-based
loopCount = 0 -- Optional ; default is 0
}
local coin = {}
local coinspawn = function()
local i = display.newSprite( imageSheet1, sequenceData1 )
i.x = display.contentWidth/2
i.y = display.contentHeight/2
i:play()
i.collided = true
i.name = "coin"
physics.addBody(i, "dynamic",
{density=.1, bounce=0.1, friction=.2, shape= shape2 ,filter=playerCollisionFilter }
)
--player.gravityScale = 0.5
coinIntro = transition.to(i,{time=2000, x=display.contentWidth/2-50 ,onComplete=jetReady , transition=easing.OutExpo } ) --
coin[#coin+1] = i
end
timer.performWithDelay( 1000, coinspawn, 0 )
function coinPlus()
for i = #coin, 1, -1 do
if coin[i] ~= nil then
local function dellcoin()
if coin[i] ~= nil then
coin[i]:removeSelf()
coin[i] = nil
end
end
transition.to( coin[i], { time=100, alpha=0, onComplete = dellcoin} )
break
end
end
end
local function onCollision(event)
if event.phase == "began" and gameIsActive == true then
local obj1 = event.object1;
local obj2 = event.object2;
if obj1.name == "playerpop" then
if obj2.name == "BGfrontFL1" then --helper()
elseif obj2.name == "BGfrontFL2" then --helper()
elseif obj2.name == "coin" then coinPlus()
end
end
end
end
Runtime:addEventListener( "collision", onCollision )
withs kinda works but it removes the last spawned coin and not the one in collision, how can i fix this ?
In coinspawn you create the coin and add it to coin table. It appears that your coin table will contain all spawned coins that have not been collided with (that seems to be your intention anyways). Then when a coin collides the onCollision() will get called, which will call coinPlus(). The latter then loops over all coins in coin table, starting with the latest spawned one (at end of table), and if it is not nil it starts a fade out with removal on completion of fade-out. This is surely not what you intend: you want to delete only the coin collided.
So the biggest problem is the way the coin that was involved in collision gets removed: looping over all coins, don't think it is necessary. You should try passing coin as arg to coinPlus:
if obj1.name == "playerpop" then
if obj2.name == "BGfrontFL1" then --helper()
elseif obj2.name == "BGfrontFL2" then --helper()
elseif obj2.name == "coin" then coinPlus(obj2)
end
end
function coinPlus(coinToRemove)
local function dellcoin()
coinToRemove:removeSelf()
for i, coin in ipairs(coin) do
if coin == coinToRemove then
coin[i] = nil
break
end
end
end
transition.to( coinToRemove, { time=100, alpha=0, onComplete = dellcoin} )
end
Another problem is your use of '#' operator: it is only to be used with tables that don't have "holes", so if you really do remove individual entries from your table (thus creating holes), then #coin will no longer work (that's why I used ipairs).

Assignment confusion in Lua

I am analysing Fishies projest from sample codes of Corona and i couldn't understand that assignment.
background = ( backgroundLandscape == background and backgroundPortrait ) or backgroundLandscape
Here is the full code:
-- Seed randomizer
local seed = os.time();
math.randomseed( seed )
display.setStatusBar( display.HiddenStatusBar )
-- Preload the sound file (theoretically, we should also dispose of it when we are completely done with it)
local soundID = audio.loadSound( "bubble_strong_wav.wav" )
-- Background
local halfW = display.viewableContentWidth / 2
local halfH = display.viewableContentHeight / 2
-- Create a table to store all the fish and register this table as the
-- "enterFrame" listener to animate all the fish.
local bounceAnimation = {
container = display.newRect( 0, 0, display.viewableContentWidth, display.viewableContentHeight ),
reflectX = true,
}
local backgroundPortrait = display.newImage( "aquariumbackgroundIPhone.jpg", 0, 0 )
local backgroundLandscape = display.newImage( "aquariumbackgroundIPhoneLandscape.jpg", -80, 80 )
backgroundLandscape.isVisible = false
local background = backgroundPortrait
-- Handle changes in orientation for the background images
local backgroundOrientation = function( event )
-- TODO: This requires some setup, i.e. the landscape needs to be centered
-- Need to add a centering operation. For now, the position is hard coded
local delta = event.delta
if ( delta ~= 0 ) then
local rotateParams = { rotation=-delta, time=500, delta=true }
if ( delta == 90 or delta == -90 ) then
local src = background
-- toggle background to refer to correct dst
background = ( backgroundLandscape == background and backgroundPortrait ) or backgroundLandscape
background.rotation = src.rotation
transition.dissolve( src, background )
transition.to( src, rotateParams )
else
assert( 180 == delta or -180 == delta )
end
transition.to( background, rotateParams )
audio.play( soundID ) -- play preloaded sound file
end
end
-- Add a global listener
Runtime:addEventListener( "orientation", backgroundOrientation )
--
-- Fishies
local numFish = 10
local file1 = "fish.small.red.png"
local file2 = "fish.small.blue.png"
--
-- Define touch listener for fish so that fish can behave like buttons.
-- The listener will receive an 'event' argument containing a "target" property
-- corresponding to the object that was the target of the interaction.
-- This eliminates closure overhead (i.e. the need to reference non-local variables )
local buttonListener = function( event )
if "ended" == event.phase then
local group = event.target
-- tap only triggers change from original to different color
local topObject = group[1]
if ( topObject.isVisible ) then
local bottomObject = group[2]
-- Dissolve to bottomObject (different color)
transition.dissolve( topObject, bottomObject, 500 )
-- Restore after some random delay
transition.dissolve( bottomObject, topObject, 500, math.random( 3000, 10000 ) )
end
-- we handled it so return true to stop propagation
return true
end
end
--
--
--
-- Add fish to the screen
for i=1,numFish do
-- create group which will represent our fish, storing both images (file1 and file2)
local group = display.newGroup()
local fishOriginal = display.newImage( file1 )
group:insert( fishOriginal, true ) -- accessed in buttonListener as group[1]
local fishDifferent = display.newImage( file2 )
group:insert( fishDifferent, true ) -- accessed in buttonListener as group[2]
fishDifferent.isVisible = false -- make file2 invisible
-- move to random position in a 200x200 region in the middle of the screen
group:translate( halfW + math.random( -100, 100 ), halfH + math.random( -100, 100 ) )
-- connect buttonListener. touching the fish will cause it to change to file2's image
group:addEventListener( "touch", buttonListener )
-- assign each fish a random velocity
group.vx = math.random( 1, 5 )
group.vy = math.random( -2, 2 )
-- add fish to animation group so that it will bounce
bounceAnimation[ #bounceAnimation + 1 ] = group
end
--
-- Function to animate all the fish
function bounceAnimation:enterFrame( event )
local container = self.container
container:setFillColor( 0, 0, 0, 0) -- make invisible
local containerBounds = container.contentBounds
local xMin = containerBounds.xMin
local xMax = containerBounds.xMax
local yMin = containerBounds.yMin
local yMax = containerBounds.yMax
local orientation = self.currentOrientation
local isLandscape = "landscapeLeft" == orientation or "landscapeRight" == orientation
local reflectX = nil ~= self.reflectX
local reflectY = nil ~= self.reflectY
-- the fish groups are stored in integer arrays, so iterate through all the
-- integer arrays
for i,v in ipairs( self ) do
local object = v -- the display object to animate, e.g. the fish group
local vx = object.vx
local vy = object.vy
if ( isLandscape ) then
if ( "landscapeLeft" == orientation ) then
local vxOld = vx
vx = -vy
vy = -vxOld
elseif ( "landscapeRight" == orientation ) then
local vxOld = vx
vx = vy
vy = vxOld
end
elseif ( "portraitUpsideDown" == orientation ) then
vx = -vx
vy = -vy
end
-- TODO: for now, time is measured in frames instead of seconds...
local dx = vx
local dy = vy
local bounds = object.contentBounds
local flipX = false
local flipY = false
if (bounds.xMax + dx) > xMax then
flipX = true
dx = xMax - bounds.xMax
elseif (bounds.xMin + dx) < xMin then
flipX = true
dx = xMin - bounds.xMin
end
if (bounds.yMax + dy) > yMax then
flipY = true
dy = yMax - bounds.yMax
elseif (bounds.yMin + dy) < yMin then
flipY = true
dy = yMin - bounds.yMin
end
if ( isLandscape ) then flipX,flipY = flipY,flipX end
if ( flipX ) then
object.vx = -object.vx
if ( reflectX ) then object:scale( -1, 1 ) end
end
if ( flipY ) then
object.vy = -object.vy
if ( reflectY ) then object:scale( 1, -1 ) end
end
object:translate( dx, dy )
end
end
-- Handle orientation of the fish
function bounceAnimation:orientation( event )
print( "bounceAnimation" )
for k,v in pairs( event ) do
print( " " .. tostring( k ) .. "(" .. tostring( v ) .. ")" )
end
if ( event.delta ~= 0 ) then
local rotateParameters = { rotation = -event.delta, time=500, delta=true }
Runtime:removeEventListener( "enterFrame", self )
self.currentOrientation = event.type
for i,object in ipairs( self ) do
transition.to( object, rotateParameters )
end
local function resume(event)
Runtime:addEventListener( "enterFrame", self )
end
timer.performWithDelay( 500, resume )
end
end
Runtime:addEventListener( "enterFrame", bounceAnimation );
Runtime:addEventListener( "orientation", bounceAnimation )
-- This function is never called,
-- but shows how we would unload the sound if we wanted to
function unloadSound()
audio.dispose(soundID)
soundID = nil
end
Lua has a slightly strange behaviour when it comes to ands and ors.
The expression a and b evaluates to a if a is considered false (only nil and false is considered false, all other values including 0 are considered true) and if a is considered true the expression evaluates to b.
The expression a or b evaluates to a if a is considered true and b if a is considered false.
Note: in both cases if the expression evaluates to a that value of b isn't even evaluated. This is called short circuit logic. In and if a is false, the and can't possibly be true, so there is no point in wasting computation time to evaluate b. Similarly, if a is true in a or b there is no point to evaluate b as the or can't possibly be false.
The construct cond and valiftrue or valiffalse (or the equivalent, due to operator precedence, (cond and valiftrue) or valiffalse) is equivalent to other language's ternary if statement, with one caveat: valiftrue must not evaluate to false. If that is the case, the whole expression will always evaluate to valiffalse. (Try and reason it out, that's the best way I find to get a grip on this construct.)
was about to post in detail but #JPvdMerwe got it spot on.
To be precise,
background = ( backgroundLandscape == background and backgroundPortrait ) or backgroundLandscape
translates to
if backgroundLandscape == background then
background = backgroundPortrait
else
background = backgroundLandscape
end
EDIT
As #JPvdMerwe pointed out, in this case, if backgroundPortrait is false then
background = backgroundLandscape
will be executed all the time.

Resources