In both case the
Instantiate() method is going to return a new copy of the original object. I believe in the case of the
PuffSmoke() method, the smokePuff variable will become a pointer on the stack to the same newly created object on the heap.
So I don’t believe there is really any difference internally.
One approach which may make a difference, albeit somewhat small depending on the number of bricks and the device in question, to the use of memory here would be to not keep instantiating and destroying the game objects actually. Instead, create a pool of instantiated smoke objects and then re-use them.
Depending on your game you would need to give some consideration to the size of the pool, for example, the number of required smoke objects may depend on the speed of the ball, the number of hits required per brick and whether or not you have added any form of power-ups which allow for the bricks to be destroyed more easily.
A slow moving ball, even with a more rapid ricochet perhaps near the top/side of the screen may only require a small pool. Once each smoke object has completed its particle effect run, it could be set as inactive and then reset the next time it is required.
A fast moving ball, destroying bricks rapidly would potentially need a greater sized pool as you would have more smoke objects in play at the same time.
If you have 200 bricks on the level, with either of the above approaches, 200 smoke objects will be instantiated and destroyed. I would imagine, unless you have a “nuke” type power-up, you would never have more than half of these bricks being destroyed in the scene at any one time, probably not even a quarter. So the size of the pool could be set to 25% of the number of bricks for example. 75% saving on over head of instantiating/destroying - allocating/de-allocating memory.
Just something to consider