Android Question Simple animation (my approach seems inefficient)

ydobemos

Member
Licensed User
Longtime User
Hello! I have been working on my first App and so far B4A is awesome. However I suspect there are still many things I need to learn about it and the way things work on Android.

My current task involves having a pulsating red orb on a black background. I use the canvas draw functions and a timer to achieve this. However it's really slow. I'm not sure if it's correct to Initialize canvas on every timer tick - but if I don't nothing shows up. Also drawing a background colored circle over the previous one to imitate animation seems kinda awkward.

Hopefully someone can give me a few pointers on how to do this right (preferably using native code as the animation will have to become very slightly more complex (changing color and so on) as further on). Ideally this should run at at least 20 frames per second.

B4X:
#Region  Activity Attributes
    #FullScreen: True
    #IncludeTitle: False
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
   
    Dim Timer1 As Timer

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.

    Dim Drawhere As Canvas
    Dim RadiusRatio As Double : RadiusRatio = 0.1
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("Draw")
    Drawhere.Initialize(Activity)
    Timer1.Initialize("Timer1",25)
    Timer1.Enabled = True
   
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Timer1_Tick
    Drawhere.Initialize(Activity)
    Drawhere.DrawCircle(Activity.Width/2,Activity.Height/2,Activity.Width/2*Sin(RadiusRatio),Colors.Black,True,2)   
    RadiusRatio = RadiusRatio +0.01
    Drawhere.DrawCircle(Activity.Width/2,Activity.Height/2,Activity.Width/2*Sin(RadiusRatio),Colors.Red,True,2)   
End Sub
 

sorex

Expert
Licensed User
Longtime User
put the drawhere.initialize in the on create and use it like this, it will be a lot faster.

B4X:
Sub Timer1_Tick
Drawhere.DrawCircle(Activity.Width/2,Activity.Height/2,Activity.Width/2*Sin(RadiusRatio),Colors.Black,True,2)
    RadiusRatio = RadiusRatio +0.01
    Drawhere.DrawCircle(Activity.Width/2,Activity.Height/2,Activity.Width/2*Sin(RadiusRatio),Colors.Red,True,2)
Drawhere.Invalidate  // or invalidate()
End Sub

oh and add a rectangle fill before the circle draw to erase the old shapes.
 
Upvote 0

ydobemos

Member
Licensed User
Longtime User
Well, obviously appetite grows when you eat... What would be the best approach to ensure smooth 60 frames per second of animation? While on a HD phone the canvas method works quite well on a 2048x1536 tablet it is a little laggy.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
you can either invalidate only the part of the image that chages or you can look for an accelerated method but that only works from 2.3 I think.
 
Upvote 0

ydobemos

Member
Licensed User
Longtime User
The thing is that circle gets rather big - as the example shows - full width of the Activity at portrait. I'd also like to smoothly animate colors at full screen (the app I'm trying to make has to deal with using the phone/tablet as a dynamic light source).
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
why don't you draw it once at full size and just resize it for the smaller ones?
 
Upvote 0

ydobemos

Member
Licensed User
Longtime User
According to the idea it should also change color over time. So it would be preferable to draw it every frame.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
never tried the accelerated methods so I don't know if they are fast enough to do this.
 
Upvote 0

ydobemos

Member
Licensed User
Longtime User
I'll probably have to look into some game library or such to use accelerated graphics features. Would prefer to keep everything very slim and library/permission free, but if the goal demands it...

Thank you again for the help so far!
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
there are most of the time ways of cheating to make it faster but I don't know what effect you're trying to do.

maybe you should post a screenshot.
 
Upvote 0

ydobemos

Member
Licensed User
Longtime User
Alright, here's a demonstration of what it should look like (without the text):
Pld8shy.jpg


In the app we can set a starting size and color, and ending size and color and total time to reach it. Then the app smoothly animates this circle (or a rectangle or just the background color) over time.

The point of this is as a simple toy to use with long exposure photography - you can paint nice, smooth light trails with flowing colors. Here's a very rough example with the current test (which only has size and no color yet):
4BB8QDL.jpg


If you look at the bottom left light trail which is closest to us you can see a slight "staircase" effect - this is due to the animation not keeping up. There's also the fact of the phone screen backlight shining through, but that's more of a physical problem than software.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
oh!

it's not that the color is changing randomly?

well as I said you could cheat :)

but it might take too much memory so beware.

People tend to see things at 24fps I believe. if you tweak it to 25 you have a nice number.

create 25 bitmaps and plot your circle and color based on the loop counter in it.

in your timer running at 4ms you just alternate the images from 0 > 25 > 0

you could use a bitmap that 1/4th of the canvas size that you resize again, might be a bit less smooth but eat way less memory.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
in fact you don't need canvas size bitmaps, just the size of the bounding box of the circle so you gain even more memory.

you just need to center the canvas on the screen and not do the plotting to the center of the oversized canvas.
 
Upvote 0

ydobemos

Member
Licensed User
Longtime User
Well, in theory the animation wouldn't just be limited to 1 second, it might be LONG and with different variations (like pulsing, etc).

I do like the sound of a lower resolution canvas - this is perfectly acceptable as there's no need for the 300+ dpi of the current devices. Even 20 dpi would look ok in most cases.

I think there's a way to combine both - how do I, for example, just constantly draw on a 64x64px canvas or such and then blow up the canvas to the currently needed size?
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
your canvas is attached to an imageview, you can resize that from 64 to width=100%x & height=width
 
Upvote 0

ydobemos

Member
Licensed User
Longtime User
So make a small imageview, attach canvas to that. Then every tick draw on there, resize it, call the Activity invalidate function and resize it back? (Maybe I'm starting to get this, who knows? :))
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
yes, I did it before the only issue that appeared was that you needed to resize it back to 60x60 before you draw again otherwise it overstretched or the plot location was not right (in my case)
 
Upvote 0

ydobemos

Member
Licensed User
Longtime User
Thanks again, sorex, I think this should be enough for most uses:
PGKzlYS.gif


I implemented a "Quality" variable which changes the size of the canvas so it's easy to adjust smoothness vs looks. Now to just build the rest of it...
 
Upvote 0
Top