player walking on predetermined path pygame

2019-01-27 07:49发布

I am new to pygame and I am trying to make a game where the player has to bypass some enemy's to get to a point where you can go to the next level. I need the enemy's to walk back and forward on a predetermined path but I can't figure out how to do it. So I was wondering if there is an easy way to do this?

This is my code.

import pygame
import random
import os
import time
from random import choices
from random import randint

pygame.init()
a = 0
b = 0
width = 1280
height = 720
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Game")
done = False
n = 0
x = 0
y = 0
x_wall = 0
y_wall = 0
clock = pygame.time.Clock()
WHITE = (255,255,255)
RED = (255,0,0)
change_x = 0
change_y = 0
HW = width / 2
HH = height / 2
background = pygame.image.load('mountains.png')

#player class
class Player(pygame.sprite.Sprite):
    def __init__(self):
      pygame.sprite.Sprite.__init__(self)
      self.image = pygame.image.load("character.png") 
      self.rect = self.image.get_rect()
      self.rect.x = width / 2
      self.rect.y = height / 2

#enemy class
class Enemy(pygame.sprite.Sprite):
  def __init__(self):
    pygame.sprite.Sprite.__init__(self)
    self.image = pygame.image.load("enemy.png")
    self.image = pygame.transform.scale(self.image, (int(50), int(50)))
    self.rect = self.image.get_rect()
    self.rect.x = width / 3
    self.rect.y = height / 3

#wall class
class Wall(pygame.sprite.Sprite):
  def __init__(self, x, y):
    pygame.sprite.Sprite.__init__(self)
    self.image = pygame.image.load("wall.png") 
    self.image = pygame.transform.scale(self.image, (int(50), int(50)))
    self.rect = self.image.get_rect()
    self.rect.x = x
    self.rect.y = y

#wall movement
  def update(self):
    self.vx = 0
    self.vy = 0
    key = pygame.key.get_pressed()
    if key[pygame.K_LEFT]:
      self.vx = 5
      self.vy = 0
    elif key[pygame.K_RIGHT]:
      self.vx = -5
      self.vy = 0
    if key[pygame.K_UP]:
      self.vy = 5
      self.vx = 0
    elif key[pygame.K_DOWN]:
      self.vy = -5
      self.vx = 0
    self.rect.x = self.rect.x + self.vx
    self.rect.y = self.rect.y + self.vy

#player sprite group
sprites = pygame.sprite.Group()
player = Player()
sprites.add(player)

#enemy sprite group
enemys = pygame.sprite.Group()
enemy = Enemy()
enemy2 = Enemy()
enemys.add(enemy, enemy2)

#all the wall sprites
wall_list = pygame.sprite.Group()
wall = Wall(x_wall, y_wall)
wall2 = Wall((x_wall + 50), y_wall)
wall3 = Wall((x_wall + 100), y_wall)
wall4 = Wall((x_wall + 150), y_wall)
wall5 = Wall((x_wall + 200), y_wall)
wall6 = Wall((x_wall + 250), y_wall)


#add all the walls to the list to draw them later
wall_list.add(wall, wall2, wall3, wall4, wall5, wall6)

#add all the walls here to fix the collision
all_walls = (wall, wall2, wall3, wall4, wall5, wall6)

while not done:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

    sprites.update()
    wall_list.update()
    enemys.update()

    #collision between player and and walls
    if player.rect.collidelist(all_walls) >= 0:
      print("Collision !!")
      player.rect.x = player.rect.x - player.vx
      player.rect.y = player.rect.y - player.vx

    #fill the screen
    screen.fill((0, 0, 0))
    #screen.blit(background,(x,y))

    #draw the sprites
    sprites.draw(screen)
    wall_list.draw(screen)
    enemys.draw(screen)

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

pygame.quit()

Here is the download link with the images if you want to run it: https://geordyd.stackstorage.com/s/hZZ1RWcjal6ecZM

2条回答
小情绪 Triste *
2楼-- · 2019-01-27 08:24

Use a list to have him walk back and forth.

class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("enemy.png")
        self.image = pygame.transform.scale(self.image, (int(50), int(50)))
        self.rect = self.image.get_rect()
        self.rect.x = width / 3
        self.rect.y = height / 3
        #x or y coordinates
        self.list=[1,2,3,4,5]
        self.index=0

    def update(self):
        # patrol up and down or left and right depending on x or y
        if self.index==4:
            #reverse order of list
            self.list.reverse()
            self.index=0
        #set the x position of the enemy according to the list
        self.rect.x=self.list[self.index] 
        self.index+=1
查看更多
该账号已被封号
3楼-- · 2019-01-27 08:35

I'd give the sprite a list of points (self.waypoints) and assign the first one to a self.target attribute.

In the update method I subtract the self.pos from the self.target position to get a vector (heading) that points to the target and has a length equal to the distance. Scale this vector to the desired speed and use it as the velocity (which gets added to the self.pos vector each frame) and the entity will move towards the target.

When the target is reached, I just increment the waypoint index and assign the next waypoint in the list to self.target. It's a good idea to slow down when you're getting near the target, otherwise the sprite could get stuck and moves back and forth if it can't reach the target point exactly. Therefore I also check if the sprite is closer than the self.target_radius and decrease the velocity to a fraction of the maximum speed.

import pygame as pg
from pygame.math import Vector2


class Entity(pg.sprite.Sprite):

    def __init__(self, pos, waypoints):
        super().__init__()
        self.image = pg.Surface((30, 50))
        self.image.fill(pg.Color('dodgerblue1'))
        self.rect = self.image.get_rect(center=pos)
        self.vel = Vector2(0, 0)
        self.max_speed = 3
        self.pos = Vector2(pos)
        self.waypoints = waypoints
        self.waypoint_index = 0
        self.target = self.waypoints[self.waypoint_index]
        self.target_radius = 50

    def update(self):
        # A vector pointing from self to the target.
        heading = self.target - self.pos
        distance = heading.length()  # Distance to the target.
        heading.normalize_ip()
        if distance <= 2:  # We're closer than 2 pixels.
            # Increment the waypoint index to swtich the target.
            # The modulo sets the index back to 0 if it's equal to the length.
            self.waypoint_index = (self.waypoint_index + 1) % len(self.waypoints)
            self.target = self.waypoints[self.waypoint_index]
        if distance <= self.target_radius:
            # If we're approaching the target, we slow down.
            self.vel = heading * (distance / self.target_radius * self.max_speed)
        else:  # Otherwise move with max_speed.
            self.vel = heading * self.max_speed

        self.pos += self.vel
        self.rect.center = self.pos


def main():
    screen = pg.display.set_mode((640, 480))
    clock = pg.time.Clock()
    waypoints = [(200, 100), (500, 400), (100, 300)]
    all_sprites = pg.sprite.Group(Entity((100, 300), waypoints))

    done = False

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

        all_sprites.update()
        screen.fill((30, 30, 30))
        all_sprites.draw(screen)
        for point in waypoints:
            pg.draw.rect(screen, (90, 200, 40), (point, (4, 4)))

        pg.display.flip()
        clock.tick(60)


if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()

Instead of the waypoints list and index I'd actually prefer to use itertools.cycle and just call next to switch to the next point:

# In the `__init__` method.
self.waypoints = itertools.cycle(waypoints)
self.target = next(self.waypoints)

# In the `update` method.
if distance <= 2:
    self.target = next(self.waypoints)
查看更多
登录 后发表回答