Sometimes, the design of an app dictates the need for “image swapping” in which the developer displays an image at a given location and then, after some time or some action, swaps that image for another. Here’s a theoretical example:
local myImage = display.newImageRect( "image1.png", 64, 64 ) -- After some time or upon some event myImage:swapImage( "image2.png" )
However, this is not how Solar2D works because :swapImage()
is not a
local myImage = display.newImageRect( "image1.png", 64, 64 ) -- After some time or upon some event display.remove( myImage ) myImage = display.newImageRect( "image2.png", 64, 64 )
In this theoretical scenario, by destroying image1.png
with the display.remove() call, you’re probably not planning on using it again soon. The advantage to this method is that it minimizes texture memory since only one of the two images is loaded at once. However, if you want to get the original image back, you’re caught in this cycle of image loading and unloading over and over. In itself, this is an inefficient process that can impact performance. Thus, if you need to swap images more frequently, you should explore other techniques. Let’s look at a few options:
If you have two images, you can simply load them both, add them to a display group, and reference the images as parameters of the group. In this example, we load two images, redBall
and blueBall
. We then position them at the same location, making one visible and the other not.
-- Create a basic display group local imageGroup = display.newGroup() -- Create a red ball inside the group local redBall = display.newImageRect( imageGroup, "red-ball.png", 128, 128 ) redBall.x = display.contentCenterX redBall.y = display.contentCenterY -- Create a blue ball inside the same group local blueBall = display.newImageRect( imageGroup, "blue-ball.png", 128, 128 ) blueBall.x = display.contentCenterX blueBall.y = display.contentCenterY -- Make the blue ball invisible blueBall.isVisible = false
Now let’s focus on the swapping code:
for i = 1,imageGroup.numChildren do if ( imageGroup[i].isVisible == false ) then imageGroup[i].isVisible = true else imageGroup[i].isVisible = false end end
Simply enough, this code loops through the children of imageGroup
which are obviously redBall
and blueBall
. If the child is invisible, it’s toggled back to visible and
You can also take advantage of the graphics “fill” methods to swap images:
local image1 = { type="image", filename="red-ball.png" } local image2 = { type="image", filename="blue-ball.png" } local ball = display.newRect( display.contentCenterX, display.contentCenterY, 128, 128 ) ball.fill = image1 ball.isShowing = "image1"
This method eliminates the need for the display group — we just create a base display object (in this case a rectangle the size of the images) and fill it with the image1
paint of red-ball.png
isShowing
, to be used in the swapping code as follows:
if ( ball.isShowing == "image1" ) then ball.fill = image2 ball.isShowing = "image2" else ball.fill = image1 ball.isShowing = "image1" end
Sometimes you’ll want to swap more than two images. In this case, we can go back to the display group model, load all of the images into an array, and access them via their index number. Here, we iterate through the imageCache
table to create the display objects, storing them in the balls
table as we go along, and make each one invisible. Then, we make the first one visible again.
local imageGroup = display.newGroup() local imageCache = { "red-ball.png", "blue-ball.png", "green-ball.png", "yellow-ball.png", "purple-ball.png" } local balls = {} for i = 1,#imageCache do balls[i] = display.newImageRect( imageGroup, imageCache[i], 128, 128 ) balls[i].x = display.contentCenterX balls[i].y = display.contentCenterY balls[i].isVisible = false end imageGroup.isShowing = 1 balls[imageGroup.isShowing].isVisible = true
Now, to manage the swapping, we can execute the following code, setting the local variable showingImage
to the index of the image we want to show. In this case, the value of 4
will show the yellow-ball.png
imageCache
array.
local showingImage = 4 for i = 1,imageGroup.numChildren do if ( i == showingImage ) then imageGroup[i].isVisible = true else imageGroup[i].isVisible = false end end
While the above methods are all perfectly valid, using individual image files is not the best use of memory and loading time. Also, creating/storing a large amount of individual files in your project directory can get chaotic. This is why we encourage the use of image sheets in which you load a single image sheet/atlas containing all of the individual images and then display a specific frame from it. Image sheets take a little more initial setup but the the benefits are well worth it.
Converting the above code to an image sheet may look like this:
local imageGroup = display.newGroup() local sheetOptions = { width = 128, height = 128, numFrames = 5, sheetContentWidth = 640, sheetContentHeight = 128 } local ballSheet = graphics.newImageSheet( "ballSheet.png", sheetOptions ) local balls = {} for i = 1,sheetOptions.numFrames do balls[i] = display.newImageRect( imageGroup, ballSheet, i, 128, 128 ) balls[i].x = display.contentCenterX balls[i].y = display.contentCenterY balls[i].isVisible = false end imageGroup.isShowing = 2 balls[imageGroup.isShowing].isVisible = true
From a code perspective, this is not much different from the array method above, other than the efficiencies gained from using image sheets — but image sheets are a gateway to sprites, an excellent way swap images!
Solar2D includes a comprehensive sprite engine. While the term “sprite” may seem to indicate only an animated object in a game, you should consider it as simply an ordered sequence of images which can be used for multiple purposes, including swapping images.
Let’s look at a
local sheetOptions = { width = 128, height = 128, numFrames = 5, sheetContentWidth = 640, sheetContentHeight = 128 } local ballSheet = graphics.newImageSheet( "ballSheet.png", sheetOptions ) local ball = display.newSprite( ballSheet, { name="balls", start=1, count=sheetOptions.numFrames } ) ball:setFrame( 2 ) ball.x = display.contentCenterX ball.y = display.contentCenterY
Like the version using graphics fills, we no longer need the display group (imageGroup
) since the sprite is, by definition, a
As you can see, there are various approaches and methods to the “swap images” concept and it depends on your needs and design specifics as to which method is most suitable. From individual images to fills to sprites, Solar2D has you covered!