Sprites are best for general purpose stuff. For animation they are a must unless you are willing to hide multiple objects and only show one at a time.
If you have areas where a texture is repeated then a tiledbackground can be useful because it can render that as one object. If you need it animated that’s not what it’s for.
If the objects are laid out in a grid you can look at the tilemap object. Again it’s not animated but it is faster than rendering multiple sprites, and collision detection is a bit faster with it.
The rule of thumb is sprites are general purpose and the other objects are special purpose that are faster in some ways but less flexible.
Other objects that may be useful:
Spritesheet - lets you select a subrectangle of a texture to draw instead of the whole thing.
Paster - lets you draw objects to it so you don’t have to redraw it every frame normally.
Anyways if the game runs slow, the rendering of that many objects is the main bottleneck. You may just need to scale the amount of objects back.
For the logic you can do
Tree: myscale<1
—- tree: set myscale to min(1, self.myscale+dt)
Or whatever the variable name is.
The condition only updates the trees that aren’t full grown, and stops at 1. Is that what you were after?