Перемещение спрайтов по экрану - PullRequest
0 голосов
/ 06 декабря 2018

Итак, я вернулся к созданию игры, и я программировал в механике игрового процесса (это пуля-уклонение, я делал пули) Пули должны продолжать генерироваться из верхнего спрайта (Minty) и продолжать распространятьсявне (поверьте мне, я потратил много времени на чтение и поиск в Google). Вот как это выглядело : (верхний спрайт - противник, маленький белый квадрат - игрок,кстати, фиолетовые круги - это пули)

как и должно быть:

enter image description here

но вот как это так:

enter image description here

И я просто не понимаю, почему это происходит?

Вот мой код:

import sys
import time
import pygame
from pygame.locals import *

pygame.init()
#INITIALISE THE WINDOW.
#CONSTANTS ARE CAPITAL, VARIABLES ARE LOWERCASE
SCREENWIDTH = 1000
SCREENHEIGHT = 650
SCREENSIZE = [SCREENWIDTH, SCREENHEIGHT]
screen = pygame.display.set_mode(SCREENSIZE)
BG_COL = [255, 123, 67]
S1_COL = (0, 255, 188)
clock = pygame.time.Clock()
screen.fill(BG_COL)
pygame.draw.rect(screen, S1_COL,(50, 50, 900, 575), 0)
pygame.display.update()
clock.tick(60)

class Player(pygame.sprite.Sprite):
    def __init__(self, sprite):
        self.sprite = sprite
        self.x = 445
        self.y = 550

class Opponent(pygame.sprite.Sprite):
    def __init__(self, sprite):
        self.sprite = sprite
        self.x = 445
        self.y = 30

class Bullet:
    def __init__(self, sprite, length, width):
        self.sprite = sprite
        self.x = 460
        self.y = 50
        self.length = length
        self.width = width
        self.area = self.sprite.get_rect(x=self.x, y=self.y)
        self.area.clamp_ip((50, 50, 900, 575))

class BulletGroup(pygame.sprite.Group):
    def __init__(self, typeof, numof):
        self.typeof = typeof
        self.numof = numof
        self.listof = []
        for i in range(0, self.numof):
            self.listof.append(typeof)

player = Player(pygame.image.load("Sprites/player.png"))
Minty = Opponent(pygame.image.load("Sprites/minty.png"))
purple_glow = Bullet(pygame.image.load("Sprites/purple-glowey.png"), 70, 65)
test_bullets = BulletGroup(purple_glow, 5)

#make functions
def background(colour): #to make it easier to draw the background each time
    screen.fill(BG_COL)
    pygame.draw.rect(screen, colour,(50, 50, 900, 575), 0)

def handle_keys():
    """ Handles Keys """
    key = pygame.key.get_pressed()
    dist = 2 
    if key[pygame.K_DOWN]: # down key
        player.y += dist # move down
    elif key[pygame.K_UP]: # up key
        player.y -= dist # move up
    if key[pygame.K_RIGHT]: # right key
        player.x += dist # move right
    elif key[pygame.K_LEFT]: # left key
        player.x -= dist # move left

#MAIN GAME LOOP
running = True
while running:   
    for events in pygame.event.get():
        if events.type == QUIT:
            pygame.quit()
            exit()
            running = False
        if events.type == KEYDOWN:
            background(S1_COL)

    #BULLETS:
    def move_bullets(bullets, xchange, ychange):
        #MOVE THE CLONED BULLETS IN ONE CONSTANT DIRECTION
        for b in bullets.listof:
            b.x += xchange
            b.y += ychange
            screen.blit(b.sprite, (b.x, b.y))
            pygame.draw.rect(screen, S1_COL, b.area, 0)
            xchange += 10
            pygame.time.delay(50)

    #STAGE1 MAKE
            screen.blit(pygame.transform.scale(Minty.sprite, (130, 140)), [Minty.x, Minty.y])
    for events in pygame.event.get():
        screen.blit(pygame.transform.scale(Minty.sprite, (130, 140)), [Minty.x, Minty.y]) #i'm going to change image size on phone later on


    print(test_bullets.listof[0].x)
    print(test_bullets.listof[0].y)
    #IF KEY PRESSED
    #MOVE PLAYER SPRITE
    screen.blit(player.sprite, (player.x, player.y))
    handle_keys()
    move_bullets(test_bullets, -10, 10)
    pygame.display.update()

Любойобратная связь, объяснение, редактирование подсказок или что-то подобное будет с благодарностью.

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Вы должны использовать класс Sprite по назначению: определить хотя бы свойство image и rect и поместить логику каждого игрового объекта в метод update.Не нужно обрабатывать рисование самостоятельно.

Тогда ваш основной цикл станет очень четким и классическим циклом из трех частей, в котором вы по порядку сделаете следующее:

  • обработка событий
  • обновить состояние игры
  • нарисовать все

Вот как это может выглядеть (обратите внимание на комментарии, где я объясняю некоторые вещи):

import pygame
import itertools
pygame.init()

SCREENWIDTH = 1000
SCREENHEIGHT = 650
screen = pygame.display.set_mode([SCREENWIDTH, SCREENHEIGHT])

screen.fill((255, 123, 67))
pygame.draw.rect(screen, (0, 255, 188), (50, 50, 900, 575), 0)
# let's create a background surface that we can reuse instead of
# drawing manually to the screen
background = screen.copy()

clock = pygame.time.Clock()

class Player(pygame.sprite.Sprite):

    # the player sprite is constant, so let's use a class variable
    sprite = pygame.image.load("Sprites/player.png")

    def __init__(self, *groups):
        # we want to use sprites, so we have to call __init__ of the Sprite class
        # groups is a list of groups we want this sprite to add to
        super().__init__(*groups)
        # the image of the sprite needs to be in an attribute called 'image'
        self.image = Player.sprite
        # for pygame to know where to draw the sprite, we need a 'rect' attribute
        self.rect = self.image.get_rect(topleft=(445, 550))

    def update(self):
        # moving by keyboard is unique to the player class, so let's to this here
        key = pygame.key.get_pressed()
        dist = 3
        # we just update our rect's position to move the sprite
        # we should use vectors here, too, but for now this is good enough
        if key[pygame.K_DOWN]:
            self.rect.y += dist
        elif key[pygame.K_UP]:
            self.rect.y -= dist
        if key[pygame.K_RIGHT]:
            self.rect.x += dist
        elif key[pygame.K_LEFT]:
            self.rect.x -= dist

class Opponent(pygame.sprite.Sprite):

    def __init__(self, sprite, *groups):
        super().__init__(*groups)
        self.image = sprite
        self.rect = self.image.get_rect(topleft=(445, 30))
        # we keep an additional attribute 'pos' to store the postion
        # it's a vector so we can use some vector math
        self.pos = pygame.Vector2(self.rect.topleft)
        # we want to move the Opponent in a specific pattern
        # so let's keep a list of points we want to move to
        # 'cycle' will generate and "endless loop" of this points
        self.path = itertools.cycle(((445, 30), (345, 235), (90, 115), (490, 80), (850, 250), (745, 110)))
        # we can use 'next' to get the next position
        self.next_point = pygame.Vector2(next(self.path))
        # maybe we want to change the speed of the opponent
        self.speed = 1
        # we use ticks to store the milliseconds since the game started later
        # this allows us to do thing over time
        self.ticks = 1000
        # a list of bullets we want to shoot later
        self.queue = []

    def update(self):
        # so we want to move to a specific point
        # we use some vector math to get the direction we have to move to
        move = self.next_point - self.pos
        move_length = move.length()
        if move_length != 0:
            # since 'move' is the vector between 'pos' and 'next_point'
            # we have to normalize it so it just points into the right
            # direction at a certain length instead of all the way from
            # 'pos' and 'next_point'
            move.normalize_ip()
            move = move * self.speed
            self.pos += move

        # if we are already at the target position (or overshoot it)
        # we take the next position from 'path'
        if move.length() == 0 or move_length < self.speed:
            self.next_point = pygame.Vector2(next(self.path))

        # we update the 'rect' position so pygame draws the sprite at the right position on the screen
        self.rect.topleft = self.pos

        # so let's count some time. Every 3000ms passed, we want to shoot some bullets
        if pygame.time.get_ticks() - self.ticks > 3000:
            self.ticks = pygame.time.get_ticks()
            self.shoot()

        # see how much time passed since the last shooting
        time_gone = pygame.time.get_ticks() - self.ticks
        for bullet in self.queue:
            # the first value of the tuples in the 'queue' describes when to shoot the bullet
            if bullet[0] <= time_gone:
                # create the bullet and add them to the 'sprites' and 'bullets' groups
                # 'bullets' isn't used yet, but you can use it later for collision detection
                Bullet(self.rect.center, bullet[1], sprites, bullets)

        # now remove all bullets that have been fired
        self.queue = [bullet for bullet in self.queue if bullet[0] > time_gone]

    def shoot(self):
        bullet_speed = 4
        # this list describes the pattern of the attack
        # the first value is the time when to shoot the bullet (afer X ms)
        # the second value is the movement vector of the bullet
        pattern = ((0, pygame.Vector2(-0.5, 1) * bullet_speed),
            (0, pygame.Vector2( 0, 1) * bullet_speed),
            (0, pygame.Vector2(0.5, 1) * bullet_speed),
            (150, pygame.Vector2(-0.5, 1) * bullet_speed),
            (150, pygame.Vector2( 0, 1) * bullet_speed),
            (150, pygame.Vector2(0.5, 1) * bullet_speed),
            (300, pygame.Vector2(-0.5, 1) * bullet_speed),
            (300, pygame.Vector2( 0, 1) * bullet_speed),
            (300, pygame.Vector2(0.5, 1) * bullet_speed))
        self.queue = pattern

class Bullet(pygame.sprite.Sprite):

    sprite = pygame.image.load("Sprites/purple-glowey.png")

    def __init__(self, pos, direction, *groups):
        super().__init__(*groups)
        self.image = Bullet.sprite
        self.rect = self.image.get_rect(topleft=pos)
        self.direction = direction
        self.pos = pygame.Vector2(self.rect.topleft)

    def update(self):
        # just move along the direction 
        self.pos += self.direction
        self.rect.topleft = (self.pos.x, self.pos.y)
        # if no longer on screen, remove from all groups
        if not screen.get_rect().colliderect(self.rect):
            self.kill()

sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()

player = Player(sprites)
Minty = Opponent(pygame.image.load("Sprites/minty.png"), sprites)

def main():        
    running = True
    while running:   
        for events in pygame.event.get():
            if events.type == pygame.QUIT:
                return

        # update all sprites, a.k.a. game logic
        sprites.update()

        # draw everything
        screen.blit(background, (0, 0))
        sprites.draw(screen)
        pygame.display.update()

        clock.tick(60)

if __name__ == '__main__':
    main()

enter image description here

0 голосов
/ 06 декабря 2018

Ваша проблема в классе BulletGroup: вы продолжаете добавлять один и тот же объект в список, поэтому вместо того, чтобы иметь список из 5 маркеров, как вы ожидаете, у вас фактически есть список из 5 ссылок на один и тот же элемент.

Когда вы пробегаете по этому списку и меняете координаты, вы меняете ту же самую пулю и рисуете ту же самую пулю.

Вы должны использовать copy, чтобы дублировать вашу первуюпуля.

...