Removing all objects, when moving to another scene by a click

I have a problem removing objects (images) in my app. I don’t know how to fix this.

I create and display two images based on two API calls. The idea is: when one image is clicked, all objects should be removed and the user goes to a new page/scene. Depending on the image clicked, the new page is different.

So far, I can remove the image the user clicked and move to another page (event.target:removeSelf()). But the other image cannot be removed. The easiest solution, I think, is to put the two images (each event.target of each API call) into one sceneGroup and remove it, when one of the two images is clicked, but I don’t know how to do it. Or, maybe there is a simpler approach to manipulate two images outside two API call functions.

Do you have any good idea?

--1st API call to fetch 1st image     local json = require( "json" )     local function networkListener( event )         local res = json.prettify( event.response )         local decoded = json.decode( res )         if ( event.isError ) then             print( "--Network error-- ", ( res ) )         else             local edmPreview = decoded.items[lot20].edmPreview[1]             -- Display image from web             local function networkListener1image( event )                 if ( event.isError ) then                     print ( "Network error - download failed" )                 else                     event.target.alpha = 0                     transition.to( event.target, { alpha = 1.0 } )                 end                 print ( "event.response.fullPath: ", event.response.fullPath )                 print ( "event.response.filename: ", event.response.filename )                 print ( "event.response.baseDirectory: ", event.response.baseDirectory )                 --Set an event to move to another scene when the image object is clicked                 local function onTap(event)                     event.target:removeSelf()                     composer.gotoScene("NewPageA",{time=800,effect="crossFade"})                 end                 local myImage = event.target                 myImage:addEventListener("tap", onTap)             end             local loadedimage = display.loadRemoteImage(edmPreview, "GET", networkListener1image, "image1.png", system.TemporaryDirectory, 200, display.contentCenterY )         end     end     network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword1.."&qf=proxy\_edm\_year:"..lotyear1.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params) -- 2nd API call to fetch 2nd image local json2 = require( "json" ) local function networkListener2( event )     local res2 = json.prettify( event.response )     local decoded2 = json2.decode( res2 )         if ( event.isError ) then                 print( "--Network error-- ", ( res2 ) )         else         local edmPreview2 = decoded2.items[lot20].edmPreview[1]         -- Display image from web         local function networkListener2image( event )                 if ( event.isError ) then                         print ( "Network error - download failed" )                 else                         event.target.alpha = 0                         transition.to( event.target, { alpha = 1.0 } )                 end                 print ( "event.response.fullPath: ", event.response.fullPath )                 print ( "event.response.filename: ", event.response.filename )                 print ( "event.response.baseDirectory: ", event.response.baseDirectory )                 --Set an event to move to another scene when the image object is clicked                 local function onTap(event)                         event.target:removeSelf()                         composer.gotoScene("NewPageB",{time=800,effect="crossFade"})                 end                 local myImage = event.target                 myImage:addEventListener("tap", onTap)         end         local loadedimage2 = display.loadRemoteImage(edmPreview2, "GET", networkListener2image, "image2.png", system.TemporaryDirectory, 900, display.contentCenterY )         end end     network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword2.."&qf=proxy\_edm\_year:"..lotyear2.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener2, params)

The above code is of course inserted in the scene template below:

-- ----------------------------------------------------------------------------------- -- Scene event functions -- ----------------------------------------------------------------------------------- -- create() -- :create( event ) indicates that this function will be associated with the Composer create scene event and that a table of data that we reference with event will be passed to the function. function scene:create(event)     --This creates a local reference to the scene's view group, automatically created by Composer, which should contain all of the display objects used in the scene. Essentially, any display object which should be part of the scene must be inserted into the scene's view group, and that group is referenced by sceneGroup inside each of the template's default scene: functions.     local sceneGroup = self.view         -- Code here runs when the scene is first created but has not yet appeared on screen. 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)     elseif ( phase == "did" ) then         -- Start the music loop=-1 means indefinete loop         audio.play(menuMusic,{channel=1,loop=-1})         -- Code here runs when the scene is entirely on screen     end end -- hide() function scene:hide( event )     local sceneGroup = self.view     local phase = event.phase     if ( phase == "will" ) then         -- Code here runs when the scene is on screen (but is about to go off screen)     elseif ( phase == "did" ) then         -- Stop the music         audio.stop(1)         -- Code here runs immediately after the scene goes entirely off screen     end end -- destroy() function scene:destroy( event )     local sceneGroup = self.view     -- We effectively release the memory taken up by the audio file     audio.dispose(menuMusic)     -- Code here runs prior to the removal of scene's view end -- ----------------------------------------------------------------------------------- -- Scene event function listeners -- ----------------------------------------------------------------------------------- scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) -- ----------------------------------------------------------------------------------- return scene

You seem to be using composer, which means that if you add all of your display objects into a display group (and then the sceneGroup), then those display objects will be removed on scene change. There’s also the matter of scope, where you create the images within those functions without returning anything. This means that you won’t have access to them outside of the functions where they are created.

Also, I don’t think that there is ever any reason to load the same library twice in one file, i.e. your json and json2. You should also avoid duplicating code like that since those functions literally do the same thing.

XeduR @Spyric Thank you very much for some tips!

Yes, that’s basically what I am struggling with.

I try to understand what I should do now. Maybe, I can forget about repeating json and json2 for the time being, to simplify the logic.

The first problem is how to process the image outside the scope of the function of a API query. I should return something, right?

Where can I do it, and how to retrieve the returned object in order to be put in a sceneGroup outside the function?

After that, I have to think of how to organise json and json2, because they are not the same. There are two different API queries and results (i.e. images) are different. So, I hope I can avoid repetition as much as possible, but I am still not very sure how to do it…

Then, two objects can be put into sceneGroup and delete them all together. The last question is how to make delete happen. Two objects should be removed, when one of the two objects is clicked. How to invoke that? Currently, the scope of one function (ie json) cannot see the scope of the other function (json2), so this sounds a bit tricky to me to implement.

Do you know a concrete (whole or partial) solution for those issues?

So, to begin with, when you require libraries or other external files/modules, e.g.

local json = require("json")

This means that you can now call functions by referring to the “json” variable as in json.decode(), json.decodeFile(), json.encode() and json.prettify(). This means that creating another variable called “json2” simply creates a duplicate without providing any additional use. This is why I am not sure if there is ever a reason to require the same library/module more than once. You typically want to require all libraries like this (only once) at the beginning of your code file so that you can freely use them later on.

Now, with the functions.
 

local json = require( "json" ) -- just once is enough! -- local composer = require( "composer" ) -- guessing you have this already required elsewhere in the file? local myGroup = display.newGroup() -- sceneGroup:insert( myGroup ) --[[if you add myGroup to your sceneGroup, then it, as well as all display objects inserted into mygroup will be removed on composer.gotoScene( "someScene" ).]]-- local function networkListener( event ) local res = json.prettify( event.response ) local decoded = json.decode( res ) if ( event.isError ) then print( "--Network error-- ", ( res ) ) else local edmPreview = decoded.items[lot20].edmPreview[1] -- Display image from web local function networkListener1image( event ) if ( event.isError ) then print ( "Network error - download failed" ) else myGroup:insert( event.target ) -- the file was downloaded, so add it to myGroup event.target.alpha = 0 transition.to( event.target, { alpha = 1.0 } ) end print ( "event.response.fullPath: ", event.response.fullPath ) print ( "event.response.filename: ", event.response.filename ) print ( "event.response.baseDirectory: ", event.response.baseDirectory ) --Set an event to move to another scene when the image object is clicked local function onTap(event) event.target:removeSelf() composer.gotoScene("NewPageA",{time=800,effect="crossFade"}) end local myImage = event.target myImage:addEventListener("tap", onTap) return myImage -- return myImage to the above function end local loadedimage = display.loadRemoteImage(edmPreview, "GET", networkListener1image, "image1.png", system.TemporaryDirectory, 200, display.contentCenterY ) return loadedImage -- return loadedImage to where the function was called end end local yourImage = network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword1.."&qf=proxy\_edm\_year:"..lotyear1.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params) -- now you can manipulate yourImage outside of the function, but remember that since you are working with asynchronous functions so the image will take time to download

All you really need to do to return something from a function is to write “return [var]”, e.g. “return myImage” at the end of the function. If you return variables like this and have your network request set to some handle, then you can later access that variable through the handle.

I only skimmed through your code quickly, so there may be some mistakes. But, concerning your two functions, there seems to only be slight differences. What you can do is A ) pass some values to the function, e.g. “whatScene”, that you could set to “NewPageA” or “NewPageB” depending on what you need. This way, you can use the same function but just include an extra parameter. Alternatively, B ) you could have some variable that is declared before the function. So, basically the standard Lua function stuff, i.e.
 

-- a) local function printVarA( var ) print( var ) end printVar( "a" ) -- outputs: a printVar( "b" ) -- outputs: b -- b) local myVar local function printVarB() print( myVar ) end myvar = "a" printVar() -- outputs: a myvar = "b" printVar() -- outputs: b

Finally, if you use composer to move between scenes, then any display objects or display groups that have been inserted into the sceneGroup will automatically be removed upon scene change. If you don’t use (or don’t want to use) composer, then you can also just remove the display group that you’ve inserted the objects into and they will be removed.

Thank you very much for the detailed answer. It is so kind of you!

Firstly, I managed to remove two images when one image is clicked and moved to a new scene, using myGroup:insert( event.target ).

Secondly, I managed to refactor the code and make it shorter, taking your suggestion A). So, now the API call function is created.

Now this part looks like:

local composer = require( "composer" ) local json = require( "json" ) local function apicall (newpage, lotkeyword, lotyear, xbutton, ximage, filenumber)     local function networkListener( event )     local res = json.prettify( event.response )     local decoded = json.decode( res )     if ( event.isError ) then         print( "--Network error-- ", ( res ) )     else         print( "Results: " .. ( res ) )         local rights = decoded.items[lot20].rights[1]         local year = decoded.items[lot20].year[1]         local edmPreview = decoded.items[lot20].edmPreview[1]         -- Display image from web         local function networkListener1image( event )             if ( event.isError ) then                 print ( "Network error - download failed" )             else                 myGroup:insert( event.target )                 event.target.alpha = 0                 transition.to( event.target, { alpha = 1.0 } )             end             print ( "event.response.fullPath: ", event.response.fullPath )             print ( "event.response.filename: ", event.response.filename )             print ( "event.response.baseDirectory: ", event.response.baseDirectory )             --Click an image and go to next scene (remove all objects)             local function onTap(event)                 event.target:removeSelf()                 composer.gotoScene(newpage,{time=800,effect="crossFade"})             end             local myImage = event.target             myImage:addEventListener("tap", onTap)             return myImage         end         local loadedimage = display.loadRemoteImage(edmPreview, "GET", networkListener1image, "image"..filenumber..".png", system.TemporaryDirectory, ximage, display.contentCenterY )             return loadedImage         end     end     local yourImage    = network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword.."&qf=proxy\_edm\_year:"..lotyear.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params)     return yourImage end apicall("pageA", lotkeyword1, lotyear1, 50, 200, 1) apicall("pageB", lotkeyword2, lotyear2, 800, 900, 2)

Those are exactly what I needed. Excellent.

Apart from that, the only thing I didn’t still understand well, is this part:

If you return variables like this and have your network request set to some handle, then you can later access that variable through the handle.

Can you show me a quick example? For example, how can I specify the position of the image (myImage) outside the function (networkListener(event))? I am guessing, I would use “params” to manipulate the image, after the following code, but not sure…

local yourImage = network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword1.."&qf=proxy\_edm\_year:"..lotyear1.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params)
local function newObject( x, y, width, height ) local object = display.newRect( x, y, width, height ) object:setFillColor( 1, 0, 0 ) return object end local object = newObject( 160, 240, 80, 20 ) object.x = object.x+40 object.y = 200

This would be the most basic way of doing that. However, as I pointed out at the very end of my previous sample code, that network.request is an asynchronous function, which means that if you tried to immediately move the display object, you’d probably crash the app. This is because the file would not yet have been downloaded and so the image wouldn’t exist yet (i.e. it’d be nil).

So, this is the way to do it, but you need to leave manipulating the object until it has been downloaded. In cases like this, you can always check if it exists before trying anything to avoid crashes, e.g. “if object then”, etc.

Thank you XeduR @Spyric again for instructing me very well. :slight_smile:

But, maybe I confused you a bit. I know how to the position of the image in general.

My question was about how to access the object returned from the functions (i.e. variables: yourImage or loadedImage) outside the function. Then, I simply replace it with the “object” variable in your last code, right?

The question may be pretty basic and I did a few trials and errors, but I did not figure it out by myself. I’m too beginner… :frowning:

I always get an error “attempt to call field ‘loadedImage’ (a nil value)”, if I do something like

local function newObject( x, y, width, height, loadedImage)     local object = display.loadedImage( x, y, width, height )     object:setFillColor( 1, 0, 0 )     return object end local object = newObject( 160, 240, 80, 20, loadedImage )

I totally understood the asynchronous actions, and yes, I have to do something with that to avoid getting nil. Cheers!

You get the aforementioned error because of how asynchronous functions work (and how I showed you in the code how to return variables from functions). With asynchronous functions, you just need to wait for them to finish before you can perform any operations on the variables you return from the functions (i.e. wait for the download or some other process to finish). If you try to change the x position of a display object before it has been created, it won’t yet exist and that is why it crashes.
 

display.loadedImage does not exist, that’s why you’re getting the nil error there.

Ah Ok, I see. asynchronous is tricky!

I almost always get nil, so it seems to be impossible to access and manipulate the image so quickly, unless I write more code to wait for the image.

In that case, I think the best way would be to use the saved images (image1.png) from the function, and manipulate it.

So, my solution would be to add the following after/outside the function (in this case change the colour of the image), and it worked.

local catImage = display.newImage( "image1.png", system.TemporaryDirectory, 0, 0 ) catImage:setFillColor( 1, 0, 0 )

Cheers!

With the above example, I can access images, but another problem is I have to think how to get other data than images (textual data from API calls), because I need to display them as well. But that’s another story…

Removing and/or maniplulating objects/images (as suggested in the question title) is accomplished. Thank you so much all!

I think you’re still missing the point of asynchronous. Can you guarantee that “image1.png” will exist at the time you try to display it? I’m not sure you can, if there are network issues or it’s a big file. You might also end up displaying the image twice.

If I understand correctly, you want to load an image remotely, and when leaving the scene have that image be removed. In order to do that, you either need to have a handle on the image so you can remove it manually, or add the image to the scene.view group.

When you call display.loadRemoteImage, this returns nothing immediately. You need to wait until the networkListener function is fired upon successful download, and then event.target is the handle to the image. You can then store this handle in a variable that is accessible outside the listener. I’ve modified the corona API example:

[lua]

local myDownloadedImage  – I am currently nil, but I have been forward-declared so I can be populated later and accessed from anywhere in this lua file
local theImageWasDownloaded = false

local function networkListener( event )
 
    if ( event.isError ) then
        print ( “Network error - download failed” )
    else
        myDownloadedImage = event.target – now I’m not nil
        myDownloadedImage:setFillColor(1,0,0)
        scene.view:insert(myDownloadedImage) – now Composer will automatically clean me up when we leave the scene

    end

end

display.loadRemoteImage( “http://coronalabs.com/images/coronalogogrey.png”, “GET”, networkListener, “coronalogogrey.png”, system.TemporaryDirectory, 50, 50 )

print (myDownloadedImage) – I am almost certainly still nil here. If I’m not, it’s not guaranteed.

local gameLoop = function ()  – I will run once a frame

  if (myDownloadedImage ~= nil and theImageWasDownloaded == false) then
   
    theImageWasDownloaded = true
   print (“myDownloadedImage is no longer nil. I can now be moved, coloured, resized, removed etc”)
  end

end

Runtime:addEventListener(“enterFrame”,gameLoop)

 

[/lua]

Thank you very much @nick_sherman

Ok, so, saving image as image1.png is not good enough to ensure the existence of the image, right?  :( 

If I understand your code correctly, the whole situation would now look like:

local composer = require( "composer" ) local json = require( "json" ) local myDownloadedImage1 local myDownloadedImage2 local theImageWasDownloaded = false local function apicall (newpage, lotkeyword, lotyear, xbutton, ximage, filenumber, myDownloadedImage)     local function networkListener( event )     local res = json.prettify( event.response )     local decoded = json.decode( res )     if ( event.isError ) then         print( "--Network error-- ", ( res ) )     else         print( "Results: " .. ( res ) )         local rights = decoded.items[lot20].rights[1]         local year = decoded.items[lot20].year[1]         local edmPreview = decoded.items[lot20].edmPreview[1]         -- Display image from web         local function networkListener1image( event )             if ( event.isError ) then                 print ( "Network error - download failed" )             else                 myDownloadedImage.alpha = 0                 transition.to( myDownloadedImage, { alpha = 1.0 } )                 myDownloadedImage = event.target                 scene.view:insert(myDownloadedImage)             end             print ( "event.response.fullPath: ", event.response.fullPath )             print ( "event.response.filename: ", event.response.filename )             print ( "event.response.baseDirectory: ", event.response.baseDirectory )             --Click an image and go to next scene (remove all objects)             local function onTap(event)                 myDownloadedImage:removeSelf()                 composer.gotoScene(newpage,{time=800,effect="crossFade"})             end             myDownloadedImage:addEventListener("tap", onTap)         end         local loadedimage = display.loadRemoteImage(edmPreview, "GET", networkListener1image, "image"..filenumber..".png", system.TemporaryDirectory, ximage, display.contentCenterY )         end     end     local yourImage    = network.request("https://www.europeana.eu/api/v2/search.json?wskey="..apikey.europeana.."&query="..lotkeyword.."&qf=proxy\_edm\_year:"..lotyear.."&start="..lot20.."&reusability=open&media=true", "GET", networkListener, params) end apicall("pageA", lotkeyword1, lotyear1, 50, 200, 1, myDownloadedImage1) local gameLoop = function ()      if (myDownloadedImage ~= nil and theImageWasDownloaded == false) then         theImageWasDownloaded = true         print ("myDownloadedImage is no longer nil. I can now be moved, coloured, resized, removed etc")         myDownloadedImage:setFillColor( 1, 0, 0 )     end end Runtime:addEventListener("enterFrame",gameLoop) apicall("pageB", lotkeyword2, lotyear2, 800, 900, 2, myDownloadedImage2) local gameLoop = function ()      if (myDownloadedImage ~= nil and theImageWasDownloaded == false) then         theImageWasDownloaded = true         print ("myDownloadedImage is no longer nil. I can now be moved, coloured, resized, removed etc")         myDownloadedImage:setFillColor( 1, 0, 0 )     end end Runtime:addEventListener("enterFrame",gameLoop) 

The gameLoop function is repeated, because I have two images from two functions. It could be refactored to reduce the code, but theoretically this would work (more or less), no?

A lot of checking to create the right code! If this is still wrong, perhaps I should cry and give up Lua :smiley:

It seems good thing is I could use the same approach to check the availability of the texts, fetched from API calls, outside the API

functions.

You’re getting there, a few mistakes though. Seems you might be trying to run before you can walk, I had built and published several games before I went anywhere near asynchronous programming and networking.

  1. You are trying to access the alpha, and make a transition on the alpha of myDownloadedImage before it is set to anything. The first thing you should do is myDownloadedImage = event.target. 

  2. You can’t have two ‘gameLoop’ functions. You could have two with different names, but you probably don’t even need them and if you did, one function to handle both scenarios would be fine. They were just there to illustrate the point that myDownloadedImage will be nil and then at some unknown point in the future, it won’t be. 

  3. The gameLoop functions as they are won’t do anything, as they are looking for myDownloadedImage, which never exists in their scope. They need to check for myDownloadedImage1 and 2, which are passed into apicall and within that function, have the pseudonym of myDownloadedImage, but outside are still accessed by their individual names.

  4. As you are adding myDownloadedImage to scene.view, you don’t need to call removeSelf - that’s the whole point. Composer will deal with it for you.  Although, your code doesn’t include the basic Composer scene variables and listeners that allow this magic to happen (scene:create etc), have you got those as per this link? https://docs.coronalabs.com/api/library/composer/index.html

i’m late to this conversation, and won’t attempt to directly answer, but my personal impression of the latest posted code was “oh my goodness!!”  :smiley:

what’s shown below is NOT a direct answer to your question, but may suggest a simpler structure for the LOOPING of asynchronous events (ie, set up a task list, start first request, then have the receiver call the requester until done, then callback to somewhere else)

fwiw, hth:

local downloadTaskList = { { url="http://www.google.com/logos/2012/eclipse12-hp.jpg", localFilename = "image1.jpg", screenX = display.contentCenterX, screenY = display.contentCenterY-200, downloadAttempted = false, downloadSucceeded = false, }, { url="http://www.google.com/logos/doodles/2018/celebrating-garden-gnomes-6194737877876736.5-l.png", localFilename = "image2.png", screenX = display.contentCenterX, screenY = display.contentCenterY, downloadAttempted = false, downloadSucceeded = false, }, { url="http://www.google.com/logos/doodles/2017/valentines-day-2017-day-4-5165155370401792-hp2x.jpg", localFilename = "image3.jpg", screenX = display.contentCenterX, screenY = display.contentCenterY+200, downloadAttempted = false, downloadSucceeded = false, }, } local downloadTaskIndex = 1 local downloadTaskImages = {} local downloadTaskOnComplete -- forward declare, to resolve scope local downloadTaskListener -- forward declare, to resolve scope local function downloadTaskRequester(onComplete) if (downloadTaskIndex \> #downloadTaskList) then -- all done if (type(downloadTaskOnComplete)=="function") then downloadTaskOnComplete() end else -- still tasks left to do print("requesting task # ", downloadTaskIndex) local task = downloadTaskList[downloadTaskIndex] display.loadRemoteImage(task.url, "GET", downloadTaskListener, task.localFilename, system.TemporaryDirectory, task.screenX, task.screenY) task.downloadAttempted = true end end downloadTaskListener = function(event) print("receiving task # ", downloadTaskIndex, "isError:", event.isError, "response:", event.response) if (event.isError) then -- whatever, as appropriate, handle the error somehow - sorry, but you didn't get this image, deal with it else downloadTaskList[downloadTaskIndex].downloadSucceeded = true downloadTaskImages[downloadTaskIndex] = event.target end -- attempt next task downloadTaskIndex = downloadTaskIndex + 1 downloadTaskRequester() end downloadTaskOnComplete = function() print("all download tasks completed") -- composer.gotoScene(...for example end downloadTaskRequester()

Hi  @nick_sherman

  1. If I understand you, the correct code (only the order is changed):

    myDownloadedImage = event.target scene.view:insert(myDownloadedImage)         myDownloadedImage.alpha = 0 transition.to( myDownloadedImage, { alpha = 1.0 } )

  2. Yes, true there are two functions with the same name…I did not check carefully. I could refactor that part of the code.

But, still, the gameLoop function should stay there, even if it does not do anything, but checking the state of the images. Is that what you mean?

  1. You mean I can use the variables (ie myDownloadedImage1 and 2) outside the API call functions to access the images individually?

  2. is not a problem. It is simply omitted from the last code above to focus on the core part. You can see my very first post, talking about the scene template. Then, according to you, this means myDownloadedImage:removeSelf() can be omitted.

Thank you very much also for @davebolinger for more info.

As you seem to have a lot of experience, I would love to refactor the code, but that takes much longer time to implement, as my level of Lua coding is very low. First, I have to examine your code example, to understand what is going on, and I need time to do so. Then, I have to think exactly how it can be applied to my code. That’s another challenge.

  1. Correct

  2. I would not keep the gameLoop if you don’t need it. 

  3. Yes. Of course, you’ll still need to check they are not nil when you use them.

You seem to be using composer, which means that if you add all of your display objects into a display group (and then the sceneGroup), then those display objects will be removed on scene change. There’s also the matter of scope, where you create the images within those functions without returning anything. This means that you won’t have access to them outside of the functions where they are created.

Also, I don’t think that there is ever any reason to load the same library twice in one file, i.e. your json and json2. You should also avoid duplicating code like that since those functions literally do the same thing.

XeduR @Spyric Thank you very much for some tips!

Yes, that’s basically what I am struggling with.

I try to understand what I should do now. Maybe, I can forget about repeating json and json2 for the time being, to simplify the logic.

The first problem is how to process the image outside the scope of the function of a API query. I should return something, right?

Where can I do it, and how to retrieve the returned object in order to be put in a sceneGroup outside the function?

After that, I have to think of how to organise json and json2, because they are not the same. There are two different API queries and results (i.e. images) are different. So, I hope I can avoid repetition as much as possible, but I am still not very sure how to do it…

Then, two objects can be put into sceneGroup and delete them all together. The last question is how to make delete happen. Two objects should be removed, when one of the two objects is clicked. How to invoke that? Currently, the scope of one function (ie json) cannot see the scope of the other function (json2), so this sounds a bit tricky to me to implement.

Do you know a concrete (whole or partial) solution for those issues?