2

For a school project, I need to finish up the pygame program below by implementing a way of shooting Kunais/Shurikens towards the mouse position, to be able to hit the enemy sprite.

import pygame
import math
import random
from pygame.locals import * 


pygame.init()
fenetre = pygame.display.set_mode((640,480), RESIZABLE) 
pause = False

class perso():
  def __init__(self,image,x=0,y=0,directionX=1,directionY=1):
    self.image = pygame.image.load(image).convert_alpha()
    self.x = x
    self.y = y
    self.directionX = directionX
    self.directionY = directionY

  def move(self):
      if self.x==0:
        self.directionX=1
      if self.x==640-60:
        self.directionX=-1
      if self.y==0:
        self.directionY=1
      if self.y==480-100:
        self.directionY=-1
      self.x+=self.directionX
      self.y+=self.directionY


fond = pygame.image.load("background.png").convert_alpha() 

naruto = perso("naruto.png")
tobi = perso("tobi.png", 250, 100)
kunai = perso("kunai.png")

kunai.x = naruto.x + 40
kunai.y = naruto.y + 70

continuer = True

pygame.key.set_repeat(100, 25)

myfont = pygame.font.SysFont("monospace", 40)
colorRed = (255, 0, 0)
colorBlack = (0, 0, 0)
colorWhite = (255, 255, 255)
colorBlue = (0, 0, 255)

vieNaruto = 10
vieTobi = 3
result = 0


while continuer:

  if int(naruto.x) >= int(tobi.x) and int(naruto.x) < int(tobi.x + 60) and int(naruto.y) >= int(tobi.y) and int(naruto.y) < int(tobi.y + 105):
    vieNaruto -= 1
    naruto.x = 0
    naruto.y = 0

  if int(naruto.x + 50) >= int(tobi.x) and int(naruto.x + 50) < int(tobi.x + 60) and int(naruto.y) >= int(tobi.y) and int(naruto.y) < int(tobi.y + 105):
    vieNaruto -= 1
    naruto.x = 0
    naruto.y = 0

  if int(naruto.x) >= int(tobi.x) and int(naruto.x) < int(tobi.x + 60) and int(naruto.y + 105) >= int(tobi.y) and int(naruto.y + 105) < int(tobi.y + 105):
    vieNaruto -= 1
    naruto.x = 0
    naruto.y = 0

  if int(naruto.x + 50) >= int(tobi.x) and int(naruto.x + 50) < int(tobi.x + 60) and int(naruto.y + 105) >= int(tobi.y) and int(naruto.y + 105) < int(tobi.y + 105):
    vieNaruto -= 1
    naruto.x = 0
    naruto.y = 0

  if vieNaruto <= 0:
    result = 1
    continuer = False

  if vieTobi == 0:
    result = 2
    continuer = False

  textVieNaruto = myfont.render("Vie Naruto : "+str(vieNaruto), True, colorRed)

  textVieTobi = myfont.render("Vie Tobi : "+ str(vieTobi), True, colorRed)

  for event in pygame.event.get():

    """GESTION DU CLAVIER"""

    if event.type == QUIT:
      continuer = False 
    elif event.type == KEYDOWN:
      if event.key == K_SPACE:
        pause = True
        while pause == True:
          for event in pygame.event.get():
            if event.type == KEYDOWN:
                if event.key==K_SPACE:
                    pause = False
      if event.key == K_UP and 5 <= naruto.y:
        naruto.y -= 5
      if event.key == K_DOWN and naruto.y <= 375:
        naruto.y += 5
      if event.key == K_LEFT and 5 <= naruto.x :
        naruto.x -= 5
      if event.key == K_RIGHT and naruto.x <= 575:
        naruto.x += 5



  tobi.move()
  tobi.move() 
  fenetre.blit(fond,(0,0))
  fenetre.blit(textVieNaruto, (400,0))
  fenetre.blit(textVieTobi, (400,30))
  fenetre.blit(naruto.image,(naruto.x,naruto.y))
  fenetre.blit(tobi.image,(tobi.x,tobi.y))

  pygame.time.Clock().tick(30)  
  pygame.display.update() 


if result == 1:
  while True :
    fond = pygame.image.load("Defeat.png").convert_alpha()
    fenetre.blit(fond,(0,0))
    pygame.display.update()

if result == 2:
  while True :
    fond = pygame.image.load("Victory.png").convert_alpha()
    fenetre.blit(fond,(0,0))
    pygame.display.update()

The extra png files are visible on : https://repl.it/@LeVeveysan/ISNNaruto

Can anyone help me out on this ? I know how to get the mouse position but I can't get a class to work properly.

Sincirely

sloth
  • 99,095
  • 21
  • 171
  • 219
  • Have you tried the Game Development Community? https://gamedev.stackexchange.com/ – Spainey Jan 10 '20 at 08:58
  • Also, it might not be best to state this is to help with your homework/school project. That tends to put some people off helping, unfortunately. Instead, I would suggest you consider which part of your code you are really struggling with and word your question more about how to get that aspect of your code working. Perhaps there might already be an answer out there if you search online? For example, this link might be what you're looking for: https://stackoverflow.com/questions/16288905/make-a-sprite-move-to-the-mouse-click-position-step-by-step – Spainey Jan 10 '20 at 09:04

1 Answers1

5

Let's discard everything and start from scratch and make use of pygame features like sprites and vector math.

We begin with a basic skeleton of a pygame game, a simple window:

import pygame

def main():
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
        screen.fill(pygame.Color('grey'))
        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()

enter image description here

Our game will have different scenes (title scene, game scene, game-over scene), so let's implement them now:

import pygame
import pygame.freetype 

pygame.init()
FONT = pygame.freetype.SysFont(None, 32)

class SimpleScene:
    def __init__(self, text, background, next_scene):
        if background:
            self.background = pygame.image.load(background).convert()
        else:
            self.background = pygame.Surface((640, 480))
            self.background.fill(pygame.Color('white'))

        if text:
            FONT.render_to(self.background, (100, 200), text, pygame.Color('black'))
            FONT.render_to(self.background, ( 99, 199), text, pygame.Color('red'))

        self.next_scene = next_scene

    def start(self):
        pass

    def draw(self, screen):
        screen.blit(self.background, (0, 0))

    def update(self, events, dt):
        for event in events:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    return self.next_scene

class Game:
    def __init__(self):
        self.background = pygame.image.load('background.png').convert()

    def start(self):
        pass

    def draw(self, screen):
        screen.blit(self.background, (0, 0))

    def update(self, events, dt):
        for event in events:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    return 'VICTORY'

def main():
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    scenes = {
        'TITLE': SimpleScene('PRESS SPACE TO START', 'background.png', 'GAME'),
        'GAME': Game(),
        'VICTORY': SimpleScene('YOU WIN!', None, 'TITLE'),
        'DEFEAT': SimpleScene('YOU LOSE!', None, 'TITLE'),
    }
    scene = scenes['TITLE']
    dt = 0
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        next_scene = scene.update(events, dt)
        if next_scene:
            scene = scenes[next_scene]
            scene.start()

        scene.draw(screen)
        pygame.display.flip()
        dt = clock.tick(60)

if __name__ == '__main__':
    main()

enter image description here

This allows us to cycle through the game scenes by pressing space.

Now let's implement the core game. First, we need some sprites, so let's create an Actor class and prepare the game scene to display and reset our sprites. We use some of pygame's basic stuff, like the Sprite class.

...
class Actor(pygame.sprite.Sprite):
    def __init__(self, image, pos, direction):
       super().__init__()
       self.image = image
       self.rect = self.image.get_rect(center=pos)
       self.pos = pygame.Vector2(*pos)
       self.direction = pygame.Vector2(*direction)

class Game:
    def __init__(self):
        self.background = pygame.image.load('background.png').convert()
        self.images = {
            'tobi': pygame.image.load('tobi.png').convert_alpha(),
            'naruto': pygame.image.load('naruto.png').convert_alpha(),
            'kunai': pygame.image.load('kunai.png').convert_alpha()
        }
        self.sprites = pygame.sprite.Group()

    def start(self):
        self.sprites.empty()
        self.sprites.add(Actor(self.images['naruto'], (50, 150), (0, 0)))
        self.sprites.add(Actor(self.images['tobi'], (450, 300), (0, 0)))

    def draw(self, screen):
        screen.blit(self.background, (0, 0))
        self.sprites.draw(screen)

    def update(self, events, dt):
        for event in events:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    return 'VICTORY'
        self.sprites.update()
...

enter image description here

Time for some action. Let's implement some behaviour in our actors. We create function of each different kind of behaviour we want. One for Tobi to move along the screen, one for Naruto to be controlled by the keyboard, and one for the kunais.

Since we use the Vector class for the position and direction of our sprites, is simply a matter of substraction to make the kunai move towards the mouse position.

Here's the full code:

import pygame
import pygame.freetype 

pygame.init()
FONT = pygame.freetype.SysFont(None, 32)

class SimpleScene:
    def __init__(self, text, background, next_scene):
        if background:
            self.background = pygame.image.load(background).convert()
        else:
            self.background = pygame.Surface((640, 480))
            self.background.fill(pygame.Color('white'))

        if text:
            FONT.render_to(self.background, (100, 200), text, pygame.Color('black'))
            FONT.render_to(self.background, ( 99, 199), text, pygame.Color('red'))

        self.next_scene = next_scene

    def start(self):
        pass

    def draw(self, screen):
        screen.blit(self.background, (0, 0))

    def update(self, events, dt):
        for event in events:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    return self.next_scene

def tobi_ai(self, dt):
    self._update_pos(dt)

    # change direction when hitting the edge of the screen
    display = pygame.display.get_surface().get_rect()
    if self.rect.bottom > display.bottom or self.rect.top < 0: self.direction.y *= -1
    if self.rect.right > display.right or self.rect.left < 0: self.direction.x *= -1

    self._keep_on_screen()

def player_ai(self, dt):

    # alter direction if arrow keys are pressed
    self.direction = pygame.Vector2()
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP] or pressed[pygame.K_w]: self.direction += (0, -1)
    if pressed[pygame.K_DOWN] or pressed[pygame.K_s]: self.direction += (0, 1)
    if pressed[pygame.K_LEFT] or pressed[pygame.K_a]: self.direction += (-1, 0)
    if pressed[pygame.K_RIGHT] or pressed[pygame.K_d]: self.direction += (1, 0)

    self._update_pos(dt)
    self._keep_on_screen()

def kunai_ai(self, dt):
    self._update_pos(dt)
    display = pygame.display.get_surface().get_rect()

    # just fly around and die if out of screen
    if not display.contains(self.rect):
        self.kill()

class Actor(pygame.sprite.Sprite):
    def __init__(self, image, pos, direction=(0, 0), speed=20, behaviour=None, rotate=False):
       super().__init__()
       self.image = image
       self.rect = self.image.get_rect(center=pos)
       # using vectors for position and direction makes it easy to calculate movement and rotation
       self.pos = pygame.Vector2(*pos)
       self.direction = pygame.Vector2(*direction)
       if rotate:
           self.image = pygame.transform.rotate(self.image, self.direction.angle_to(pygame.Vector2(1,0)))
       self.speed = speed
       self.behaviour = behaviour

    def update(self, dt):
        if self.behaviour:
            self.behaviour(self, dt)

    def _update_pos(self, dt):
        if self.direction.length() > 0:
            self.pos = self.pos + (self.direction.normalize() * self.speed * dt/100)
            self.rect.center = int(self.pos.x), int(self.pos.y)

    def _keep_on_screen(self):
        self.rect.center = self.pos
        display = pygame.display.get_surface().get_rect()
        self.rect.clamp_ip(display)
        self.pos.x, self.pos.y = self.rect.center

class Game:
    def __init__(self):
        self.background = pygame.image.load('background.png').convert()
        self.images = {
            'tobi': pygame.image.load('tobi.png').convert_alpha(),
            'naruto': pygame.image.load('naruto.png').convert_alpha(),
            'kunai': pygame.image.load('kunai.png').convert_alpha()
        }
        self.sprites = pygame.sprite.Group()
        self.kunais = pygame.sprite.Group()

    def start(self):
        self.sprites.empty()
        self.kunais.empty()
        self.naruto = Actor(self.images['naruto'], (50, 150), behaviour=player_ai)
        self.tobi = Actor(self.images['tobi'], (450, 300), (1, 1), behaviour=tobi_ai)
        self.sprites.add(self.naruto)
        self.sprites.add(self.tobi)
        self.player_lives = 10
        self.enemy_lives = 3

    def draw(self, screen):
        screen.blit(self.background, (0, 0))
        self.sprites.draw(screen)
        FONT.render_to(screen, (430, 10), 'Naruto:' , pygame.Color('red'))
        FONT.render_to(screen, (550, 10), str(self.player_lives), pygame.Color('red'))
        FONT.render_to(screen, (430, 50), 'Tobi:', pygame.Color('red'))
        FONT.render_to(screen, (550, 50), str(self.enemy_lives), pygame.Color('red'))

    def update(self, events, dt):
        for event in events:
            if event.type == pygame.MOUSEBUTTONDOWN:
                kunai = Actor(self.images['kunai'], 
                               self.naruto.pos, 
                               event.pos-self.naruto.pos, 
                               speed=35, 
                               behaviour=kunai_ai,
                               rotate=True)
                self.sprites.add(kunai)
                self.kunais.add(kunai)

        self.sprites.update(dt)

        # kunai hits tobi
        # we use https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollide 
        # for collition detection
        for sprite in pygame.sprite.spritecollide(self.tobi, self.kunais, True):
            self.enemy_lives -= 1
            if self.enemy_lives <= 0:
                return 'VICTORY'

        # tobi hits naruto
        if self.tobi.rect.colliderect(self.naruto.rect):
            self.player_lives -= 1
            if self.player_lives <= 0:
                return 'DEFEAT'
            self.naruto.pos = pygame.Vector2(50, 150)
            self.tobi.pos.x = max(self.tobi.pos.x, 400)

def main():
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    scenes = {
        'TITLE': SimpleScene('PRESS SPACE TO START', 'background.png', 'GAME'),
        'GAME': Game(),
        'VICTORY': SimpleScene('YOU WIN!', None, 'TITLE'),
        'DEFEAT': SimpleScene('YOU LOSE!', None, 'TITLE'),
    }
    scene = scenes['TITLE']
    dt = 0
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        next_scene = scene.update(events, dt)
        if next_scene:
            scene = scenes[next_scene]
            scene.start()

        scene.draw(screen)

        pygame.display.flip()
        dt = clock.tick(60)

if __name__ == '__main__':
    main()

enter image description here

Now we have a simple game that's replayable and easily extendable. Feel free to use this code for whatever you like.

sloth
  • 99,095
  • 21
  • 171
  • 219