3

I am creating a basic tanks game and I am currently working on the gun of the tank. I have managed to make the gun rotate around an axis and it moves whenever you press the "a" and "d" keys but sometimes when the gun has been rotated and then is moved, it moves to the right for some reason. Here is the code:

class tankGun(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = tankGunImage
        self.rect = self.image.get_rect()
        self.x = 400
        self.xx = 410
        self.y = 400
        self.rect.x = self.x
        self.rect.y = self.y
        self.angle = 0
        self.original = self.image
        tankGunList.add(self)
        tankGunList.draw(gameDisplay)

def rotate(self, chk):
    test = False
    if chk == 1:
        self.angle += 1
    elif chk == 2:
        self.angle -= 1
    if self.angle > 0 and self.angle < 180:
        test = True
    else:
        if chk == 1:
            self.angle = 180
        elif chk == 2:
            self.angle = 0
    if test == True:
        self.image = pygame.transform.rotate(self.original, self.angle)
        tankGunList.clear(gameDisplay, background)
        tankGunList.draw(gameDisplay)
        self.rect = self.image.get_rect(center = self.rect.center)

def move(self, chk):
    self.x += chk
    self.xx = self.x + 10
    self.rect.x = self.x
    tankGunList.clear(gameDisplay, background)
    tankGunList.draw(gameDisplay)        

(Side note: chk is sent in as either +1 or -1 for the movement and 1 or 2 for the rotation. It's just a way of detecting which key was pressed.)

Harry West
  • 35
  • 5
  • BTW: `self.x` has always the same value as `self.rect.x` so you can use only `self.rect.x` and remove `self.x` - the same with `self.y` – furas Dec 14 '17 at 23:48
  • it seems you use `tankGunList.draw(gameDisplay)` in three places. You should draw only once - in mainloop – furas Dec 14 '17 at 23:51

2 Answers2

1

The problem occurs because you don't update the self.x position after a rotation. When you rotate an image, its bounding box size gets changed and so the new rect's x self.rect.x position will be changed. However, the self.x attribute still stays the same and when you move the tank the next time, you synchronize self.x and self.rect.x again and it jumps.

To fix the problem, just update self.x after a rotation:

if turn == True:
    self.image = pg.transform.rotate(self.original, self.angle)
    self.rect = self.image.get_rect(center=self.rect.center)
    self.x = self.rect.x

I'd rather use the x and y attributes for the center coords and then set self.rect.center = (self.x, self.y), so that you don't have to update the self.x after a rotation.

import pygame as pg


pg.init()
screen = pg.display.set_mode((640, 480))
tankGunImage = pg.Surface((30, 50), pg.SRCALPHA)
tankGunImage.fill((70, 200, 30))


class tankGun(pg.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = tankGunImage
        self.rect = self.image.get_rect()
        self.x = 300  # Center x.
        self.xx = 310
        self.y = 200  # Center y.
        # Set the center attributes.
        self.rect.centerx = self.x
        self.rect.centery = self.y
        self.angle = 0
        self.original = self.image

    def rotate(self, chk):
        turn = False
        if chk == 1:
            self.angle += 4
        elif chk == 2:
            self.angle -= 4
        if self.angle > 0 and self.angle < 180:
            turn = True
        else:
            if chk == 1:
                self.angle = 180
            elif chk == 2:
                self.angle = 0

        if turn == True:
            self.image = pg.transform.rotate(self.original, self.angle)
            self.rect = self.image.get_rect(center=self.rect.center)
            # No need to update the `self.x` now, since the center stays the same.

    def move(self, chk):
        self.x += chk
        self.xx = self.x + 10
        self.rect.center = (self.x, self.y)


def main():
    clock = pg.time.Clock()
    color = pg.Color(30, 30, 30)
    all_sprites = pg.sprite.Group()
    tank = tankGun()
    all_sprites.add(tank)

    done = False
    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True

        keys = pg.key.get_pressed()
        if keys[pg.K_a]:
            tank.rotate(1)
        if keys[pg.K_d]:
            tank.rotate(2)
        if keys[pg.K_j]:
            tank.move(-4)
        if keys[pg.K_l]:
            tank.move(4)
        all_sprites.update()

        screen.fill(color)
        all_sprites.draw(screen)
        pg.display.flip()
        clock.tick(30)


if __name__ == '__main__':
    main()
    pg.quit()
skrx
  • 19,980
  • 5
  • 34
  • 48
0

I don't know why you have problem - I can't run it to see problem.

But I show what I would change. Maybe it will help.

First I would use more readable names instead of chk - ie. direction, step

class tankGun(pygame.sprite.Sprite):

    def __init__(self):
        super().__init__()

        self.original = tankGunImage
        self.image = self.original

        self.rect = self.image.get_rect()
        self.rect.x = 400
        self.rect.y = 400

        self.xx = self.rect.x + 10

        self.angle = 0

        tankGunList.add(self)

    def rotate(self, direction):
        rotate_image = False

        if direction == 1:
            if self.angle < 180
                self.angle += 1
                rotate_image = True
        elif direction == 2:
            if self.angle > 0
                self.angle -= 1
                rotate_image = True

        if rotate_image == True:
            self.image = pygame.transform.rotate(self.original, self.angle)
            self.rect = self.image.get_rect(center=self.rect.center)

    def move(self, step):
        self.rect.x += step
        self.xx = self.rect.x + 10

# --- in mainloop ---

tankGunList.clear(gameDisplay, background)
tankGunList.draw(gameDisplay)
furas
  • 134,197
  • 12
  • 106
  • 148