I have created a sprite sheet which plays continuously,
local sheet3 = sprite.newSpriteSheet( "sample.png",400,317)
local spriteSet3 = sprite.newSpriteSet(sheet3, 1, 8)
sprite.add( spriteSet3, "puma", 1, 8, 1000, 0 ) -- play 8 frames every 1000 ms
local instance3 = sprite.newSprite( spriteSet3 )
instance3.x = 2* display.contentWidth / 4 + 30
instance3.y = baseline - 5
instance3.xScale = .5
instance3.yScale = .5
instance3:prepare("puma")
instance3:play()
As we know spritesheet shows image sequence in loop. I want to stop play of image sequence after it completes one loop.
Anybody know how can i do this? or at least provide me any link to help me out to solve this problem?
When you define the animation in the first place you set whether or not to loop:
http://developer.anscamobile.com/reference/index/spriteadd
Alternatively if you need to adjust the animation programmatically (ie. loop until the player does something) then you can set an event listener and call pause() when the loop event happens:
http://developer.anscamobile.com/reference/index/spriteinstanceaddeventlistener
Related
I'm doing my first game with Lua in college and I'm having a hard time part.
My char shoots arrows non-stop and I want it to have a delay to shot each arrow.
I tried to create functions to simulate a delay but it did not work
local function atkRight()
system.setTapDelay(10)
display.remove(char)
char = display.newImageRect ( "Sprites/archerRight.png", 50, 60)
char.x = display.contentCenterX
char.y = display.contentCenterY+50
physics.addBody (char, "static", { isSensor=false })
char.myName = "char"
local arrowRight = display.newImageRect ( "Sprites/arrowRight.png", 50, 5)
arrowRight.x = display.contentCenterX+40
arrowRight.y = display.contentCenterY+40
physics.addBody (arrowRight, "dynamic", { bounce = 0 })
arrowRight:setLinearVelocity(500, 0)
arrowRight.gravityScale = 0
arrowRight.myName = "arrowRight"
end
atkiconRight:addEventListener( "tap", atkRight )
I wish this attack function could only be executed every 0.5 seconds
There are various ways to achieve this. The simplest way is probably to have your event callback check the time.
https://docs.coronalabs.com/api/library/system/getTimer.html
Store the time a shot was fired in a global variable.
When a shot is fired and there is a timestamp of a preceeding shot, check and only shoot if it is at least 0.5 seconds later.
Another way would be to remove the event listener and start a timer event that will re-add the event listener after 500ms. Or you have a global flag that prevents shooting and have a timer reset this flag every 500ms.
Which way to go is up to you.
Problem: I have a long duration frame-based animation for which i have created many spritesheets (e.g. 50 spritesheets with 10 images in each - 2 MB per spritesheet). It takes noticeable time (and memory) to pre-load all 50 spritesheets before the animation can be played using code based on the example code snippet below (this is suggested in the corona docs):
local function spriteListener( event )
thisSprite = event.target -- "event.target" references the sprite
if ( event.phase == "ended" ) then
sequenceNumer = sequenceNumer + 1;
if (sequenceNumer <=10) then
thisSprite:setSequence(filenameArray[sequenceNumer])
thisSprite:play()
end
end
end
local spriteWidth = 551
local spriteHeight = 401
local numFrames1 = 15
local startFrame = 1
frameInfoSet1 = {width = spriteWidth , height = spriteHeight, numFrames = numFrames1}
filenameArray = {"001-015","016-030","031-045","046-060","061-075","076-090","091-105","106-120","121-135","136-150"}
fullSequence =
{
{name=filenameArray[1], sheet=graphics.newImageSheet("spritesheets/" .. filenameArray[1] .. ".png", frameInfoSet1), start = startFrame, count=numFrames1, time=2250, loopCount=1},
{name=filenameArray[2], sheet=graphics.newImageSheet("spritesheets/" .. filenameArray[2] .. ".png", frameInfoSet1), start = startFrame, count=numFrames1, time=2250, loopCount=1},
{name=filenameArray[3], sheet=graphics.newImageSheet("spritesheets/" .. filenameArray[3] .. ".png", frameInfoSet1), start = startFrame, count=numFrames1, time=2250, loopCount=1},
-- more such spritesheets are loaded further...
}
firstSpriteSheet = fullSequence[1]["sheet"]
sequenceNumer = 1
tt = display.newSprite (firstSpriteSheet, fullSequence)
tt.x = display.contentWidth/2 ; tt.y = display.contentHeight/2
tt:addEventListener( "sprite", spriteListener )
tt:play()
However, what i would like to achieve is a "streaming" version of this technique. i.e. I will load only 3 spritesheets out of the total of 50, therefore requiring say 6 MB to start playing the animation and keep loading additional spritesheets as the already loaded ones keep playing. So, to illustrate further, i load sheets 1,2,3 and start playing the animation and load sheet 4 by the time sheet 1 is finished, load sheet 5 by the time sheet 2 is finished and so on.
Any advice is appreciated ! Thanks in advance.
I see this is old question, but I will add thoughts from my expirience:
This is not an easy task, but it's possible
Preload sheets before you go
I suggest to add simple loading scene, where you will show progress bar while loading.
local filenameArray = ... -- this is from your code
local preloadedSheets = {}
local function enterFrameLoading()
local nextSheetToLoad = #preloadedSheets + 1
if (nextSheetToLoad > #filenameArray) then
Runtime:removeEventListener("enterFrame", enterFrameLoading)
-- go to game scene if you are using scenes
return
end
preloadedSheets[nextSheetToLoad] =
graphics.newImageSheet("spritesheets/" .. filenameArray[nextSheetToLoad] .. ".png", ??)
updateProgress(nextSheetToLoad) -- this function will update UI, i.e. progress bar, text, etc
end
Runtime:addEventListener("enterFrame", enterFrameLoading)
After preloading all sheets will be in memory, so you shouldn't have performance issues
Use multiple sprite objects, created on demand
You can create new objects inside spriteListener - again, store all sprites inside a table and use .isVisible = false to hide old sprites
Combine solutions 1 and 2
You can create sprite objects with just 3 sequences. Then preload other sheets in enterFrame listener like I showed above.
When preloading is finished you want to destroy old sprite and create new with the same sequence and frame
Let me know if you need more information on this
I'm using Corona SDK and currently I have this. When you tap on the image the number increases. I was wonder how would I change the image once it reached a certain amount of clicks?
display.setStatusBar(display.HiddenStatusBar)
local newButton = display.newImage ("button.png",0,0)
newButton.x = display.contentWidth - 60
newButton.y = display.contentHeight - 62.5
local number = 0
local textField = display.newText(number, 30, 30, native.systemFont, 25)
local function moveButtonRandom(event)
number = number + 1
textField:removeSelf()
textField = display.newText(number, 30, 30, native.systemFont, 25)
end
newButton:addEventListener("tap", moveButtonRandom)
The best practice would be to use a sprite and when you increment to a certain point, tell the sprite to play the next frame.
There are multiple tutorial on working with imageSheets and sprites here: http://coronalabs.com/resources/tutorials/images-audio-video-animation/
Rob
You can make an if then decision statement in your function and use number as argument. When statement true switch the pictures.
Example: if number >= 3 then
--[[after 3 clicks Remove existing picture and add new picture code executes here--]]
End
Good luck
For example if you want an image to show up when you get to 15 clicks, you can modify your tap function to look like this, so that the number gets replaced by an image:
local function moveButtonRandom(event)
number = number + 1
-- Here goes our if check
if number >= 15 then
-- The number of clicks is 15 or more, replace the numbers with an image
textField:removeSelf()
textField = display.newImage("yourImage.png")
else
-- Do things the 'normal' way
textField:removeSelf()
textField = display.newText(number, 30, 30, native.systemFont, 25)
end
end
If you dont want the image to replace the text, then create a new local variable at the top of your file, and assign it inside the if check
I'm trying to have a different amount of force applied to an object when the screen is tapped a different amount of times ( only once and twice ).
I'm not sure what I'm doing wrong. Here is the code:
local function moveUp(event)
if event.numTaps > 1 then
jumper:applyForce( 0, 250, jumper.x, jumper.y )
elseif event.numTaps < 1 then
jumper:applyForce( 0, 0, jumper.x, jumper.y )
else
jumper:applyForce( 0, 200, jumper.x, jumper.y )
end
end
-- start game
createPlayScreen( )
system.setTapDelay( 2 )
Runtime:addEventListener("tap", moveUp)
I've tried moving the Runtime:addEventListener into the function. I've also tried have the event.numTaps == 2 and event.numTaps == 1, but to no avail.
The issue is that the TapDelay refuses to wait for the second tap.
Any and all help is greatly appreciated
P.S. I have the seconds set to two for testing purposes, but once I find that this works, I will be lowering the time to like 0.3 or something
There are several issues with the strategy you are using. For one, it is not scalable (to more than two taps). But ok, maybe you are 100% sure you will never ever need more than 2 taps. The next problem is that event.numTaps can only be 1 or 2, yet your listener tests for < 1! This can never happen. The next problem is that when you tap twice, at least in the simulator (did not test on device) you get two events: one for the first tap with numTaps = 1, and another for 2nd tap with numTaps = 2. In other words, the Corona engine does not wait before emitting a one-tap event to know if a second tap event occurs within some time range. So you get two tap events for a two-tap event, there is no way of knowing in the handler whether you should "wait" to see if another tap might occur within an allowable delay to constitute a "two-tap" event instead.
What you will have to do is create your own N-tap event generator. Whenever a tap occurs, check if your timer has been started. If so, increase the tap count and reset the timer. If not, start the timer that expires some short delay later. If no other tap occurs in that delay, the count you have saved is your tap number. Reset the counter if your timer expires. I have created some functions that do this and I have put them all in a table "object":
local tapEvents = {
measureInterTapTime = false, -- set to true to measure how fast you can tap!
onTapHandler = nil, -- set this to your handler
-- implementation details
tapTimer = nil,
tapCounter = 0,
tapEventTime = 0,
doneTap = function(self, event)
self.tapTimer = nil
if self.onTapHandler then
self.onTapHandler(self.tapCounter)
end
self.tapCounter = 0
self.tapEventTime = 0
end,
-- end implementation details
tap = function(self, event)
self.tapCounter = self.tapCounter + 1
if self.tapTimer ~= nil then
timer.cancel(self.tapTimer)
self.tapTimer = nil
end
local delayMS = 250
self.tapTimer = timer.performWithDelay(delayMS, function(e) self:doneTap(e) end, 1)
-- check how much time between taps, for interest:
if self.measureInterTapTime then
if self.tapEventTime ~= 0 then
local interTapTime = system.getTimer() - self.tapEventTime
print("Time (ms) between taps:", interTapTime)
end
self.tapEventTime = system.getTimer()
end
end,
}
tapEvents.onTapHandler = function(tapCounter)
print(tapCounter .. "-tap event")
end
-- because tapEvents contains a 'tap' function, will get called automatically with self:
Runtime:addEventListener('tap', tapEvents)
This captures N-tap events without limit!! I have included a flag you can set to true if you want to print out the ms delay between single taps so you can determine what the optimum delay should be (you don't want delay to be too short or you might inadvertently break an N tap into two smaller events; you don't want it to be too long either, or user will have to noticeably wait to indicate "end of my multitap").
Tap events must be added to a display object, not to the Runtime.
If you have a display object jumper for example, use:
jumper:addEventListener("tap", moveUp)
More documentation here: http://docs.coronalabs.com/api/event/tap/index.html
Hi I've been wondering this for a while and it's causing me lots of problems not knowing more about how to reference / specify all indexes of a spawned enemy on the screen at one time.
Say when my character dies I want all the enemies on the screen at that time to move away from my dead character as the screen fades out. Simply calling 'enemy1' only makes one (the last spawned I think) do as it's told.
Here is my enemy spawn script:
local spawnTable2 = {}
local function spawnEnemy()
enemy1 = display.newSprite( group, sheetE, sequenceData2 )
enemy1.x=math.random(100,1300)
enemy1.y=math.random(360,760)
enemy1.gravityScale = 0
enemy1:play()
enemy1.type="coin"
enemy1.objTable = spawnTable2
enemy1.index = #enemy1.objTable + 1
enemy1.myName = "enemy" .. enemy1.index
physics.addBody( enemy1, "kinematic",{ density = 0, friction = 0, bounce = 1 })
enemy1.isFixedRotation = true
enemy1.type = "enemy1"
enemy1.timer = nil
enemy1.enterFrame = moveEnemy
Runtime:addEventListener("enterFrame",enemy1)
enemy1.objTable[enemy1.index] = enemy1
hudGroup:toFront()
return enemy1
end
To reference all objects, you would need to have two things.
A Counter
Loop
First make a counter:
counter = 0
Every time the funciton is called have a counter:
counter = counter + 1
For tables, you would do something like this:
spawnTable2[counter].x=math.random(100,1300)
Then when you want to remove the object, you would just do this:
display.remove(spawnTable2[counter])
Just keep in mind, everything you do to manipulate that object will have to be inside that function. Good luck and hope this helps.