transition2: A customizable extension to the transition library

Added shading to zRotate(). Just set shading=true to apply default shading like this:

transition.zRotate(iceTrapIcon, { degrees = 360, iterations = 0, time = 2000, horizontal = false, transition = easing.inOutSine, reverse = true, transitionReverse = easing.inOutQuad, onRepeat = function(target, params) params.horizontal = not params.horizontal end, shading = true, })

Applied to my Ice Trap icon it looks like below. The weird tracks under the image are just a result of the GifCam recording.

o4czQzw.gif

If you want to adjust darkness/brightness of the shading you can do that too.

transition.zRotate(iceTrapIcon, { degrees = 360, iterations = 0, time = 2000, horizontal = false, transition = easing.inOutSine, reverse = true, transitionReverse = easing.inOutQuad, onRepeat = function(target, params) params.horizontal = not params.horizontal end, shading = true, shadingDarknessIntensity = 0.75, -- Value between 0-1. Default = 1. shadingBrightnessIntensity = 0.5, -- Value between 0-1. Default = 0. })

MC38qaW.gif

All very cool but have you bench-marked it yet?  I had to remove transitions in my game because they were just too slow and pre-calculate everything and animate using enterFrame(). 

Oh and definitely add support for delta = true (I appreciate this won’t work for all transitions). 

Nope, haven’t benchmarked anything yet and haven’t paid any attention to optimization either. Feel free to do it. :slight_smile:

I agree that transitions tend be too slow if you have many objects moving in your game, and just like you I don’t use transitions for everything in my game because of that. But I also think there are many occasions where you only need to animate a few objects and don’t really care that much about performance.

delta = true should already be implemented. Doesn’t it work for you?

EDIT:  Sorry, was a bit quick on the delta thing. It’s been implemented for to() and from(), but not for the other functions. Will review them to see where it makes sense to add delta support. Thanks.

Thanks Markus!

Wow, looks great!

New convenience function fallingLeaf() just added. Example usage:

for i = 1, 100 do local leaf = display.newImageRect("leaf-64px.png", 64, 64) leaf.x, leaf.y = math.random(0, display.contentWidth), -50 leaf.rotation = math.random(0, 360) local colors = { {1, 1, 0, 1}, {1, 0.6, 0, 1}, } leaf:setFillColor(unpack(colors[math.random(1,#colors)])) transition.fallingLeaf(leaf, { delay = math.random(0, 5000), speed = 0.35, verticalIntensity = 0.4, horizontalIntensity = 0.5, rotationIntensity = 0.25, horizontalDirection = "random", randomness = 0.75, zRotateParams = { shadingDarknessIntensity = 0.5, shadingBrightnessIntensity = 0.25, }, cancelWhen = function() return (not leaf.y) or (leaf.y \> display.contentHeight) end, onCancel = function(target) transition.fadeOut(target, { time = 1000, onComplete = function(target) target:removeSelf() end }) end, }) end

38kLNQ4.gif

Looks so realistic…

I haven’t tried to solve the issue yet, but I suspect the leaf routine has a memory leak. 

When I add the following code… 

local monitorMem = function() collectgarbage("collect") print( "\nMemUsage: " .. collectgarbage("count") ) local textMem = system.getInfo( "textureMemoryUsed" ) / 1000000 print( "TexMem: " .. textMem ) end local memTimer = timer.performWithDelay(1000, monitorMem, -1)

I add this to the bottom of the code, and when I run the leaf function, it rapidly increases memory. When I comment out the leaf function it does not increase texture memory.

Jan 08 09:57:38.397: TexMem: 69.949348

Jan 08 09:57:39.404: ** MemUsage: 538.185546875**

Jan 08 09:57:39.405: TexMem: 69.949348

Jan 08 09:57:40.412: ** MemUsage: 561.837890625**

… after a couple of minutes…

Jan 08 09:59:13.163: TexMem: 69.948804

Jan 08 09:59:14.172: ** MemUsage: 15183.090820312**

Jan 08 09:59:14.172: TexMem: 69.949348

Jan 08 09:59:15.180: ** MemUsage: 15365.926757812**

 

I’m using the routine you posted here, although I’m using 5 loops, not 100. I also have a 

timer.performWithDelay( 1000, leaves ,-1 ) So yes, I am calling it every second, but to drop 5 leaves. With the life of the leaf, it does remove itself. So memory should level out.

The basic issue is that it runs fine on Apple TV, but after about an hour, the app silently quits. I suspect its a memory leak. If I took out the leaf routine, it didn’t quit.

I’ll see if I can find anything, but thought I’d mention it in case you had a better idea of where to look.

If there area any thoughts, let me know :slight_smile:

New convenience function fallingLeaf() just added. Example usage:

Thanks for reporting this! I don’t have time to investigate right now, but I’ll take notes and look in to it later. Let me know if you find out anything more.

I’ve dug into the memory leak problem now and found a nasty little bug that prevented cancelled transitions from getting cleaned up properly. Reason: I forgot to actually call the removeTransition() function… :wacko: This affected all transitions, not only fallingLeaf().

The fix for this silly mistake is a simple one-liner which has been pushed to the master branch of the Git repo. But if you don’t want to pull a new version you can just make the following change yourself in transition2-main.lua (at the bottom):

local function cleanUpTransition(transitionRef) if (transitionRef) then -- Immediately flag transition as cancelled so that it won't be processed anymore by the enter frame listner transitionRef.isCancelled = true -- Then wait for lock to be available before actually doing the cleaning up local removeTransition removeTransition = function() if (not locked) then locked = true -- Unset cross reference from target-\>transitionRef if (transitionRef.target and transitionRef.target.transitionRefs) then transitionRef.target.transitionRefs[transitionRef] = nil end -- Unset reference in table indexed by tag if (transitionsByTag[transitionRef.tag]) then transitionsByTag[transitionRef.tag][transitionRef] = nil end -- Note! Removal from the transitions array is done by the enter frame listener when it traverses the array and finds cancelled transitions -- Can't do it here without looping through the array which would be inefficient. locked = false else -- Try again shortly... timer.performWithDelay(20, removeTransition) end end -- ADD THIS LINE! -- removeTransition() end end

I made a small adjustment to zRotate which I think would be beneficial to add to the library. I wanted to be able to rotate an object 180 degrees, and then at some later point rotate back from 180 to 0 degrees. However the zRotate function always starts the rotation at 0. So I changed:

getStartValue = function(target, params) return 0 end

to this:

getStartValue = function(target, params) return params.startDegrees or 0 end

This way I can pass in an extra parameter specifying the rotation to start at, as well as the rotation to transition towards:

transition.zRotate(myObj, { startDegrees = 0, degrees = 180, iterations = 1, time = 1000, transition = easing.inOutSine, reverse = false, })

and then back again:

transition.zRotate(myObj, { startDegrees = 180, degrees = 0, iterations = 1, time = 1000, transition = easing.inOutSine, reverse = false, })

I realise that the transition already has a “reverse” parameter, but afaik that can only be triggered automatically after the initial rotation whereas by using the startDegrees param I can trigger it whenever I want.

@Markus - Beautiful!  I’m excited to try this.

Thanks a lot for your input! I’ve implemented your suggested param startDegrees in transition2 now, but with some modifications.

What I’ve done is:

  • Always save the current “zRotation” degrees on the target object being rotated, to be able to access it through target.zRotation just as with any other display object params.
  • params.degrees will always be considered a delta value instead of an absolute value. If the object being rotated already has a zRotation value, the rotation will start from that angle. If not, it will default to start rotating from a 0 value, just like it was hard-coded before.
  • The param startDegrees can be used to override the saved zRotation value.

So in your case, it should be possible to get the same result by skipping startDegrees completely and instead reversing the value of params.degrees to -180 when rotating back.

Like this:

transition.zRotate(myObj, { degrees = 180, iterations = 1, time = 1000, transition = easing.inOutSine, reverse = false, }) -- Some time later... transition.zRotate(myObj, { degrees = -180, iterations = 1, time = 1000, transition = easing.inOutSine, reverse = false, })

Just let me know if you see any problems with this and I’ll do my best to find another solution.

@Markus

That’s great! I looked for the quickest workaround I could find, but this is even better.

+1

Added an onValue  callback function that can be used with any transitions. May come in handy if context or target object needs to be updated depending on transition state.

Example use with the color transition:

transition.color(displayObject, { startColor = {0, 0, 0, 1}, endColor = {1, 1, 1, 1}, time = 500, reverse = true, iterations = 0, onValue = function(target, value) local R,G,B = value[1], value[2], value[3] if (R \> 0.8 and G \> 0.8 and B \> 0.8) then print("This color is pretty bright!") end end, })

Also made use of the onValue function in zRotate by implementing a convenience param hideBackside.Setting this to true will hide the target object when rotated away from the display. This makes it easy to combine two separate objects and make them appear as a single two-sided object while rotating. Like this example with a coin from Ice Trap:

local coinFront = display.newImageRect("coin-front.png", 29, 29) local coinBack = display.newImageRect("coin-back.png", 29, 29) coinBack.isVisible = false params = { degrees = -540, iterations = 0, time = 2000, horizontal = true, transition = easing.inOutSine, reverse = true, transitionReverse = easing.inOutQuad, shading = true, shadingDarknessIntensity = 0.75, shadingBrightnessIntensity = 0.25, hideBackside = true, } transition.zRotate(coinFront, params) -- Use the same params, but start with the backside rotated away from the display params.startDegrees = 180 transition.zRotate(coinBack, params)

p7HumsR.gif

Hi, I’m having difficulty getting my head round a concept that I’m trying to implement and was wondering if anyone could help.

I have an object that I want to move backwards and forwards across the screen.

Say the object starts at x=20 and moves to a point somewhere to the right, once it gets there I would like it to move back to a random point on the left followed by a random point to the right etc.

I knowhow to set a new destination using the onRepeat and recalculateOnIteration functions but can’t figure out how to make sure the new destination is in the correct direction.

​Then, just when it was complicated enough, I’d like the object to be moving at a constant speed which I guess could be calculated using the following function that I found on the forums from a few years back…

local function distanceBetween( point1, point2 ) local xfactor = point2.x-point1.x local yfactor = point2.y-point1.y local distanceBetween = math.sqrt((xfactor\*xfactor) + (yfactor\*yfactor)) return distanceBetween end local speed = 0.1 times = 1 \* distanceBetween(A,B) / speed transition.to(A, {time=times,x=B.x, y=B.y})

I hope all of this makes some kind of sense :slight_smile:

If I understand your question correctly, I believe that you can solve this a lot easier easier without using iterations, with a single transition function calling itself onComplete. Using recalculateOnIteration in combination with for example onRepeat should work for changing direction and target position. But, because you need the object to move at constant speed this solution won’t work since it’s not possible to change the transition time between iterations.

Here’s an example with a single transition function calling itself and setting a property on the target object to keep track of which direction it is currently moving. Just dry coding from memory here without test running anything, but hopefully you’ll get the picture.  :slight_smile:

-- Declare the move variable before initializing it to be able to reference it from onComplete local move move = function(target) -- Store direction on target object, and alternate between left/right target.direction = (target.direction == "right") and "left" or "right" -- Then calculate new target position... local newTargetX = calculateNewTargetX(target.x, target.direction) -- ...and also calculate time for new transition now that you know the distance the object will travel at a constant speed. local timeMs = calculateTimeForConstantSpeed(target.x, newTargetX) transition.to(target, { x = newTargetX, time = timeMs, onComplete = move }) end move(yourObject) 

That looks excellent. Many thanks for your detailed reply.
I’m off out to the cinema shortly to see Ready Player One so I’ll try that out when I get back :slight_smile:

Hi Markus, I am currently getting this error when using your transition library:

Module 'transition2lib.transition2-main' not found: no field package.preload['transition2lib.transition2-main'] no file '/Users/0278046/Library/Application Support/Corona/Simulator/Plugins/transition2lib/transition2-main.lua' no file '/Users/0278046/Desktop/Corona SDK/Corona Projects/Space Shooter/transition2lib/transition2-main.lua' no file '/Users/0278046/Desktop/Corona SDK/Corona Simulator.app/Contents/Resources/transition2lib/transition2-main.lua' no file './transition2lib/transition2-main.lua' no file './transition2lib/transition2lib/transition2-main.lua' no file '/Users/0278046/Library/Application Support/Corona/Simulator/Plugins/transition2lib/transition2-main.dylib' no file './transition2lib/transition2-main.dylib' no file '/Users/0278046/Desktop/Corona SDK/Corona Simulator.app/Contents/Resources/transition2lib/transition2-main.dylib' no file '/Users/0278046/Library/Application Support/Corona/Simulator/Plugins/transition2lib.dylib' no file './transition2lib.dylib' no file '/Users/0278046/Dmodule 'transition2lib.transition2-main' not found

And the stack traceback points to this line:

local createTransition2 = require("transition2lib.transition2-main")

What should be my course of action?