[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Frantic (again)



David Mikesell wrote:
[snip snip]
The only thing I could see taking up that much memory were if you calculated the rotations
of all of your sprites through 360 degrees and stored them in a huge
array.
Are you doing that?
This would also make sense for the long load time at startup.
It would be better memory-usage wise to just rotate the sprites at
runtime.

Not 360 frames for each. The starship has 72, the enemies have 18 in
most (if not all) cases. I can't rotate the enemies in real time as the
sprites are modeled and prerendered in 3D. This is pygame sprite
rendered, not OpenGL rendered. Am I missing something basic here?
The enemies are rotated as in their sprite has multiple images that represent different physical views of the same 3d object.
I meant rotated as in the pygame surface itself is transformed. for example, when you hold down "left" during the game and the ship spins around.
I was wondering if you stored each of the ship's rotations (facing up, facing left, facing right, facing 45 degrees, 30 degrees, and so on)
for each of the frames of your sprite.
Say this array is an image (imagine :P)
[1,2,3,4,5,6,7,8,9,10]
now say I rotate it 3 elements to the right.
[8,9,10,1,2,3,4,5,6,7]
now I know how to rotate the image in this way (just shift the elements over a couple of places and put the elements that fall off the edge back to the other side.)
So there is no reason for me to store every single iteration of this rotation,
[[1,2,3,4,5,6,7,8,9,10],[10,1,2,3,4,5,6,7,8,9],[9,10,1,2,3,4,5,6,7,8],[8,9,10,1,2,3,4,5,6,7] ....... ] all in memory
because I can just store the source array [1,2,3,4,5,6,7,8,9,10] and manipulate it later if I need to rotate it by
doing
for x in range(len(array) % rotationamount):
tmp = array.pop(len(array)-1)
array = [tmp]+array


and by this method I will save 10x the memory and I'll just have to use a little CPU power to rotate the array whenever I need a rotated version.

Now say instead of my array of numbers I have an image.
let's say it's a starship.
>>> starship = pygame.image.load("images/starship.png").convert_alpha()
It's not an animated sprite, it's just a single frame for this example.
now one  may be tempted to say

starshiprotation = []
for degrees in range(360):
   starshiprotation.append(pygame.transform.rotate(starship,degrees))

Now this kicks ass for efficiency later on. one can just say...
screen.blit(starshiprotation[270],(0,0))
to display a starship that's facing left, and the only time-delay is (ignoring things like the lookup that occurs when you use a "." and such)
1. retrieve (reference to) image from starshiprotation array -> should be negligible
2. pass the (reference of the) image to the screen.blit() function -> should be negligible as well
3. wait for pygame to blit the image to the screen. -> depends on resolution, image alpha, blah blah... takes a while.
So the only thing we're waiting on is the blit function. This is good and fast.


However, what you have to remember is that
1. now we're taking up 360x as much memory as just holding the single image,
2. we have to wait a long time while python number-crunches those images and rotates each of them.


The alternative to this is to say...
screen.blit(pygame.transform.rotate(starship,270),(0,0))
to have an image facing left.

The disadvantages of this are
1. Each time we need to display this image we have to wait for the rotation to occur.


The advantages are
1. Only need to allocate enough memory to hold the single starship image.
2. No overhead in the beginning generating the rotated array.

To soften the blow of the disadvantage, having to rotate the image each time we need to blit it,
we can just whip up a simple class to take care of it for us!


class StarShip(object):
   def __init__(self,imgpath):
      self.original = pygame.image.load(self.imgpath)
      self.currentdegrees = 0
      self.starship = self.original

   def rotate(self,degrees):
      if degrees == self.currentdegrees:
         return self.starship
      self.starship = pygame.transform.rotate((self.starship,degrees))
      self.currentdegrees = degrees
      return self.starship

#StarshipTest.py
--- import modules, initialize them, get screen surface.
ship = StarShip("images/starship.png")

rotations = [0,45,45,45,45,90,180,270]
for x in rotations:
   screen.blit(ship.rotate(x),(x,x))
   pygame.display.update()

during the calls to ship.rotate() where the previous value is used (during the repeated 45 degree rotations)
no cpu overhead will happen for the rotation (other than calling functions, returning references, negligible things)
As an added bonus, the ship will move diagonally down the screen!
Anyway, as you can see, rotating images only when they're necessary is much more memory- and loading-time efficient.
So My original question was which of these ways you were doing it.


I hope that I didn't blabber on too much about stuff you already knew and that
my explanations were clear.
Anyway, I think I fixed the loading bug with the very latest release.
Cool. That's good.
[snip] Felt it would be too easy.
I see your point. The ship has much more maneuverability seeing as it can stop on a dime more or less, so some added restrictions
should be placed to keep it from being too easy.


If it completely flops, I'll release the source :)
I hope I don't see the source then! :)
Thanks for the feedback, Luke.
Anytime :D
Dave
Luke