#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2012
    Posts
    3
    Rep Power
    0

    Pygame sprite movement


    Greetings. I'm a very novice programmer working on a roguelike game in python-pygame to help my young daughters practice their basic math skills. I already created a text-based version of this game that they've used for several years, but at their urging have decided to attempt a graphics version. I've also decided to have a more object-oriented approach this time, but my unfamiliarity with an OO approach is periodically biting me.
    I have a section of code for moving the hero avatar that I recently changed to try to make it so the hero glides one tile in the direction indicated by the mouse, rather than just leaping there. This code isn't working, the hero just sits there, and I can't figure out why. I'd love to both fix this problem and understand what it is that I'm missing about the underlying principles so I can learn something from the process.

    Here's the Hero Class:

    Code:
    class Hero(pygame.sprite.Sprite):
        """Moves the hero around the screen, following the mouse"""
        def __init__(self):
            pygame.sprite.Sprite.__init__(self)
            self.image, self.rect = load_image("girlhero-64.png", -1)
            self.rect = self.image.get_rect()
            self.rect.topleft = (384,0)
            self.targetx, self.targety = 640,480
            
        def movetarget(self):
            pos = pygame.mouse.get_pos()
            self.rect = self.image.get_rect()
            self.targetx = self.rect.topleft[0]
            self.targety = self.rect.topleft[1]
            oldx, oldy = self.rect.topleft
            if oldx + tilesize < pos[0]:
                self.targetx = oldx + tilesize
                #print(targetx)
            if oldx > pos[0]:
                self.targetx = oldx - tilesize
            if oldx + tilesize >= pos[0] and oldx < pos[0]:
                self.targetx = oldx
            if oldy + tilesize < pos[1]:
                self.targety = oldy + tilesize
            if oldy > pos[1]:
                self.targety = oldy - tilesize
            if oldy + tilesize >= pos[1] and oldy < pos[1]:
                self.targety = oldy
            print("NOW THE TARGET IS SET!!!!!!!!")
            print(self.targetx,self.targety)
            
        def update(self):
            "Move the hero closer to the last mouse position clicked self.target"
            self.rect = self.image.get_rect()
            try:
                self.targetx = self.movetarget.targetx
            except:
                self.targetx = self.targetx
            try:
                self.targety = self.movetarget.targety
            except:
                self.targety = self.targety
            currx, curry = self.rect.topleft
            xmove = 0
            ymove = 0
            print("update target is")
            print(self.targetx,self.targety)
            if currx > 960:
                self.rect.topright = (960, curry)
            if currx < 0:
                self.rect.topleft = (0, curry)
            if currx >= 0 and currx <= 960:
                if currx < self.targetx:
                    xmove = 4
                if curry < self.targety:
                    ymove = 4
                if currx > self.targetx:
                    xmove = -4
                if curry > self.targety:
                    ymove = -4
            self.rect = self.rect.move(xmove,ymove)
            print("topleft after")
            print(self.rect.topleft)
    Running this code gets me the following debug messages from the print to terminal commands:
    Code:
    NOW THE TARGET IS SET!!!!!!!!
    64 0
    update target is
    640 480
    topleft after
    (4, 4)

    The load_image class is taken from the Pygame chimp tutorial:
    Code:
    def load_image(name, colorkey=None):
        fullname = os.path.join('data', name)
        try:
            image = pygame.image.load(fullname)
        except pygame.error as 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()

    The body of the program that calls the Hero class, edited to what I think are the relevant portions:
    Code:
    hero = Hero()
    
    charactersprites = pygame.sprite.Group((hero))
    
    mathsterGroup = pygame.sprite.Group()
    mathster = Mathster((0, 0))
    mathsterGroup.add((mathster))
    charactersprites.add((mathster))
    mathster = Mathster((128, 256))
    mathsterGroup.add((mathster))
    charactersprites.add((mathster))
    mathster = Mathster((512, 320))
    mathsterGroup.add((mathster))
    charactersprites.add((mathster))
    
    inventory = TextObject("Hit a mathster!", Font, (0,0,0), (1024,50))
    charactersprites.add((inventory))
    nextproblem = Input(x=825, color=(255,25,25), 
    
    heroGroup = pygame.sprite.Group()
    heroGroup.add(hero)
    
    clock = pygame.time.Clock() 
    
    while 1:
        events1 = pygame.event.get()
        
        for event in events1:
            if event.type == pygame.QUIT: sys.exit()
            
        if not menu:
            for events in events1:
                if event.type == MOUSEBUTTONDOWN:
                    mouseclick = 1
        
            if not collisiontest and mouseclick:
                for heros in charactersprites:
                    hero.movetarget()
                for mathsters in mathsterGroup:
                    mathster.movetarget()
                mouseclick = 0
            screen.fill((50,100,200))
            gamebackground = GameMap()
            if not mapdrawn:
                levelmap = gamebackground.drawmap()
                mapdrawn = 1
            screen.blit(levelmap, (0,0))
            wallGroup.draw(screen)
            nextproblem.update()
            nextproblem.draw(screen)
            charactersprites.update()
            charactersprites.draw(screen)
            screen.fill((50,100,200))
            screen.blit(menubackground, (0,0))
            menusprites.update()
            menusprites.draw(screen)
                
                
        pygame.display.flip()
        clock.tick(120)
    Thanks in advance to anyone who takes a look and is willing to lend a hand. I've been reading lots of tutorials to try to educate myself, but I've been unable to find where I've gone wrong.

    Any tips to improve my code are welcome, although fixing my motionless hero is my highest priority.
  2. #2
  3. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,702
    Rep Power
    480
    Syntax error in your post:

    nextproblem = Input(x=825, color=(255,25,25),


    How to help? I'd need you need to supply more code
    (we don't know about Mathster) or propose a simpler question such as "How do I make a pygame sprite follow the mouse?"

    Dave.

    PS. load_image is a function, not a class.
    Last edited by b49P23TIvg; June 29th, 2012 at 04:04 PM.
    [code]Code tags[/code] are essential for python code and Makefiles!
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2012
    Posts
    3
    Rep Power
    0
    Originally Posted by b49P23TIvg
    Syntax error in your post:

    nextproblem = Input(x=825, color=(255,25,25),


    How to help? I'd need you need to supply more code
    (we don't know about Mathster) or propose a simpler question such as "How do I make a pygame sprite follow the mouse?"

    Dave.

    PS. load_image is a function, not a class.
    Thanks so much for replying. The Syntax error is from a inaccurate copy/paste. I appreciate the terminology guidance vis-a-vis function versus class.

    I shall try to reframe my problem as a more succinct and specific question. Below is the Hero class with a couple more print commands to the terminal that I'm trying to use to sort out what's going wrong:

    Code:
    class Hero(pygame.sprite.Sprite):
        """Moves the hero around the screen, following the mouse"""
        #male = load_image("malehero-64.png", -1)
        #female = load_image("girlhero-64.png", -1)
        def __init__(self):
            pygame.sprite.Sprite.__init__(self)
            self.image, self.rect = load_image("girlhero-64.png", -1)
            self.rect = self.image.get_rect()
            self.rect.topleft = (384,0)
            self.target = self.movetarget()
        
        #def setgender(self, gender):
            #if gender == "male":
                #self.image, self.rect = self.male
            #if gender == "female":
                #self.image, self.rect = self.female
            #self.startingpos = (480, 640)
            #self.rect.topleft = (480,640)
            #self.target = self.rect.topleft
            
        def movetarget(self):
            self.pos = pygame.mouse.get_pos()
            self.rect = self.image.get_rect()
            self.target = self.rect.topleft
            self.targetx,self.targety = self.target
            self.oldx, self.oldy = self.rect.topleft
            if self.oldx + tilesize < self.pos[0]:
                self.targetx = self.oldx + tilesize
            if self.oldx > self.pos[0]:
                self.targetx = self.oldx - tilesize
            if self.oldx + tilesize >= self.pos[0] and self.oldx < self.pos[0]:
                self.targetx = self.oldx
            if self.oldy + tilesize < self.pos[1]:
                self.targety = self.oldy + tilesize
            if self.oldy > self.pos[1]:
                self.targety = self.oldy - tilesize
            if self.oldy + tilesize >= self.pos[1] and self.oldy < self.pos[1]:
                self.targety = self.oldy
            self.target = (self.targetx,self.targety)
            print("NOW THE TARGET IS SET!!!!!!!!")
            print(self.targetx,self.targety)
            return self.target
            
        def update(self):
            "Move the hero closer to the last mouse position clicked self.target"
            self.rect = self.image.get_rect()
            print("topleft before")
            print(self.rect.topleft)
            self.xmove = 0
            self.ymove = 0
            print("update target is")
            print(self.target)
            if self.rect.topleft[0] > 960:
                self.rect.topleft = (960, self.rect.topleft[1])
            if self.rect.topleft[0] < 0:
                self.rect.topleft = (0, self.rect.topleft[1])
            if self.rect.topleft[0] >= 0 and self.rect.topleft[0] <= 960:
                if self.rect.topleft[0] < self.target[0]:
                    self.xmove = 4
                if self.rect.topleft[1] < self.target[1]:
                    self.ymove = 4
                if self.rect.topleft[0] > self.target[0]:
                    self.xmove = -4
                if self.rect.topleft[1] > self.target[1]:
                    self.ymove = -4
            self.newx=self.rect.topleft[0]+self.xmove
            self.newy=self.rect.topleft[1]+self.ymove
            print("newx and newy")
            print (self.newx,self.newy)
            self.rect.topleft = (self.newx,self.newy)
            print("topleft after")
            print(self.rect.topleft)
    When run, a snippet of the terminal is as follows:

    Code:
    NOW THE TARGET IS SET!!!!!!!!
    64 64
    NOW THE TARGET IS SET!!!!!!!!
    64 64
    NOW THE TARGET IS SET!!!!!!!!
    64 64
    NOW THE TARGET IS SET!!!!!!!!
    64 64
    topleft before
    (0, 0)
    update target is
    (0, 0)
    newx and newy
    0 0
    topleft after
    (0, 0)
    topleft before
    (0, 0)
    update target is
    (0, 0)
    newx and newy
    0 0
    topleft after
    (0, 0)
    topleft before
    (0, 0)
    update target is
    (0, 0)
    newx and newy
    0 0
    topleft after
    (0, 0)
    What I am attempting to accomplish:
    When the mouse is clicked on the game map, Hero.movetarget is called, and sets a movement target one tile distant from the current rect. Then, when the charactersprites group calls the update methods(is that the correct term here?) on all sprites in the group, they should move a few pixels towards that target tile.

    I had the sprites doing this without issue when the update method was simply moving all sprites one tile in a leap. I'm trying to change it so that the sprites glide over several frames to their new target tile, rather than simply leaping there.

    As I read the terminal printout above, the self.target variable set in Hero.movetarget is not the same as the self.target printed to terminal at the beginning of Hero.update.

    I don't understand why changing self.target in Hero.movetarget isn't changing self.target in Hero.update.

    Please excuse any ignorance in my phrasing of the question, and thank you in advance for any insight you can provide.
  6. #4
  7. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,702
    Rep Power
    480

    status report


    I plan to work on this. I've used pygame once---only to answer a question at this forum. Probably won't get to it until Monday and I expect the answer to be less than straightforward (for me). I'm a text based dude, not a GUI guy. Slog through manuals. Try this and that.
    [code]Code tags[/code] are essential for python code and Makefiles!
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2012
    Posts
    3
    Rep Power
    0
    Originally Posted by b49P23TIvg
    I plan to work on this. I've used pygame once---only to answer a question at this forum. Probably won't get to it until Monday and I expect the answer to be less than straightforward (for me). I'm a text based dude, not a GUI guy. Slog through manuals. Try this and that.
    I solved the issue. I'll post my new, working code below, as I'm still not 100% sure that I understand how I solved the problem and that's not a learning experience. Anyway, I have working code, so please put this on your back burner, but if you get the chance and have the inclination to explain why these changes worked, I'd love to know.

    I'm pretty sure it was the change from calling the entire charactersprites group to update to calling the hero group separately that solved the issue, but I did also make a couple tweaks to the Hero class to get is working correctly.

    Thanks so much for your willingness to help.

    The revised hero class:

    Code:
    class Hero(pygame.sprite.Sprite):
        """Moves the hero around the screen, following the mouse"""
        male = load_image("malehero-64.png", -1)
        female = load_image("girlhero-64.png", -1)
        def __init__(self):
            pygame.sprite.Sprite.__init__(self)
            self.image, self.rect = self.female
            self.rect = self.image.get_rect()
            self.rect.topleft = (320,0)
            self.target = self.movetarget()
        
        def setgender(self, gender):
            if gender == "male":
                self.image, self.rect = self.male
            if gender == "female":
                self.image, self.rect = self.female
            #self.startingpos = (480, 640)
            self.rect.topleft = (480,640)
            self.target = self.movetarget()
            print("Gender set")
            print(gender)
            
        def movetarget(self):
            self.pos = pygame.mouse.get_pos()
            #self.rect = self.image.get_rect()
            self.target = self.rect.topleft
            self.targetx,self.targety = self.target
            self.oldx, self.oldy = self.rect.topleft
            if self.oldx + tilesize < self.pos[0]:
                self.targetx = self.oldx + tilesize
            if self.oldx > self.pos[0]:
                self.targetx = self.oldx - tilesize
            if self.oldx + tilesize >= self.pos[0] and self.oldx < self.pos[0]:
                self.targetx = self.oldx
            if self.oldy + tilesize < self.pos[1]:
                self.targety = self.oldy + tilesize
            if self.oldy > self.pos[1]:
                self.targety = self.oldy - tilesize
            if self.oldy + tilesize >= self.pos[1] and self.oldy < self.pos[1]:
                self.targety = self.oldy
            self.target = (self.targetx,self.targety)
            #print("NOW THE TARGET IS SET!!!!!!!!")
            #print(self.targetx,self.targety)
            return self.target
            
        def update(self):
            "Move the hero closer to the last mouse position clicked self.target"
            self.xmove = 0
            self.ymove = 0
            if self.rect.topleft[0] > 960:
                self.rect.topleft = (960, self.rect.topleft[1])
            if self.rect.topleft[0] < 0:
                self.rect.topleft = (0, self.rect.topleft[1])
            if self.rect.topleft[0] >= 0 and self.rect.topleft[0] <= 960:
                if self.rect.topleft[0] < self.target[0]:
                    self.xmove = 4
                if self.rect.topleft[1] < self.target[1]:
                    self.ymove = 4
                if self.rect.topleft[0] > self.target[0]:
                    self.xmove = -4
                if self.rect.topleft[1] > self.target[1]:
                    self.ymove = -4
            self.newx=self.rect.topleft[0]+self.xmove
            self.newy=self.rect.topleft[1]+self.ymove
            self.rect.topleft = (self.newx,self.newy)
            
        def draw(self, blit_target):
            blit_target.blit(self.image, self.rect)
    The revised bits from the main loop:
    Code:
    hero = Hero()
    
    #charactersprites = pygame.sprite.Group((hero))
    
    mathsterGroup = pygame.sprite.Group()
    mathster = Mathster((2, 3))
    mathsterGroup.add((mathster))
    #charactersprites.add((mathster))
    mathster = Mathster((4, 5))
    mathsterGroup.add((mathster))
    #charactersprites.add((mathster))
    mathster = Mathster((9, 8))
    mathsterGroup.add((mathster))
    #charactersprites.add((mathster))
    #print(charactersprites)
    print(mathsterGroup)
    
    inventory = TextObject("Hit a mathster!", Font, (0,0,0), (16*TILESIZE,50))
    #charactersprites.add((inventory))
    nextproblem = Input(x=825, color=(255,25,25), maxlength=5, prompt="")
    
    heroGroup = pygame.sprite.Group()
    heroGroup.add(hero)
    
    
    
    for events in events1:
                if event.type == MOUSEBUTTONDOWN:
                    mouseclick = 1
        
            if not collisiontest and mouseclick:
                hero.movetarget()
                for mathster in mathsterGroup:
                    mathster.movetarget()
                newmathstertest = random.randint(1, 50)
                if newmathstertest > 49 and not problemgiven:
                    randomx = random.randint(0, 15)
                    randomy = random.randint(0, 11)
                    mathster = Mathster((randomx, randomy))
                    #charactersprites.add(mathster)
                    mathsterGroup.add(mathster)
                mouseclick = 0
            screen.fill((50,100,200))
            gamebackground = GameMap()
            if not mapdrawn:
                levelmap = gamebackground.drawmap()
                mapdrawn = 1
            screen.blit(levelmap, (0,0))
            wallGroup.draw(screen)
            nextproblem.update()
            nextproblem.draw(screen)
            hero.update()
            hero.draw(screen)
            mathsterGroup.update()
            mathsterGroup.draw(screen)
            inventory.draw(screen)
    And to the text-based, yes. My first version of this was text-based and functional and the kids played it and practiced their math facts quite a bit. But these young whippersnappers want their flashing lights and ominous beeps.

    Thanks again.

    Comments on this post

    • b49P23TIvg agrees : Thank you! I was about to start browsing the internet for load_image and now i won't.

IMN logo majestic logo threadwatch logo seochat tools logo