[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Fwd: [pygame] 2 surfaces on a Sprite class]



You can overlay sprites nicely and move them around in a bunch, using
the group updates, or even deriving their posn from a 'master' sprite
for each group.

Attached code is for a gameboard operated by drag and drop, includes a
snap-to-square cursor.
SpritePieces is a utility for chopping up images, got idea from samepyg.
SpritePanelMouse moves the cursor, the snap to grid and the carried
piece, if there is one (.carrying)

If interested I can put this incomplete stuff with a demo game up
somewhere.


P

-------- Original Message --------
From: "alex" <alex@quad.com.ar>
Subject: [pygame] 2 surfaces on a Sprite class
To: <pygame-users@seul.org>

Hey there, I'm getting started with Sprites, I was wondering how could I
manage 2 or more surfaces on a single sprite class. I first tried to
make a
single sprite class for each surface, but each class must share a lot of
internal variables, so having them as a single class makes things
easier..
but then I realized that when myGroup.draw() is called, it will blit
self.image to the given surface (usually screen) at the self.rect
position,
so...

should I manually blit all class' surfaces into self.image before
calling
myGroup.draw()? or is there a better/clean way to do this?

any kind of suggestions are very welcome.


Alex


____________________________________
pygame mailing list
pygame-users@seul.org
http://pygame.seul.org
#spritepanels.py
#13feb02 PG separated from spygame
#08feb02 PG for mygamebox. sprite panels are a utility for game writers
#See http://www.mrexcessive.net/games/

OPTIONS = {}
pause = 0
screen = None
background = None
level = None

#ZZZ set these parameters in init call to SpritePanelMouse
GRID_square = None
GRID_offset = None
GRID_light_offset = None
GRID_center = None
GRID_number = None

try:
   import os,sys,getopt
   import random
   import pygame
   import pygame.draw
   import pygame.sprite
   from pygame.locals import *
except ImportError, err:
   print "couldn't load module. %s" % (err)
   sys.exit(2)


def load_image(name, colorkey=None):
   fullname = os.path.join('data',name)
   try:
      image = pygame.image.load(fullname)
   except pygame.error, message:
      print 'Cannot load image:', name
      raise SystemExit, message
   image = image.convert()
   if colorkey is not None:
      if colorkey is -1:
         colorkey = image.get_at((0,0))
      image.set_colorkey(colorkey, RLEACCEL)
   return image, image.get_rect()
   

class SpritePanelMouse(pygame.sprite.Sprite):
   def __init__(self,GRID_BITS = None):
      if GRID_BITS:
         self.GRID_square = GRID_BITS['square']
         self.GRID_offset = GRID_BITS['offset']
         self.GRID_light_offset = GRID_BITS['light_offset']
         self.GRID_center = GRID_BITS['center']
         self.GRID_number = GRID_BITS['number']
      pygame.sprite.Sprite.__init__(self)    #call Sprite initialiser
      self.original_image, self.original_rect = load_image('cursor.png',None)
      self.image_carrying,drop = load_image('cursor_carrying.png',None)
      self.image_carryingmany,drop = load_image('cursor_carryingmany.png',None)
      self.image_vanish,drop = load_image('cursor_vanish.png',None)
      (self.image,self.rect) = (self.original_image,self.original_rect)
      self.carrying = None
      self.pickedup = 0
      self.saveimage = None
      self.update()    # set original position based on mouse
      
   def update(self):
      "move the cursor based on the mouse position"
      self.pos = pygame.mouse.get_pos()
      self.rect.center = self.pos
      if self.carrying:    # YYY show carried sprite on top of mouse (XXX ZZZ hopefully!)
         self.carrying.rect.center = self.pos
      
   def mousein(self):
      if self.saveimage:
         self.image = self.saveimage
      else:
         self.image = self.original_image
   
   def mouseout(self):
      "we no longer have focus"
      self.saveimage = self.image
      self.image = self.image_vanish

   def mousedown(self,panel,flag_allow_reposition = 0):
      if panel.ishit(self.rect.center):
         flag_donedrop = 0
         oldcarrying = None
         if panel.flag_target_drop and self.carrying: # if something to drop
            if panel.gridsprite.visible:     # only drop if can
               #ZZZ replace or question on collision (something already there, not compatible)
               #ZZZ snap to grid
               if self.pickedup:
                  #YYY PG without .clone() just moves sprite from panel to board
                  #panel.sticksprite(self.lastcontrolhit.clone(),self.pos)    # drop the newthing
                  if panel.sticksprite(self.carrying,self.pos):    # try to drop piece here
                     flag_donedrop = 1
                     self.carrying.butup()
                     self.pickedup = 0
                     self.image = self.original_image
                     oldcarrying = self.carrying
                     self.carrying = None # forget which piece now it's dropped
         if (panel.flag_target_pick or flag_allow_reposition):    # allow pickup from board
            hits = pygame.sprite.spritecollide(self,panel,0)    # 0 => don't kill
            for hitwhat in hits:
               if not self.carrying:      #YYY only process until something picked up, so don't .butdown() on two and pick up one.
                  hitwhat.doyourthing()      # always do its thing (paintable ones just don't)
                  if hitwhat.strength >= 0:    # -ve => unpickable/moveable
                     if flag_donedrop and hitwhat == oldcarrying:  # if hit what I just dropped, ignore it.
                        pass
                     else:
                        hitwhat.butdown()
                        self.image = self.image_carrying
                        self.carrying = hitwhat
                        self.pickedup = 1
            
   def mouseup(self,panel):
      pass
      

class SpritePieces:
   "holds and provides access to an array of graphics"
   def __init__(self,gridsize,piecesfile='pieces'):    # gridsize is e.g. (10,10) for 100 squares
      pieces = None     # no pieces
      self.pieces,self.pieces_rect = load_image(piecesfile + '.png',None)
      self.gridsize = gridsize
      self.gdx = self.pieces_rect.width / self.gridsize[0]
      self.gdy = self.pieces_rect.height / self.gridsize[1]
      self.colourkey = self.pieces.get_colorkey()
      
   def pieceofimage(self,gxy):
      (gx,gy) = gxy
      pieceimage = self.pieces.subsurface((gx*self.gdx), (gy*self.gdy), self.gdx, self.gdy).convert()
      pieceimage.set_colorkey(self.colourkey,RLEACCEL)
      return pieceimage,pieceimage.get_rect()
      #
      pieceimage = pygame.Surface((self.gdx,self.gdy))      # new surface
      pieceimage.set_colorkey(self.colourkey,RLEACCEL)
      xy = pygame.Rect(gx*self.gdx, gy*self.gdy,self.gdx,self.gdy)
      pieceimage.blit(self.pieces,(0,0),xy)
      print "CKin poin=" + repr(pieceimage.get_colorkey())
      return pieceimage,pieceimage.get_rect()


class SpritePanelSprite(pygame.sprite.Sprite):
   """ same as pygame.sprite.Sprite, but with spritepanel common behaviours, required
      default implementations &c. """
   def doyourthing(self):
      pass


class ShowGrid(SpritePanelSprite):
   def __init__(self):
      pygame.sprite.Sprite.__init__(self)
      self.image, self.rect = load_image('cursor_snap.png',None)
      self.strength = -1      # cannot pick/move &c.


class SpritePaint(SpritePanelSprite):
   def __init__(self,xy,imagefile = 'button_default',pieces=None,choosepiece=None):
      pygame.sprite.Sprite.__init__(self)    #call Sprite initialiser
      self.flag_paintable = 1
      if not pieces:    # get images from file, as of old
         self.imagefile = imagefile
         self.image_original, self.rect = load_image(self.imagefile + '.png', None)
         self.image_pushed, drop = load_image(self.imagefile + '_push.png', None)
         self.pieces = pieces
         self.choosepiece = choosepiece
      else:    # get images from pieces, which is of class SpritePieces
         if choosepiece:      # choosepiece is gxgy into pieces, xy is coords on screen for centre
            self.pieces = pieces
            self.choosepiece = choosepiece
            self.image_original, self.rect =pieces.pieceofimage(choosepiece)
            #ZZZ for now piece images don't do 'pushed' (just do next row though, if needed. Ie. y+1)
            self.image_pushed = self.image_original.convert()  # copy
      #YYY and always remember to...
      self.image = self.image_original
      self.rect.center = xy
      self.busy = 0
      
   def update(self):
      "move/popup automatically &c."
      pass
      
   def doaction(self,action):
      if not self.busy:
         self.busy = 1
         self.busy_counter = 30
         
   def butdown(self):
      self.image = self.image_pushed
      self.image.set_alpha(140)
   
   def butup(self):
      self.image = self.image_original
      self.image.set_alpha(255)
      
   def clone(self):
      "return a new instance of myself, like myself"
      if not self.pieces:
         minime = SpritePaint(self.rect.center,self.imagefile)
      else:
         print "Should be cloned in subclass"
      return minime


class SpriteAction(SpritePaint):
   def __init__(self,xy,imagefile = 'button_default_action.png',action='go',targetpanel=None):
      SpritePaint.__init__(self,xy,imagefile)
      self.flag_paintable = 0
      self.action = action
      self.settarget(targetpanel)
      self.strength = -2      # not pickable, but actionable
      
   def settarget(self,targetpanel):
      "setup the target panel"
      self.targetpanel = targetpanel
      
   def doyourthing(self):
      if self.targetpanel:
         self.targetpanel.doaction(self.action)


class SpritePanel(pygame.sprite.RenderUpdates):
   # a special panel of sprites which interacts with the mouse
   def __init__(self,rect=None,showgrid=0,flag_target_drop=0,flag_target_pick=0,GRID_BITS=None,**argv):
      if GRID_BITS:
         self.GRID_square = GRID_BITS['square']
         self.GRID_offset = GRID_BITS['offset']
         self.GRID_light_offset = GRID_BITS['light_offset']
         self.GRID_center = GRID_BITS['center']
         self.GRID_number = GRID_BITS['number']
      self.rect = rect     # active drop area for this panel, default None
      sprites = []
      self.showgrid_original = showgrid # there will be a grid
      self.showgrid = self.showgrid_original
      self.gridsprite = ShowGrid()  # initially invisible (not in group)
      pygame.sprite.RenderUpdates.__init__(self,sprites)
      self.flag_target_drop = flag_target_drop
      self.flag_target_pick = flag_target_pick
      
   def ishit(self,coord):
      " is the panel hit ?"
      if self.rect:
         if self.rect.collidepoint(coord):
            return 1
      return 0
      
   def doaction(self,action):
      #ZZZ debug
      if action:
         for sprite in self.sprites():
            sprite.doaction(action)
      
   def grid_on(self):
      "show if was showing before"
      self.showgrid = self.showgrid_original
      
   def grid_off(self):
      self.showgrid = 0

   def snaptogrid(self,pos):
      self.gridsprite.visible = 0
      if self.showgrid:
         if self.ishit(pos):
            (gx,gy) = self.pos2grid(pos)
            if gy > 4:     # must be in bottom 3 rows of board XXX ZZZ This test should only apply to gamestate when placing, not playing
               self.gridsprite.rect.center = self.gridpos(pos)
               if self.rect.contains(self.gridsprite.rect):    # only draw if whole gridsquare fits
                  if not self.has(self.gridsprite):
                     self.add(self.gridsprite)
                  self.gridsprite.visible = 1
      if not self.gridsprite.visible:
         if self.has(self.gridsprite):
            self.remove(self.gridsprite)
   
   def gridpos(self,pos):
      "snap pos to grid and return pixel coords"
      (x,y) = pos
      (a,b) = self.GRID_light_offset
      (c,d) = self.GRID_center
      (q,r) = self.GRID_square
      x = c+ ((int((x-a)/q)*q)+a)
      y = d+ ((int((y-b)/r)*r)+b)
      return (x,y)
      
   def grid2pos(self,grid):
      "convert grid coords to pixel coords"
      (gx,gy) = grid
      (a,b) = self.GRID_light_offset
      (c,d) = self.GRID_center
      (q,r) = self.GRID_square
      x = c + (q*gx)+a
      y = d + (r*gy)+b
      return (x,y)
      
   def pos2grid(self,pos):
      "snap pos to grid and return grid coords"
      (x,y) = pos
      (a,b) = self.GRID_light_offset
      (q,r) = self.GRID_square
      gx = int((x-a)/q)
      gy = int((y-b)/r)
      return (gx,gy)
            
   def sticksprite(self,sprite,xy):
      "just stick it down, no checking"
      sprite.rect.center = self.gridpos(xy)
      sprite.visible = 1
      self.add(sprite)
      return 1    # always allow for now