Newb:引用调用类的最佳方法

时间:2014-05-02 03:50:13

标签: python pygame

完整的游戏代码:http://pastebin.com/v9UvDYWy

我觉得我做错了...我有一个游戏类,它有一个加载器方法,然后是一个玩家类。玩家类需要向游戏类的加载器方法询问其图像,并且当它发射子弹时(使用子弹类)它再次需要询问子弹图像......这就是我现在拥有的... < / p>

class Player(pygame.sprite.Sprite):
    """ This class represents the player. """

    mouse = True

    def __init__(self, caller):
        pygame.sprite.Sprite.__init__(self)
        self.image = caller.PLAYER_IMAGE
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)

    def update(self):
        """ Update the player location. """
        if self.mouse:
            self.rect.center = pygame.mouse.get_pos()           # center player to mouse
            self.rect.y = SCREEN_HEIGHT-130                     # lock player to bottom of screen


class Bullet(pygame.sprite.Sprite):
    speed = 0

    """ This Class Represents a Bullet"""
    def __init__(self, caller):

        #call the parent class constructor
        pygame.sprite.Sprite.__init__(self)
        self.image = caller.BULLET_IMAGE
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)
        self.speed = 8

    def update(self):
        """Move Bullet"""
        self.rect.y -= self.speed

目前我的游戏类有一个方法“射击”,但我觉得玩家类应该是拥有这个方法的人......但是如果我这样做那么Player类和Bullet类之间的引用会变得混乱。游戏应该是保持子弹列表等的游戏,以便它可以检查碰撞但是如果玩家是启动子弹类的玩家那么玩家对象需要一种引用游戏对象的方式来创建该列表那里。

这是子弹碰撞逻辑:

for bullet in self.bullet_list:
    # check if the lasers(bullet) hit anything in the block list(enemies)
    bullet_hit_list = pygame.sprite.spritecollide(bullet, self.block_list, True,collided=pygame.sprite.collide_mask)

    for block in bullet_hit_list:
        self.bullet_list.remove(bullet)
        self.all_sprites_list.remove(bullet)
        self.score += block.scorevalue

    if bullet.rect.y < -10:  # remove bullet if it goes off screen
        self.bullet_list.remove(bullet)
        self.all_sprites_list.remove(bullet)

我知道我的问题不是很清楚,但我不知道怎么说得更清楚...所以我改变了代码,所以玩家有方法射击..注意玩家现在需要暂时保持子弹图像,因为子弹类不知道如何找到它。

class Player(pygame.sprite.Sprite):
    """ This class represents the player. """

    mouse = True
    bullet_list = None

    def __init__(self, caller):
        pygame.sprite.Sprite.__init__(self)
        self.bullet_list = pygame.sprite.Group()
        self.image = caller.PLAYER_IMAGE
        self.BULLET_IMAGE = caller.BULLET_IMAGE
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)

    def shoot(self,caller):
        if len(self.bullet_list) < 3:
            bullet = Bullet(self)
            # Set the bullet so it is where the player is(and centered)
            bullet.rect.center = self.rect.center
            # Add the bullet to the lists
            #pygame.Surface.fill(self.bullet.image,RED)
            caller.all_sprites_list.add(bullet)
            self.bullet_list.add(bullet)
            #Play Bullet sound
            caller.sound_bullet_fire.play()

    def update(self):
        """ Update the player location. """
        if self.mouse:
            self.rect.center = pygame.mouse.get_pos()           # center player to mouse
            self.rect.y = SCREEN_HEIGHT-130                     # lock player to bottom of screen


class Bullet(pygame.sprite.Sprite):
    speed = 0

    """ This Class Represents a Bullet"""
    def __init__(self, caller):

        #call the parent class constructor
        pygame.sprite.Sprite.__init__(self)
        self.image = caller.BULLET_IMAGE
        self.rect = self.image.get_rect()
        self.mask = pygame.mask.from_surface(self.image)
        self.speed = 8

    def update(self):
        """Move Bullet"""
        self.rect.y -= self.speed

2 个答案:

答案 0 :(得分:0)

由于您已将caller传递到Player.shoot,为什么不将其传递给Bullet的初始值设定项?

class Player(pygame.sprite.Sprite):
    ...
    def shoot(self,caller):
        if len(self.bullet_list) < 3:
            bullet = Bullet(self, caller)

答案 1 :(得分:0)

我有一个pygame游戏,也管理射弹。它有类似的困境。我发现最有效的方法是让子弹类管理自己的图像,并让玩家管理其所有附属子弹,调用子弹需要更新的子弹中包含的任何功能。角色还包含投射子弹的功能。所有游戏都将管理子弹(我将所有在屏幕上定期更新的内容更新为渲染更新组)。这段代码有点普遍,但它对于弹丸管理非常有效,我使用这个基本的大型机快速制作了几个游戏:

class Moveable(pygame.sprite.Sprite):
    def __init__(self,pos,imageFileName):
        pygame.sprite.Sprite.__init__(self)
        self.init2(pos,imageFileName)
    def init2(self,pos,imageFileName):
        self.right = pygame.image.load(imageFileName).convert_alpha()
        self.left = pygame.transform.flip(self.right,True,False)
        self.vertical = pygame.transform.rotate(self.right,90)
        self.image = self.right
        if self not in projectiles:
            self.direction = "right"
        self.rect = self.image.get_rect()
        self.rect.center = pos
    def move(self,dx,dy):
        if self in special:
            pygame.display.update(special.draw(screen))
        #print COLOR
        screen.fill((COLOR),self.rect)
        #print COLOR
        if dx > 0:
            self.image = self.right
            if self not in projectiles:
                self.direction = "right"
        elif dx < 0:
            self.image = self.left
            if self not in projectiles:
                self.direction = "left"
        collisions = pygame.sprite.spritecollide(self, collidable, False)
        for other in collisions:
            if other != self:
                (awayDx,awayDy) = self.moveRelative(other,-1)
                if abs(dx + 1*(awayDx)) < abs(dx):
                    dx = 0
                if abs(dy + 1*(awayDy)) < abs(dy):
                    dy = 0
                dx = dx + 1*(awayDx)
                dy = dy + 1*(awayDy)
        for item in gravities:
            if item != self:
                (toDx,toDy) = self.moveRelative(item,1)
                dx = dx + (item.force*(toDx))/(self.rangeTo(item)/100)
                dy = dy + (item.force*(toDy))/(self.rangeTo(item)/100)
        self.rect.move_ip(dx,dy)
    def moveRelative(self,other,speed,exact=False):
        dx = other.rect.x - self.rect.x
        dy = other.rect.y - self.rect.y
        if exact == False:
            if abs(dx) > abs(dy):
                # other is farther away in x than in y
                if dx > 0:
                    return (+speed,0)
                else:
                    return (-speed,0)
            else:
                if dy > 0:
                    return (0,+speed)
                else:
                    return (0,-speed)
        else:
            angle = math.atan2(dx,dy)
            newx = speed*math.cos(angle)
            newy = speed*math.sin(angle)
            return (newy,newx)
    def movePerpendicular(self,other,speed):
        dx = other.rect.x - self.rect.x
        dy = other.rect.y - self.rect.y
        if abs(dx) > abs(dy):
            # this is to dodge a projectile
            if dy > 0:
                if dx > 0:
                    return (+speed/2,+speed)
                else:
                    return (-speed/2,+speed)
            else:
                if dx > 0:
                    return (+speed/2,-speed)
                else:
                    return (-speed/2,-speed)
        else:
            if dx > 0:
                if dy > 0:
                    return (+speed,+speed/2)
                else:
                    return (+speed,-speed/2)
            else:
                if dy > 0:
                    return (-speed,+speed/2)
                else:
                    return (-speed,-speed/2)
    def rangeTo(self,other):
        dx = other.rect.x - self.rect.x
        dy = other.rect.y - self.rect.y
        return math.sqrt(dx*dx + dy*dy)

class Stationary(pygame.sprite.Sprite):
    def __init__(self,pos,imageFileName,collide,impermeable=False,alpha=False):
        pygame.sprite.Sprite.__init__(self)
        self.init2(pos,imageFileName,collide,alpha)
        self.impermeable = impermeable
    def init2(self,pos,imageFileName,collide,alpha):
        drawn.add(self)
        updated.add(self)
        if collide:
            collidable.add(self)
        if alpha:
            self.image = pygame.image.load(imageFileName).convert_alpha()
        else:
            self.image = pygame.image.load(imageFileName).convert()
        self.rect = self.image.get_rect()
        self.rect.center = pos
    def rangeTo(self,other):
        dx = other.rect.x - self.rect.x
        dy = other.rect.y - self.rect.y
        return math.sqrt(dx*dx + dy*dy)

class unit(Moveable):
    def __init__(self,pos,image,health,projectile_count,fire_rate,team,speed,impermeable=False,special_reload={}):
        Moveable.__init__(self,pos,image)
        self.projectiles = []
        for number in range(projectile_count):
            self.projectiles.append(None)
        self.health = health
        self.team = team
        team.add(self)
        drawn.add(self)
        collidable.add(self)
        updated.add(self)
        self.firetime = fire_rate
        self.fire_rate = fire_rate
        self.reload = special_reload
        self.speed = speed
        self.impermeable = impermeable
    def maintain(self):
        self.firetime -= 1
        for item in self.reload:
            self.reload[item][0] -= 1
        index = 0
        for projectile in self.projectiles:
            if projectile != None:
                if projectile.update() == False:
                    projectile.end()
                    self.projectiles[index] = None
            index += 1
        collisions = pygame.sprite.spritecollide(self, damagers, False)
        for other in collisions:
            if other != self and other.parent != self:
                try:
                    self.health -= other.damage
                except AttributeError:
                    print "Error: Unit Damage Collisions:", other.__name__, "lacks the attribute self.damage used to damage object"
                    raise RuntimeError
        if self.health <= 0:
            self.end()
    def project(self,bolt,speed=6,direction=None,target=None,accuracy=None,aimed=False):
        if bolt not in self.reload.keys():
            if self.firetime > 0:
                pass
            else:
                if direction == None:
                    if aimed == True:
                        direction = self.moveRelative(target,speed,exact=True)
                    else:
                        direction = self.moveRelative(target,speed)
                index = 0
                for projectile in self.projectiles:
                    if projectile == None:
                        self.projectiles[index] = bolt(self.rect.center,direction,self)
                        projectiles.add(self.projectiles[index])
                        updated.add(self.projectiles[index])
                        break
                    index += 1
                self.firetime = self.fire_rate
        else:
            if self.reload[bolt][0] > 0:
                pass
            else:
                if direction == None:
                    direction = self.moveRelative(target,speed)
                index = 0
                for projectile in self.projectiles:
                    if projectile == None:
                        self.projectiles[index] = bolt(self.rect.center,direction,self)
                        projectiles.add(self.projectiles[index])
                        updated.add(self.projectiles[index])
                        break
                    index += 1
                self.reload[bolt][0] = self.reload[bolt][1]
    def end(self):
        for projectile in self.projectiles:
            if projectile != None:
                projectile.end()
        drawn.remove(self)
        collidable.remove(self)
        updated.remove(self)
        special.remove(self)
        damagers.remove(self)
        explosion_triggers.remove(self)
        self.team.remove(self)
        screen.fill(COLOR,self.rect)
    def goup(self):
        self.directions = (0,-1)
        self.inertia()
    def godown(self):
        self.directions = (0,1)
        self.inertia()
    def goleft(self):
        self.directions = (-1,0)
        self.inertia()
    def goright(self):
        self.directions = (1,0)
        self.inertia()
    def magnitude(self,factor):
        return (self.directions[0]*factor,self.directions[1]*factor)
    def inertia(self):
        self.move(*self.magnitude(self.speed))
    def shoot(self,bolt):
        self.project(bolt,direction=self.magnitude(10))
    def aimshoot(self,bolt,vector):
        self.project(bolt,direction=vector)

class projectile(Moveable):
    def __init__(self,pos,direction,image,destructcount,damager=False,boomtrigger=False,simple=True,parent=None,arc=False,explodable=False,countdown=None):
        Moveable.__init__(self,pos,image)
        self.arc=arc
        self.destructCountDown = destructcount
        self.full = destructcount
        if self.arc == True:
            magnitude = math.sqrt(direction[0]*direction[0]+direction[1]*direction[1])
            if explodable == True:
                time = float(self.full)-float(countdown)/2.0
            else:
                time = float(self.full)/2.0
            self.change = float(magnitude)/float(time)
        self.parent = parent
        drawn.add(self)
        if boomtrigger != False:
            explosion_triggers.add(self)
        if damager != False:
            self.damage = damager
            damagers.add(self)
        self.directions = list(direction)
        self.explodable = explodable
        self.delay = 0
        if simple == True:
            if abs(self.directions[1]) > abs(self.directions[0]):
                self.image = self.vertical
                self.rect = self.image.get_rect()
                self.rect.center = pos
        else:
            self.image = pygame.transform.rotate(self.vertical,math.degrees(math.atan2(direction[0],direction[1])))
            self.rect = self.image.get_rect()
            self.rect.center = pos
    def update(self):
        if self.delay > 0:
            self.delay -= 1
        else:
            screen.fill((COLOR),self.rect)
            collisions = pygame.sprite.spritecollide(self, collidable, False)
            for other in collisions:
                if other != self:
                    if other.impermeable == True:
                        self.end()
                        return False
            self.destructCountDown = self.destructCountDown - 1
            if self.explodable == True:
                if self.destructCountDown == self.countdown:
                    self.destruct()
            if self.destructCountDown <= 0:
                return False
            else:
                if self.explodable == True:
                    if self.exploded == False:
                        if self.arc == True:
                            print self.destructCountDown, (self.full-self.countdown)/2
                            if self.destructCountDown > (self.full-self.countdown)/2:
                                self.rect.move_ip(*self.directions)
                                self.directions[0], self.directions[1] = self.adjustmag(self.directions[0], self.directions[1],-1)
                            else:
                                self.rect.move_ip(*self.directions)
                                self.directions[0], self.directions[1] = self.adjustmag(self.directions[0], self.directions[1],1)
                        else:
                            self.rect.move_ip(*self.directions)
                else:
                    if self.arc == True:
                        if self.destructCountDown > self.full/2:
                            self.rect.move_ip(*self.directions)
                            self.directions[0], self.directions[1] = self.adjustmag(self.directions[0], self.directions[1],-1)
                        else:
                            self.rect.move_ip(*self.directions)
                            self.directions[0], self.directions[1] = self.adjustmag(self.directions[0], self.directions[1],1)
                    else:
                        self.rect.move_ip(*self.directions)
                return True
    def adjustmag(self,x,y,change):
        angle = math.atan2(y,x)
        print "x, y: ", x, y
        magnitude = math.sqrt(x*x+y*y)
        print "magnitude: ", magnitude
        if magnitude > 1 and change < 0:
            magnitude = float(magnitude) + float(change)
        elif change > 0:
            magnitude = float(magnitude) + float(change)
        nx = math.cos(angle)*float(magnitude)
        ny = math.sin(angle)*float(magnitude)
        return nx, ny
    def end(self):
        drawn.remove(self)
        collidable.remove(self)
        updated.remove(self)
        damagers.remove(self)
        explosion_triggers.remove(self)

此代码是较大程序的一部分,如果你运行它不会起作用,它还包括你不应该需要的一些功能,但这是一个很好的,很好实现的如何射弹的例子应该是有条理的。然而,基本思想是单元管理射弹的更新并具有射击它们的功能,射弹具有内部更新功能,可以在初始化时作为精灵移动,管理自己的图像(它们是自己的类),以及在更新功能中管理他们自己的毁灭,他们可以返回到玩家,游戏将绘制所有内容。