This is an issue I come up against time and time again. I just can't get my head round it. This code was under create scene now I want to put it in a function with the idea of generated these blocks at different locations automatically through a loop. The errors I get are:
bad argument #1 in newRect (number expected) - 1st line.
sceneGroup is a nil value.
The solutions I have tried are:
1) defining sceneGroup at top of script. But then I get error that upvalue is a nil value.
2) defining it immediately before. - nil value.
If someone could explain this to me I would be very grateful. I keep getting problems like this.
local Backgroundrectangle = display.newRect(sceneGroup, 75, 75, display.contentWidth-150, display.contentHeight/2 )
Backgroundrectangle.isVisible = false
Backgroundrectangle.anchorX = 0
Backgroundrectangle.anchorY = 0
aAbackground = display.newRoundedRect(sceneGroup, Backgroundrectangle.x, Backgroundrectangle.y, 100, 125, 10 )
sceneGroup:insert(aAbackground)
aAbackground.id = "a"
aAbackground.strokeWidth = 2
aAbackground:setFillColor( gradient )
aAbackground:setStrokeColor( 0.2 )
aAmenutext = display.newText( "Aa", 100, 200, "Comic Sans MS", 50)
aAmenutext.x = aAbackground.x
aAmenutext.y = aAbackground.y - aAbackground.height/6
aAmenutext:setFillColor( 0.2 )
sceneGroup:insert(aAmenutext)
"Upvalue is nil" means the Runtime expects sceneGroup to be a local defined outside the scope of the function, but this is not the usual way of doing this in Corona.
If you have this
local composer = require( "composer" )
local scene = composer.newScene()
at the top of your Lua file for the scene, whenever you want to add a DisplayObject to the scene's GroupObject (possibly in your scene:create() method), you can declare
local sceneGroup = scene.view
and then use sceneGroup as you have been. scene will be defined (it has file scope) and the view property gives you the scene's GroupObject.
Related
Currently creating a game with Corona SDK is it possible to have an image and when it is clicked it displays 3 images and once one them 3 images are clicked the score increases by 1. Also Im only a beginner at coding , this is a new language to me. Thanks.
local CButton = display.newImage("+5.jpg" , 100 , 600)
CButton.alpha = 0.5
CButton.name = "CButton"
local CButtonLabel = display.newText( { text = "", x = 0, y = 0, fontSize = 28 } )
CButtonLabel:setTextColor( 0 ) ; CButtonLabel.x = 100 ; CButtonLabel.y = 45
local function touchCListener( event )
local object = event.target
print( event.target.name.." TOUCH on the '"..event.phase.."' Phase!" )
local ChordCOne = display.newImage("+5.jpg", 900,300)
local ChordCTwo = display.newImage("+5.jpg", 1000,300)
local ChordCThree = display.newImage("+5.jpg", 1100,300)
end
--add "touch" listener -- LABEL IS FOR TESTING!
CButton:addEventListener( "touch", touchCListener)
ChordCOne:addEventListener( "touch", updateScore)
CButtonLabel.text = "touch"
Yes, new DisplayObjects can be created in a listener function and listeners can be added to those objects as well.
In your code, you have not added the DisplayObjects created in your listener to any GroupObject (such as scene.view), which will give unexpected results.
Since the variables pointing to the newly created DisplayObjects (ChordCOne, etc.) are local to the function where they are instantiated, you cannot call addEventListener() on them outside the function. You should add the listener when they are created.
Also, the updateScore() listener function isn't defined anywhere. Make sure updateScore is not nil when and wherever you give it as an argument to addEventListener().
I have the following code:
function scene:create( event )
local sceneGroup = self.view
-- Initialize the scene here.
-- Example: add display objects to "sceneGroup", add touch listeners, etc.
local options1 = {
x = display.contentWidth / 2,
y = 200,
onRelease = button,
lableAlign = "center",
emboss = true,
id = "1",
label = "Button1"
}
local options2 = {
y = 300,
x = display.contentWidth / 2,
onRelease = button,
lableAlign = "center",
emboss = true,
id = "2",
label = "Button2"
}
local button1 = widget.newButton(options1)
local button2 = widget.newButton(options2)
sceneGroup:insert(button1)
sceneGroup:insert(button2)
end
When I place this code in a standalone file (not a scene) the buttons show up as supposed. However now I am turning this standalone file into a scene and for some reason the above code results in nothing in the simulator. Any ideas?
You have two choices:
move your scene the a yourScene.lua file (named as you wish) in which you call storyboard.newScene() (no argument) and in your main.lua you use storyboard.goto('yourScene')
you can create the scene in main.lua via storyboard.newScene('yourScene') and goto it from within main.lua via storyboard.goto('yourScene')
Basically your scene can be in a separate module, corona automatically names it based on module name, and main.lua goes to it, or your scene can be in same module, but then you have to give it a name yourself, and main.lua can "goto" it (within same module). You can even have multiple scenes in same module.
I recommend you have your scenes in separate modules, it just makes for better modularization. Conjecture: maybe the method 2 was the original, and method 1 was added latter when developers found their scenes were getting large and so needed to move them to separate modules.
The code posted above is within the main.lua file. Therefore, the scene is never called. The solution is to rename the file above to something like menu.lua and then call this scene from main.lua.
I am trying to understand how to efficiently remove scene objects once a scene is destroyed. According to corona docs objects can be inserted to self.view in order to clean them up once a scene is destroyed.
Here is my code that I am trying
local composer = require( "composer")
local scene = composer.newScene()
local rects={}
local rectsNum = 0
local rectsCount = 0
local rectsTimer
local sceneGroup
local function rectTransComplete( obj )
print("rectTransComplete .. called")
rectsCount = rectsCount + 1
if rectsCount == 3 then
composer.removeScene("scene2")
end
end
local function spawnRect( )
print("inside spawnrects")
rectsNum = rectsNum + 1
rects[rectsNum] = display.newRect( display.contentWidth/2, 100, 100, 100)
rects[rectsNum]:setFillColor( 1,1,1 )
rects[rectsNum].id = numRect
rects[rectsNum].transition = transition.to( rects[rectsNum], {time = 9000, y = display.contentHeight + 100,
onComplete = rectTransComplete
} )
sceneGroup:insert( rects[rectsNum] )
end
function scene:create( event )
sceneGroup = self.view
end
function scene:show(event)
print("inside create")
if event.phase == "did" then
rectsTimer = timer.performWithDelay( 1000, spawnRect, -1 )
end
end
function scene:hide( event )
-- body
end
function scene:destroy( event )
timer.cancel( rectsTimer )
print("scene Destroyed")
end
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )
return scene
I seems to work fine since all the rectangles are removed from the screen once the scene is destroyed but I see the app printing messages "rectTransComplete .. called" even after the scene is destroyed. Why is it so and why are the transitions not automatically cancelled once the objects are removed. Do I need to cancel all transitions manually by iterating through all the rectangles and cancel each transition. What about the rects table as it also contains references to the rects. Should I manually clean it up. If I need to manually clean everything (or most of the things), what exactly is the benefit of using self.view
Just a note, you will also have the same problem if you ever use Runtime events to propagate events to your display objects.
I had the same issue a few days ago, there are 2 ways to approach this:
Method 1: You keep a reference to the opened transitions, iterate through all your references and call transition.cancel(transitionReference) on them.
Method 2: You create a listener in your rectangles objects, send a runtime event to the rectangles object when the scene is destroyed that triggers the listener, and inside the listener you "cancel" the transition linked to current rectangle (transition.cancel(rectangle))
You can have a look at this article if you need a howto to work with runtime events: http://www.engelteddy.com/softwaredevelopment/mobiledevelopment/observer-pattern-corona-sdk/
You need to call transition.cancel() in scene:destroy function.
I'm trying a GTween example from the following link
Gideros GTween with Easing
The example doesn't work out of the box, so I dug into the source code of GTween and added the following lines to my example in order to allow event dispatching.
local tween = GTween.new(jewel, 2, animProperties, gtweenProperties)
tween.suppressEvents = false -- New Line #1
tween.dispatchEvents = true -- New Line #2
tween:addEventListener('complete', function()
stage:removeChild(jewel)
jewel = nil
end)
However, the app crashes. I tried commenting the following line in gtween.lua
self:dispatchEvent(Event.new(name))
and the app doesn't crash, however the callbacks aren't invoked (obviously, why would it?)
This is the stack trace from the app.
gtween.lua:445: attempt to call method 'dispatchEvent' (a boolean value)
stack traceback:
gtween.lua:445: in function 'dispatchEvt'
gtween.lua:255: in function 'setPosition'
gtween.lua:86: in function <gtween.lua:74>
Any pointers would be greatly appreciated. Thanks.
PS: I'm not sure if this is a bug on Gideros.
i just tried with the latest gideros' gtween (note that it is edited 10 days ago), and use this sample (i took the sample from your link and add sprite definition, also include a image file in project) and it works(the callback is called) :
local animate = {}
animate.y = 100
animate.x = 100
animate.alpha = 0.5
animate.scaleX = 0.5
animate.scaleY = 0.5
animate.rotation = math.random(0, 360)
local properties = {}
properties.delay = 0
properties.ease = easing.inElastic
properties.dispatchEvents = true
local sprite = Bitmap.new(Texture.new("box.png")) -- ADD THIS
stage:addChild(sprite) -- ADD THIS
local tween = GTween.new(sprite, 10, animate, properties)
tween:addEventListener("complete", function()
stage:removeChild(sprite)
sprite = nil
end)
I'm currently making a tower defense game with Corona SDK. However, while I'm making the gaming scene, The background scene always cover the monster spawn, I've tried background:toBack() ,however it's doesn't work.Here is my code:
module(..., package.seeall)
function new()
local localGroup = display.newGroup();
local level=require(data.levelSelected);
local currentDes = 1;
monsters_list = display.newGroup()
--The background
local bg = display.newImage ("image/levels/1/bg.png");
bg.x = _W/2;bg.y = _H/2;
bg:toBack();
--generate the monsters
function spawn_monster(kind)
local monster=require("monsters."..kind);
newMonster=monster.new()
--read the spawn(starting point) in level, and spawn the monster there
newMonster.x=level.route[1][1];newMonster.y=level.route[1][2];
monsters_list:insert(newMonster);
localGroup:insert(monsters_list);
return monsters_list;
end
function move(monster,x,y)
-- Using pythagoras to calauate the moving distace, Hence calauate the time consumed according to speed
transition.to(monster,{time=math.sqrt(math.abs(monster.x-x)^2+math.abs(monster.y-y)^2)/(monster.speed/30),x=x, y=y, onComplete=newDes})
end
function newDes()
currentDes=currentDes+1;
end
--moake monster move according to the route
function move_monster()
for i=1,monsters_list.numChildren do
move(monsters_list[i],200,200);
print (currentDes);
end
end
function agent()
spawn_monster("basic");
end
--Excute function above.
timer2 = timer.performWithDelay(1000,agent,10);
timer.performWithDelay(100,move_monster,-1);
timer.performWithDelay(10,update,-1);
move_monster();
return localGroup;
end
and the monster just stuck at the spawn point and stay there.
but, When i comment these 3 lines of code:
--local bg = display.newImage ("image/levels/1/bg.png");
--bg.x = _W/2;bg.y = _H/2;
--bg:toBack();
The problem disappear
Any ideas??Thanks for helping
Since you are using director, you should insert all your display objects into the localGroup.
You haven't inserted bg into localGroup.
SO director class inserts bg finally after inserting localGroup.
Modify your code as
--The background
local bg = display.newImage (localGroup,"image/levels/1/bg.png");
bg.x = _W/2;bg.y = _H/2;
bg:toBack();
or add the code
localGroup:insert(bg)
In more recent versions of Corona SDK:
Composer is the official scene (screen) creation and management library in Corona SDK.... The primary object in the Composer library is the scene object...and it contains a unique self.view.... This self.view is where you should insert visual elements pertaining to the scene.
So now in your scene:create() method, you should insert all DisplayObjects into self.view. It looks like this:
local composer = require( "composer" )
local scene = composer.newScene()
function scene:create()
local sceneGroup = self.view
local bg = display.newImage( ...
bg.x, bg.y = ...
local dude = display.newImage( ...
dude.x, dude.y = ....
sceneGroup:insert(bg)
sceneGroup:insert(dude)
end
The layering of DisplayObjects in this sceneGroup depends on the order in which they are added to the group, with the object added last on top. You can control this ordering with the toBack() and toFront() methods.