简体   繁体   中英

How do I make my sprites move (the enemy sprites), by themselves?

I need to make my enemy sprites defined in drawGrid function move, I would like them to move to the edge of the grid (horizontally) and once reaching the end of the grid, they move down a certain amount and begin moving the opposite horizontal direction. (Like the aliens in space invaders.)

I have tried to move them to no avail.

import pygame
pygame.init()

window = pygame.display.set_mode((650, 630))

pygame.display.set_caption("PeaShooters")

avatar = pygame.image.load('Sprite 1 Red.png')
enemy1 = pygame.image.load('Sprite 3 Red.png')
enemy2 = pygame.image.load('Sprite 3 Yellow.png')
background = pygame.image.load('Bg.jpg')
white = (255, 255, 255)

class player(object):
    def __init__(self, x, y, width, height, shots):
        self.x = 300
        self.y = 500
        self.width = 40
        self.height = 60
        self.vel = shots


def drawGrid():
    window.blit(background, (0,0))
    window.blit(enemy1, (en.x, en.y))
    window.blit(enemy1, (200, en.y))
    window.blit(enemy1, (100, en.y))
    window.blit(enemy1, (400, en.y))
    window.blit(enemy1, (500, en.y))
    window.blit(enemy1, (en.x, en.y))
    window.blit(enemy2, (50, 200))
    window.blit(enemy2, (150, 200))
    window.blit(enemy2, (250, 200))
    window.blit(enemy2, (350, 200))
    window.blit(enemy2, (450, 200))
    window.blit(enemy2, (530, 200))
    window.blit(avatar, (av.x, av.y))
    pygame.draw.line(window, white, [50,50], [50, 600], 5)
    pygame.draw.line(window, white, [50,50], [600, 50], 5)
    pygame.draw.line(window, white, [600,600], [600, 50], 5)
    pygame.draw.line(window, white, [50,600], [600, 600], 5)
    pygame.draw.line(window, white, [50,450], [600, 450], 5)

    for bullet in bullets:
        bullet.draw(window)
    pygame.display.update()


class shots(object):
    def __init__(self, x, y, radius, colour):
        self.x = x
        self.y = y
        self.radius = radius
        self.colour = colour
        self.vel = shots

    def draw(self, window):
        pygame.draw.circle(window, self.colour, (self.x,self.y), self.radius)

class enemy(object):

    def __init__(self, x, y, width, height, end):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.end = end
        self.path = [self. x, self.end]
        self.vel = 4

    def draw(self,window):
        self.move()

    def move(self):
        pass

av = player(300, 500, 40, 60, 9)
en = enemy(300, 100, 40, 60, 500)

bullets = []
running = True
while running:
    pygame.time.delay(100) 

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    for bullet in bullets:
        if bullet.y < 600 and bullet.y > 70:
            bullet.y -= 8
        else:
            bullets.pop(bullets.index(bullet))

    keys = pygame.key.get_pressed()

    if keys[pygame.K_w] and av.y > 440:
        av.y -= av.vel

    if keys[pygame.K_a] and av.x > 65:
        av.x -= av.vel

    if keys[pygame.K_s] and av.y < 535:
        av.y += av.vel

    if keys[pygame.K_d] and av.x < 530:
        av.x += av.vel

    if keys[pygame.K_SPACE]:
        if len(bullets) < 5:
            bullets.append(shots(round(av.x + av.width//2), round(av.y + av.height//2), 6, (0,0,0)))


    drawGrid()

window.blit(enemy, (en.x, en.y))
window.blit(avatar, (x,y))

pygame.quit()

I expect the enemies to move, in the way I have described but they do not move at all.

Here are a few things I would do to get yourself closer to the game you are looking for:

  1. Create more enemy objects

Right now you create 1 enemy here: en = enemy(300, 100, 40, 60, 500) and just throw enemy images at the screen in a bunch of places. To really make use of the Enemy class, I would do something like this:

enemiesList = []
for i in range (MAX_ENEMIES_I_WANT):
    enemiesList.append(enemy(300, 100*i, 40, 60, 500))

Note: you will need to define where you place your enemies differently if you want them to fall into a certain shape. For the traditional space invaders you can loop twice, once for y coordinates, once for x.

  1. blit your enemies from within your Enemy class

Instead of doing things the way you have them now, I would add a blitToScreen() method in your Enemy class. Something like:

def BlitToScreen(self,screen):
    screen.blit(self.sprite, self.x, self.y)

Then, blitting all of your enemies to screen becomes two lines:

for e in enemiesList:
    e.BlitToScreen(window)
  1. Finally getting to your question, if we are truly mimicking space invaders, each enemy moves the exact same amount each frame so we can make the same changes to each Enemy object.

If you have a frame number it will look something like this:

tomove = [0,0]
if(frame%100 == 99):
    tomove[0] += 50 # Move down
else if(frame/100%2 == 0):
    tomove[1] +=50 # Move right
else:
    tomove[1] -=50 # Move left

for e in enemies:
    e.x += tomove[0]
    e.y += tomove[1]

Hopefully this is helpful for you!

@Hoog's answer mostly covers the basics of what's wrong with the OP's code, and how to get it working.

However the OP really should consider using the PyGame sprite class for each "enemy" object (well, all objects actually). Yes , it's a bit more work to setup initially, but it provides so much extra (pre-optimised) functionality that you would otherwise have to code. This takes some of the tedious work out of the programming, and allows you to spend more time coding up an interesting game.

It's fairly trivial to convert the existing objects to Sprites:

class Enemy( pygame.sprite.Sprite ):
    def __init__( self, bitmap, x, y, end=None ):
        pygame.sprite.Sprite.__init__( self )
        self.image  = bitmap
        self.rect   = bitmap.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.vel    = 4
        self.end    = end

   def update():
       # TODO - handle any sort of sprite movement

Create a bunch of sprites, as per @Hoog's suggestion, except use a Sprite Group to hold them:

enemy_image1 = pygame.image.load('Sprite 3 Red.png')
enemy_image2 = pygame.image.load('Sprite 3 Yellow.png')

...

enemies_list = pygame.sprite.Group()
for j in range( ENEMY_LINE_COUNT ):
    for i in range( ENEMY_PER_LINE_COUNT ):
         enemy_x = 50 + ( i * 30 )
         enemy_y = 50 + ( j * 40 )
         enemies_list.append( Enemy( enemy_image1, enemy_x, enemy_y ) )

The Moving and Drawing can then be handled easily in your main loop, using the abilities (and ease) of the Sprite Group:

while running:
    ...
    enemies_list.update()        # re-position the sprites
    enemies_list.draw( window )  # draw the entire sprite-group to the window
    ...

The Sprite Group .update() call (in the main loop) will call the Enemy.update() function for every sprite contained in the group.

This brings us to the movement part of your question. Let's think about how Space Invaders move - Right to Left, Down, Left to Right, Down, More Speed, repeat ... This kind of sounds like a program to me ;)

So I propose to control movement, write a program that each invader follows:

# In Enemy.__init__() ~
self.move_program = 'rrrrrrrrrrrrrdllllllllllllds'  #TODO - make better
self.move_cursor  = 0

Where each letter of the self.move_program effects the movement at each 'tick'. This leads us to the sprite's .update() function:

# In Enemy class ~
def update( self ):
    move_instruction = self.move_program[ self.move_cursor ]
    self.move_cursor += 1
    # If the movement program has ended, restart
    if ( self.move_cursor == len( self.move_program ) ):
        self.move_cursor = 0
    # Interpret the movement instruction
    if ( instruction == 'r' ):   # right
        self.rect.x = self.rect.x + 10
    elif ( instruction == 'l' ): # left
        self.rect.x = self.rect.x - 10
    elif ( instruction == 'd' ): # down
        self.rect.y = self.rect.y + 10
    elif ( instruction == 's' ): # speedup
        self.vel += 1

Obviously you can have any sort of instructions you like. Different sets of instructions could be given to different sprites as part of the constructor.

Putting the game projectiles and player(s) into sprites will allow the code to easily find collisions, etc. but that's another answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM