We can run App’s init method. We should also set a constant NUM_TARGETS to the number of targets that we want at one time in our game. Lastly, we need to set the player’s score to 0.
def __init__(self):
# this can be changed, it's the number of targets allowed at a time.
# we initialize this before super().__init__ because super().__init__ calls
# create_objects, which utilizes self.NUM_TARGETS
self.NUM_TARGETS = 3
super().__init__(title="Tanks")
self.playerscore = 0 # the player's score
# sets the display icon to the TankIcon.png provided
pygame.display.set_icon(pygame.image.load("./TankIcon.png"))
This should run at the end of the init function. This will initialize the objects that we will need for the game. Since we already have objects, this is pretty simple.
def create_objects(self):
"""
This creates the initial objects seen when the game
first starts up.
"""
# tank
self.tank = Tank(speed=TANKSPEED)
self.tank.moveto(
(
self.size[0] / 2 - self.tank.size[0], # move to middle x
self.size[1] - self.tank.size[1], # move to bottom y
)
)
# targets
# create targets
self.targets = [Target(speed=[0, 0]) for i in range(self.NUM_TARGETS)]
# move each target to a random position
for target in self.targets:
target.moveto(
(
random.randint(
0, self.size[0] - target.size[0]
), # random x
random.randint(
0, self.size[1] - target.size[1]
), # random y
)
)
# bullets (none because no shots fired)
self.bullets = []
# score text
self.font = pygame.font.SysFont(pygame.font.get_default_font(), 32)
This method is pretty easy since all we have to do is use the move methods that we built.
def move_objects(self):
"""
This method moves the objects within the game.
If a bullet is outside of the screen, it is
not moved and is unreferenced.
"""
self.tank.move()
self.bullets = [
bullet
for bullet in self.bullets
if bullet.check_out_of_screen(self.size) is False
]
for bullet in self.bullets:
bullet.move()
For this method, we just need to fill the screen with SANDBROWN (to clear the past screen), use our pre-built draw methods, and then draw the score text onto the screen.
def update_display(self):
self.screen.fill(SANDBROWN)
# tank
self.tank.draw(self.screen, SANDBROWN)
# targets
for target in self.targets:
target.draw(self.screen, BLACK)
# bullets
for bullet in self.bullets:
bullet.draw(self.screen, BLACK)
# score text
font_img = self.font.render(
"Score: %s" % str(self.playerscore), True, BLACK
)
font_rect = font_img.get_rect()
pygame.draw.rect(self.screen, SANDBROWN, font_rect, 1)
self.screen.blit(font_img, font_rect)
This method should check for collisions between bullets and targets and between the tank and targets. So, we use the pre-built check_collision method.ch
def check_collisions(self):
"""
This checks whether any of the objects within the game have collided
with each other. Specifically, we are looking for collisions between
bullets and targets or the tank and targets
"""
deletions = 0 # number of targets deleted
num_bullets = len(self.bullets)
# check bullet-target collisions
for i in range(num_bullets):
for target in self.targets:
# if the bullet collided with the target
if self.bullets[i - deletions].check_collision(target) is True:
# pop both the bullet and target so that they will be
# effectively deleted
self.bullets.pop(i - deletions)
self.targets.pop(self.targets.index(target))
# give points for hitting the target
self.playerscore += 20
deletions += 1
break # stop the current iteration since the target and
# bullet are popped, so referencing them would error.
# check tank-target collisions
for target in self.targets:
if self.tank.check_collision(target) is True:
self.targets.pop(self.targets.index(target))
deletions += 1
self.playerscore += 10 # only 10 for running over targets lol
# create a new target for every deleted target
for i in range(deletions):
a = Target(speed=[0, 0])
a.moveto(
(
random.randint(0, self.size[0] - a.size[0]),
random.randint(0, self.size[1] - a.size[1]),
)
)
self.targets.append(a)
This is probably the most complex method since we want the tank to be moved if keys are pressed and bullets to be created if the mouse is clicked.
def check_events(self, event):
"""
We imported all from pygame.locals, so that means
that we can check KEYDOWN and KEYUP and individual
keys such as K_w (w key), K_a (a key), etc.
"""
# change the path of the tank if w, a, s, or d was pressed
if event.type == KEYDOWN:
if event.key == K_w:
self.tank.set_path("up")
if event.key == K_s:
self.tank.set_path("down")
if event.key == K_a:
self.tank.set_path("left")
if event.key == K_d:
self.tank.set_path("right")
if event.type == KEYUP:
if event.key == K_w:
self.tank.unset_path("up")
if event.key == K_s:
self.tank.unset_path("down")
if event.key == K_a:
self.tank.unset_path("left")
if event.key == K_d:
self.tank.unset_path("right")
self.tank.set_speed()
# create bullets if mouse button was pressed
if event.type == MOUSEBUTTONDOWN:
bul = Bullet(speed=BULLETSPEED)
bul.moveto(
(self.tank.rect.centerx, (self.tank.rect.top - bul.size[1]))
) # move the bullet to the front of the tank
# math stuff to calculate trajectory
mouse_pos = pygame.mouse.get_pos()
h = mouse_pos[1] - bul.rect.center[1]
w = mouse_pos[0] - bul.rect.center[0]
hyp = math.sqrt(h ** 2 + w ** 2)
vertical_speed = (
BULLETSPEED[1] * (h / hyp) if hyp != 0 else BULLETSPEED[1] * h
)
horizontal_speed = (
BULLETSPEED[0] * (w / hyp) if hyp != 0 else BULLETSPEED[0] * w
)
# set the bullet's speed and add it to self.bullets (for
# future collision checking and displaying)
bul.set_speed((horizontal_speed, vertical_speed))
self.bullets.append(bul)