Group Display Objects vs. Tables in Corona Lua - lua

First time post and I sincerely apologize if this has been answered in any other post but I have not been able to find a resolution for the problem I'm facing on this or any other site. I'm a new programmer self-teaching with web tutorials and any other resource I have found. I am trying to create code which will spawn characters and allow you to call them. I've had trouble assigning an index value to the individual instances I have created with a for function. I have tried to establish the instance as both a table and a group display object. If anyone is able to point me in the direction of any resources to get a more indepth understanding of tables and group display objects for the Corona SDK implementation of Lua I'm sure that my problem is that I don't have a thorough enough understanding of these particular functionalities and how they work. Here is the code I've written so far.
-- Character Game
require "sprite"
require "ui"
local background = display.newImage("Background Placeholder.png")
halfW = display.viewableContentWidth / 2
halfH = display.viewableContentHeight / 2
local numCharacters = 20
local roundedRect = display.newRoundedRect( 365, 20, 110, 40, 8 )
roundedRect:setFillColor( 0, 255, 0, 80 )
score = 0
t = ui.newLabel{ bounds = { 370, 30, 100, 40 },
text = "Score " .. score,
textColor = { 255, 0, 20, 255 },
size = 18,
align = "center"
}
local scoreboard = function ( event )
t:setText( "Score " .. score )
end
Runtime:addEventListener( "enterFrame", scoreboard )
local group = display.newGroup()
local character = sprite.newSpriteSheetFromData( "Character Placeholder.png", require("Character Placeholder").getSpriteSheetData() )
local characterSet1 = sprite.newSpriteSet(character,1,8)
sprite.add(characterSet1,"character",1,8,1500,0)
local characterplay = function( event )
score = score + group.points
group[i]:removeSelf()
end
do
for i=1, numCharacters do
group:insert(sprite.newSprite(characterSet1))
group[i].xScale = .2
group[i].yScale = .2
group.points = 50
group[i]:setReferencePoint ( display.BottomCenterReferencePoint )
group[i]:translate( halfW + math.random( -100, 100 ), halfH + math.random( -130, -110 ) )
end
timer.performWithDelay( 500, charactermovie )
for i=1, 21 do
timer.performWithDelay( math.random( 500, 5000 ) , charactermove )
charactermove = function(event )
transition.to( group[i], { time=10000, y = 580 } )
transition.to( group[i], { time=8000, x = math.random( 0, 480 ) } )
transition.to( group[i], { time=7000, xScale = 1.5} )
transition.to( group[i], { time=7000, yScale = 1.5} )
group[i]:prepare("character")
group[i]:play()
end
group[i]:addEventListener( "tap", characterplay )
end
end
charactermovie = function( event )
group[i]:prepare("character")
group[i]:play()
end
local function spriteListener( event )
print( "Sprite event: ", event.sprite, event.sprite.sequence, event.phase )
end
for i, group in pairs(group) do print (group, i, v) end
I'm currently getting an "attempt to index field '?' at the line containing this code "group[i]:addEventListener( "tap", zombieplay )" upon launch and a "nil key supplied for property lookup" error at the "group[i]:removeSelf()" line of the "zombieplay" function. I've tried moving the offending code to a variety of locations to see if this is a scoping issue but I largely run into the same error and believe I may not properly understand indexes and keys... I've found that the app functions as intended but I have to call index keys 1 through 21 to get them all to move even though I am only calling for 20 characters and the removeSelf line throwing errors is not removing the individual characters. I'm going to try writing a module for the characters and see if that helps clear any of this up. I will post my results shortly.

Your characterplay and charactermovie functions are trying to use the variable i, this is outside the functions scope.
There is a property of event called target, which is used to get the event callee. You want to do something like this:
local characterplay = function( event )
score = score + group.points
event.target:removeSelf()
end

Related

Listen Event for Any Object in a Table

Another newbie query. Now on my third day of working with Corona.
The following code works fine: Balloons are generated and float into the air. Now I want to use :addEventListener( "tap", pushBalloon ) to make it so that when a balloon is clicked the pushBalloon is executed. Can anyone tell me what variable I would use and how I would define it? And also I guess I would have to change the pushBalloon function too for the new variable.
Thank you.
local function createBalloon()
local randomBalloon = math.random( 10 )
local newBalloon = display.newImageRect( objectSheet, randomBalloon, 112, 142 )
table.insert( balloonsTable, newBalloon )
physics.addBody( newBalloon, "dynamic", { radius=70, bounce=0 } )
newBalloon.myName = "bigBalloon"
newBalloon.alpha = 0.75
newBalloon.gravityScale = randomBalloon/-150
local whereFrom = math.random( 3 )
if ( whereFrom == 1 ) then
-- From the left
newBalloon.x = 100
newBalloon.y = display.contentHeight+150
elseif ( whereFrom == 2 ) then
-- From the top
newBalloon.x = 160
newBalloon.y = display.contentHeight+150
elseif ( whereFrom == 3 ) then
-- From the right
newBalloon.x = 220
newBalloon.y = display.contentHeight+150
end
end
local function gameLoop()
-- Create new balloon
createBalloon()
-- Remove balloons which have drifted off screen
for i = #balloonsTable, 1, -1 do
local thisBalloon = balloonsTable[i]
if ( thisBalloon.x < -100 or
thisBalloon.x > display.contentWidth + 100 or
thisBalloon.y < -100 )
then
display.remove( thisBalloon )
table.remove( balloonsTable, i )
end
end
end
local function pushBalloon()
-- balloon:applyLinearImpulse( 0.2, -2, balloon.x, balloon.y )
-- tapCount = tapCount + 1
-- tapText.text = tapCount
newBalloon.gravityScale = 10
end
You are adding the newBalloon objects to a table, but you should add the event listener to each newBalloon DisplayObject as it is instantiated. This doesn't do exactly what you ask in the title (where simply inserting an object into a table would effectively add an event listener to that object), but achieves the event response I think you are looking for.
If you are tapping the balloon, you would put the listener on the balloon. If you use a "tap" event, the target property tells you which object was touched, so your pushBalloon() function works for any balloon.
local pushBalloon( event )
local balloon = event.target
if event.phase == "began"
-- do something to the balloon object (apply impulse, etc.)
end
end
local function createBalloon()
...
local newBalloon = display.newImageRect( ... )
if newBalloon then
-- set properties of DisplayObject and add event listener
newBallon:addEventListener( "tap", pushBalloon )
end
...
end
I have wrapped the call to addEventListener() in a check to make sure newBalloon ~= nil.

Corona SDK. Lua. display.newText() - my updated score text is overlapping old one without erasing

I'm developing clone version of Nintendo Tetris game using Corona SDK. There are two text objects on the top of my screen: one represents current level, another one represents current score. Every time I fill in line with blocks my program erases this line and add some scores and +1 level. The problem is that once I update my score and level variables and use myText.text to refresh my texts it doesn't erase old text and creates the new text that overlapping the old one.
My code is following:
1) I declare two local variables at the begging of my scene
local scoreText
local levelText
2) I have function that erases the line and updates texts
function eraseLines()
-- some code that erases lines
scores = scores + 10
scoreText.text = "Score:"..scores
level = level + 1
levelText.text = "Level:"..level
end
3) In scene:show(event) I create our texts
function scene:show( event )
-- some code
scoreText = display.newText("Score:"..scores, halfW*0.5, 20 )
levelText = display.newText("Level:".. level, halfW*1.5, 20 )
sceneGroup:insert( scoreText )
sceneGroup:insert( levelText )
scoreText:setFillColor( 0, 0, 0 )
levelText:setFillColor( 0, 0, 0 )
end
Please help me to find out why overlapping happens
At the moment you are adding twice score/level labels, 'cause the show event is called two times (phases) will and did. Add display objects when you are creating the scene.
-- create()
function scene:create( event )
local sceneGroup = self.view
-- Code here runs when the scene is first created but has not yet appeared on screen
scoreText = display.newText( "Score: 0", halfW * 0.5, 20 )
levelText = display.newText( "Level: 0", halfW * 1.5, 20 )
sceneGroup:insert( scoreText )
sceneGroup:insert( levelText )
scoreText:setFillColor( 0, 0, 0 )
levelText:setFillColor( 0, 0, 0 )
end
-- show()
function scene:show( event )
local sceneGroup = self.view
local phase = event.phase
if ( phase == "will" ) then
-- Code here runs when the scene is still off screen (but is about to come on screen)
scoreText.text = "Score: " .. score
levelText.text = "Level: " .. level
elseif ( phase == "did" ) then
-- Code here runs when the scene is entirely on screen
end
end
Here's a poorly constructed single script for displaying score
local scoreCounter = {}
local frameTime = 0
scoreCount = 0
finalScore = nil
local tempText = nil
local function update( event )
frameTime = frameTime + 1
--after every 7 frames score will increase
if(frameTime % 7 == 0) then
scoreCount = scoreCount + 1
tempText.text = scoreCount
frameTime = 0
end
end
-- here is a memory leak I guess
function scoreCounter.displayScore(xCoordinate, yCoordinate)
tempText = display.newText(scoreCount, xCoordinate, yCoordinate)
end
Runtime:addEventListener("enterFrame", update)
return scoreCounter
Usage:
local scoreCounter = require("pathtoluafile")
scoreCounter.displayScore(xCoordinate, yCoordinate)

Attempt to index global 'object' (a nil value)

So this is the error I have been getting:
Game.lua:66: attempt to index global 'Spears' (a nil value)
stack traceback:
Game.lua:66: in function '_listener'
and this is some of the code, showing where the error happens:
local Spears = {}
local SpearsTimer
local SpearsCounter = 1
local delayTimer
local removeListeners
end
end
local field = display.newCircle( 0, 0, 0 ) ; field.alpha = 0.3
field.name = "field"
field.x = display.contentCenterX ; field.y = display.contentCenterY
physics.addBody( field, "static", { isSensor=true, radius=320 } )
local spawnSpears = function()
local Fall = math.random(display.contentWidth * -0.2, display.contentWidth * 1.2)
Spears[SpearsCounter] = display.newImageRect( "Spear.png", 15, 50 )
Spears[SpearsCounter].x = Fall
Spears[SpearsCounter].y = -200
physics.addBody( Spears[SpearsCounter], "dynamic", {bounce = 0} )
--Spears[SpearsCounter].collision = onCollision
--Spears[SpearsCounter]:addEventListener( "collision", Spears[SpearsCounter] )
transition.to( Spears[SpearsCounter], { rotation = Spears[SpearsCounter].rotation+720, time=15000, onComplete=spinImage } )
Spears[SpearsCounter].gravityScale = 0.5
sceneGroup:insert(Spears[SpearsCounter])
group:insert(Spears[SpearsCounter])
SpearsCounter = SpearsCounter + 1
end
SpearsTimer = timer.performWithDelay( 5000, spawnSpears, -1 )
The Error points to line 66, which is this line of code:
Spears[SpearsCounter] = display.newImageRect( "Spear.png", 15, 50 )
So what am I doing wrong?
Oh, and keep in mind that I am trying to make objects spawn randomly throughout the screen and fall/go to the center of the screen. I put Radial Gravity.
The problem is you have declared the
local Spears = {}
in side a function which is not accessible outside that function. Try declaring it outside the function and access. Learn about the scope of the variables.
This is most likely a scope problem. This tutorial will guide you into understanding scope:
https://coronalabs.com/blog/2015/06/16/tutorial-scope-for-beginners/

Attempt to Index GlobalCredits 'event' (a nil value)

I have just started to learn LUA in school, but i cannot find many helpful tutorials o the internet to aid in my learning. I have made a simple game (which doesn't work yet, i realize that) and a main menu. However, when i try to start the app, it gives me this error:
/Users/jordanmcbride/Desktop/Lua Projects/Tapinator/main.lua:47: attempt to index global 'showCredits' (a nil value)
stack traceback:
/Users/jordanmcbride/Desktop/Lua Projects/Tapinator/main.lua:47: in main chunk
[Finished in 9.4s]
I have looked the error, and I cannot seem to understand how to fix it. The other questions have said something about returning the function returning a nil value, and that I should add a return statement to the end, but that doesn't work either.
Here is the code # line 47.
function showCredits.touch(e)
playButton.isVisible = false
creditsButton.isVisible = false
creditsView = display.newImage('credits.png', 0, display.contentHeight)
lastY = name.y
transition.to(name, {time = 300, y = display.contentHeight * 0.5 - title.height - 25})
transition.to(creditsView, {time = 300, y = display.contentHeight * 0.5 + creditsView.height, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end
Here is my full code, in case the problem lies elsewhere:
display.setStatusBar(display.HiddenStatusBar)
radius = 40
smallTime = 200
bigTime = 800
score = 0
scoreInc = 2000
--HomePage
local name
local playButton
local creditsButton
local homePage
--Credits
local creditsPage
--Sounds
local circleSpawn = audio.loadSound( "circle_spawn.wav" )
local circleTap = audio.loadSound( "circle_tap.wav" )
function Main()
name = display.newImage('title.png', display.contentWidth / 2, 53)
name:scale( .5, .5 )
playButton = display.newImage('playButton.png', display.contentWidth / 2, 245)
playButton:scale( .5, .5 )
creditsButton = display.newImage('creditsButton.png', display.contentWidth / 2, 305)
creditsButton:scale( .5, .5 )
homePage = display.newGroup(name, playButton, creditsButton)
startButtonListeners('add')
end
function showCredits.touch(e)
playButton.isVisible = false
creditsButton.isVisible = false
creditsView = display.newImage('credits.png', 0, display.contentHeight)
lastY = name.y
transition.to(name, {time = 300, y = display.contentHeight * 0.5 - title.height - 25})
transition.to(creditsView, {time = 300, y = display.contentHeight * 0.5 + creditsView.height, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end
function hideCredits.touch(e)
transition.to(creditsView, {time = 300, y = display.contentHeight, onComplete = function() creditsButton.isVisible = true playButton.isVisible = true creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
transition.to(name, {time = 300, y = lastY});
end
function startButtonListeners(action)
if(action == 'add') then
playButton:addEventListener('touch', playGame)
creditsButton:addEventListener('touch', showCredits)
else
playButton:removeEventListener('touch', playGame)
creditsButton:removeEventListener('touch', showCredits)
end
end
Main()
printScore = display.newText("Score: " .. tostring(score), display.contentWidth-80, 40, native.systemFontBold, 20)
-- A function that creates random circles
function generateCircle ()
-- Creates a new circle between 0 (the left most bounds) and the width of the display (being the content width), and also
-- 0 (the upper most bounds) and the height of the display (being the content height). The radius of the circle is 'radius'
x = math.random(radius, display.contentWidth-radius)
y = math.random(80, display.contentHeight)
score = score + scoreInc
myCircle = display.newCircle( x, y, radius )
myCircle:setFillColor( math.random(), math.random(), math.random() )
delayTime = math.random(smallTime, bigTime)
score = score + scoreInc
printScore.text = "Score:"..tostring(scores)
local spawnChannel = audio.play( circleSpawn )
timer.performWithDelay( delayTime, generateCircle )
end
generateCircle()
function myCircle:touch( event )
local tapChannel = audio.play( circleTap )
myCircle:removeSelf()
end
myCircle:addEventListener( "touch", myCircle )
The answer by greatwolf will work. But just a tip from my experience. One way I like to create functions is to try to define the name of the function first near the top of the lua file. Then I will define the function later on in the file. Something like this:
--function preallocation
local onPlayTap
local onSoundOnTap
local onSoundOffTap
local onCreditsTap
local onHelpTap
---------------------------------------------------------------------------------
-- Custom Function Definitions
---------------------------------------------------------------------------------
--Called when Sound On Button is tapped, turn off sound
onSoundOnTap = function(event)
end
--Called when Sound Off Button is tapped, turn on sound
onSoundOffTap = function(event)
end
--Called when Credits button is tapped, shows credits
onCreditsTap = function(event)
end
--Called when Help button is tapped, shows help
onHelpTap = function(event)
end
--Callback to Play button. Moves scene to Level Picker Scene
onPlayTap = function(event)
end
What this does is allow each function to be called by any other function in the file. If you do it the way you are doing it by adding the function name before the function like so:
local showCredits = {}
function showCredits.touch(e)
end
local hideCredits = {}
function hideCredits.touch(e)
end
your showCredit function will not be able to call the hideCredits function below it because the hideCredits variable has not been defined yet when the showCredit function was defined. Although this may not effect your current game, in future apps or games, you may need to call functions inside of other functions. To make this work properly, predefine all your function variables first, then define all your function afterwards. Hope this helps.

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.

Resources