[英]I am having trouble with enemies in pygame
我一直在尝试为我的蛇游戏创建一个敌人 class,我可以在我的 pygame循环中执行,但是我的fill_screen()
function 一直在屏幕上,所以屏幕上没有一个敌人是敌人. 我希望有人可以帮助我解决这个问题,因为我已经研究了 3 天多,但我还没有找到任何实际的解决方案。 一个例子真的很有帮助。 我的代码附在下面。
import pygame,sys,random
from pygame.locals import *
pygame.init()
clock=pygame.time.Clock()
movement="nil"
enemy_list = []
enemy_img = pygame.image.load(r'C:\Users\hudso\Documents\PyGame\__pycache__\Images\mele_enemy.png')
SPAWNENEMY = pygame.USEREVENT
pygame.time.set_timer(SPAWNENEMY,1000)
cooled=False
food_created=False
food_counter=0
cooldown_tracker=0
window_size=[1000,1000]
screen=pygame.display.set_mode((window_size[0],window_size[1]),RESIZABLE)
display=pygame.Surface((400,400))
snake_health=100
snake_pos=[188,188]
enemy_pos=[100,100]
snake_img=pygame.image.load(r"C:\Users\hudso\Documents\PyGame\__pycache__\Images\snake.png")
def new_food_pos():
food_x=random.randint(25,350)
food_y=random.randint(25,350)
food_coords=[food_x,food_y]
return food_coords
class snake():
def __init__(self,snake_position,snake_hitpoints):
self.snake_position=snake_position
self.snake_hp=snake_hitpoints
def create_snake(self):
global snake_img
display.blit(snake_img,(self.snake_position[0],self.snake_position[1]))
pygame.draw.rect(display,(0,0,0),[self.snake_position[0]-3,self.snake_position[1]-7,31,5])
pygame.draw.rect(display,(212,23,48),[self.snake_position[0]-1,self.snake_position[1]-6,27,3])
sub_hp=self.snake_hp-100
take_health=abs(sub_hp)
gray_health=(take_health*0.27)
pygame.draw.rect(display,(40,40,40),[self.snake_position[0]-0+self.snake_hp*.27,self.snake_position[1]-6,gray_health,3])
def move_snake(self,moving):
global snake_img
if moving=="up":
snake_pos[1]-=1
if moving=="down":
snake_pos[1]+=1
if moving=="right":
snake_pos[0]+=1
if moving=="left":
snake_pos[0]-=1
if moving=="nil":
display.blit(snake_img,(self.snake_position[0],self.snake_position[1]))
food_pos=new_food_pos()
class food():
def __init__(self):
global food_pos
self.food_location=food_pos
self.food=pygame.image.load(r"C:\Users\hudso\Documents\PyGame\__pycache__\Images\apple.png")
def create_food(self):
display.blit(self.food,(self.food_location[0],self.food_location[1]))
class Enemys:
def __init__(self):
#self.xval = random.randint(0,700)
self.size = random.randint(50,50)
def create_enemy(self):
Enemy = pygame.Rect(random.randint(20,480), random.randint(20,480), self.size,self.size)
#enemy_updated = pygame.transform.scale(enemy_img,(self.size,self.size))
enemy_list.append(Enemy)
display.blit(enemy_img,Enemy)
def draw(self): # draw all enemies
for e in enemy_list:
display.blit(enemy_img,e)
def move_enemy(self):
if self.enemy_position[0]!=snake_pos[0]:
if self.enemy_position[0]+15>snake_pos[0]:
self.enemy_position[0]-=.5
if self.enemy_position[0]-15<snake_pos[0]:
self.enemy_position[0]+=.5
if snake_pos[0]-16<self.enemy_position[0] and snake_pos[0]+16>self.enemy_position[0]:
if self.enemy_position[1]!=snake_pos[1]:
if self.enemy_position[1]-15>snake_pos[1]:
self.enemy_position[1]-=.5
if self.enemy_position[1]+15<snake_pos[1]:
self.enemy_position[1]+=.5
def fill_screen():
display.fill((0,0,0))
pygame.draw.rect(display,(29,10,200),[2,2,396,396])
pygame.draw.rect(display,(43,7,230),[3,3,394,394])
while True:
fill_screen()
food().create_food()
food_rect=pygame.Rect((food_pos[0],food_pos[1]),(25,25))
snake(snake_pos,snake_health).create_snake()
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
sys.exit()
if event.type==pygame.KEYDOWN:
if event.key==K_RIGHT:
movement="right"
if event.key==K_LEFT:
movement="left"
if event.key==K_UP:
movement="up"
if event.key==K_DOWN:
movement="down"
if event.type == SPAWNENEMY:
Enemys().create_enemy()
snake(snake_pos,snake_health).move_snake(movement)
snake_rect=pygame.Rect((snake_pos[0],snake_pos[1]),(25,25))
food_eaten=snake_rect.colliderect(food_rect)
if snake_health<=0:
pygame.quit()
sys.exit()
print("You Died From Lack Of Living")
if food_eaten:
food_pos=new_food_pos()
food_counter+=1
snake_health+=5
if snake_health>=100:
snake_health=100
surf=pygame.transform.scale(display,window_size)
screen.blit(surf,(0,0))
Enemys().draw()
pygame.display.update()
clock.tick(60)
谢谢
这个答案会很长,关于这段代码有很多话要说。 不要将此视为严厉的批评,而是希望它能指导您以更好的方式组织代码并理解 OOP(面向对象编程)。
这或多或少是对您的代码的竞争性重构,并解释了我为什么要进行每次更改。 我重构了你写的东西,但我没有完成游戏,这留给你做。
我注意到的第一件事是你使用类,但你似乎并不真正理解它们是如何被使用的。 例如,如果您将诸如“snake_health”或“snake_position”之类的变量放在 class 之外,然后使用一些时髦的方法将值返回到您的类中,为什么还要使用蛇 class。
这是我重新编写蛇 class 的方法:
class Snake():
def __init__(self, pos):
self.pos = pos
self.lenght = 0
self.health = 100
self.direction = None
self.img = pg.image.load("snake.png")
def move(self):
if self.direction=="up":
self.pos[1]-=1
elif self.direction=="down":
self.pos[1]+=1
elif self.direction=="right":
self.pos[0]+=1
elif self.direction=="left":
self.pos[0]-=1
def take_damage(self, damage):
self.health -= damage
有几点需要您考虑:
snake_position
简单地变为position
(或者我个人更喜欢称之为pos
),因为它位于蛇 class 内部,我们已经知道它是与蛇的链接。 class 方法也是如此,因此move_snake(...)
变为move(...)
,因为无论如何您都将使用snake.move()
调用它这是我刚才提到的抽奖 function:
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw snake
screen.blit(snake.img, snake.pos)
# Draw hp bar
snake_x, snake_y = snake.pos
bar_width, bar_height = 30,5
bar_x, bar_y = snake_x - bar_width // 2 + snake.img.get_width() // 2, snake_y - bar_height - snake.img.get_height() // 2
# hp bar background
pg.draw.rect(screen, (255,0,0), pg.Rect(bar_x, bar_y, bar_width, bar_height))
# hp bar filled part
pg.draw.rect(screen, (0,255,0), pg.Rect(bar_x, bar_y, bar_width * (snake.health / 100), bar_height))
# hp bar contour
pg.draw.rect(screen, (80,80,80), pg.Rect(bar_x, bar_y, bar_width, bar_height), 1)
# Draw food
screen.blit(food.img, food.pos)
# Draw enemies
for enemy in Enemy.LIST:
screen.blit(enemy.img, enemy.pos)
要更新您的值并执行一些游戏逻辑:将其分组为 function(大多数人将其称为update()
,所以让我们坚持下去)
def update():
# Move the snake every frame
snake.move()
# Check if snake ate the food
snake_rect = pg.Rect(snake.pos, snake.img.get_size())
food_rect = pg.Rect(food.pos, food.img.get_size())
if snake_rect.colliderect(food_rect): # (if food_eaten)
food.pos = new_food_pos()
snake.lenght += 1
# Move the enemies
for enemy in Enemy.LIST:
enemy.move(snake.pos)
# Check if enemy reached snake
enemy_rect = pg.Rect(enemy.pos, enemy.img.get_size())
if snake_rect.colliderect(enemy_rect):
snake.take_damage(10)
enemy.kill()
if snake.health <= 0:
game_over()
一个好的经验法则是编写您的类,以便通过调用更新 function 中的方法,无需查看代码中的其他地方就可以很容易地理解逻辑。 例如,如果您查看:
if snake_rect.colliderect(enemy_rect):
snake.take_damage(10)
enemy.kill()
if snake.health <= 0:
game_over()
很明显,如果蛇和敌人发生碰撞,蛇会受到伤害,敌人会被杀死,然后如果蛇的生命值达到0,那么游戏就结束了。
这是我重新编写食物 class 的方法:
class Food():
def __init__(self, pos):
self.pos = pos
self.img = pg.image.load("apple.png")
如此简单,您可能根本不需要 class,但我想坚持您为它编写 class 的选择。 我想您看到了,但我也将“位置”一词替换为“位置”。 这是您应该避免的另一件事:使用不同的术语说同样的事情:对于蛇的 position,您使用术语“位置”,对于您使用术语“位置”的食物的 position,两者都是正确的名称,但请选择一个并在所有课程中使用相同的内容,否则只会增加混乱。
现在你的敌人 class:
class Enemy:
LIST = []
def __init__(self, pos):
self.pos = pos
self.img = pg.image.load("enemy.png")
w = h = rd.randint(10, 20)
self.size = (w, h)
self.img = pg.transform.scale(self.img, self.size)
# Add the enemy to the list
Enemy.LIST.append(self)
def move(self, snake_pos):
enemy_x, enemy_y = self.pos
snake_x, snake_y = snake_pos
# Move enemy towards the player
# (no need to check if position are equals since it will never happend -> as soon as rects collides)
if enemy_x - snake_x > 0:
enemy_x -= .5
else:
enemy_x += .5
if enemy_y - snake_y > 0:
enemy_y -= .5
else:
enemy_y += .5
self.pos = (enemy_x, enemy_y)
def kill(self):
Enemy.LIST.remove(self)
对于事件处理部分,您基本上是对的,@Rabbid76 提到了一个识别问题,我写的有点不同,但关闭了原版:
def handle_events():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == pg.KEYDOWN:
if evt.key == pg.K_ESCAPE:
exit()
if evt.type == pg.KEYDOWN:
if evt.key == pg.K_RIGHT:
snake.direction = "right"
elif evt.key == pg.K_LEFT:
snake.direction = "left"
elif evt.key == pg.K_UP:
snake.direction = "up"
elif evt.key == pg.K_DOWN:
snake.direction = "down"
if evt.type == SPAWN_ENEMY:
Enemy((rd.randint(20, 480), rd.randint(20, 480)))
现在唯一剩下的就是这 3 个小函数:
def new_food_pos():
return [rd.randint(25,350),rd.randint(25,350)]
def game_over():
print("You Died From Lack Of Living")
exit()
def exit():
pg.quit()
sys.exit()
我认为他们不需要解释。
最后一个关键点是要了解,当您像这样调用 class 名称时:
ClassName(...)
您创建了该 class 的实例,因此每次在您的代码中调用snake(snake_position, snake_health)
时,您都在创建一个新的snake 实例,该实例与之前已经创建的所有snake 没有任何共享,这就是为什么您仍然需要使用全局变量和class 变得毫无意义。
总结:您对 OOP 概念感到困惑,我建议您观看/阅读有关该主题的教程。 如果您发现自己很难理解这些教程,这可能意味着 OOP 对您来说太先进了,先巩固基础,过几周再回到 OOP,它会变得更加顺畅 Z34D1F91FB2E514B8576FAB1A5
注意可读性:与您的命名习惯保持一致,使用更多空格。 您可以通过打开终端来查阅 python 的禅宗(python 代码的指南),输入“python”然后“import this”
由于您是初学者,请首先在 pygame 项目中使用此结构:
def update():
pass
def draw():
pass
def handle_events():
pass
while True:
handle_events()
update()
draw()
pg.display.update()
clock.tick(FPS)
以后您将有时间体验代码结构。 现在:更新所有值并在 update() 中执行游戏逻辑,在 draw() 中渲染所有组件并在 handle_events() 中管理每个事件。 这将使您更快、更轻松地学习 pygame。
除了那些有点特殊的功能外,尽量遵循单一职责原则,这意味着每个 function 必须有一份工作,只有一份工作,它的可读性大大提高。
最重要的是:不要气馁,我们都需要从某个地方开始,即使你写的很多东西都不会被认为是“好代码”,你仍然可以编写出你想要的代码,这已经是一个很大的成就你可以自豪。 随着您继续编程,您的代码会越来越好。 我什至从你的代码中学到了一些东西(我在 5 年前开始使用 pygame):我不知道有一个“pygame.USEREVENT”,这对我未来的项目来说非常方便。
快乐的编程,这里是一个完整的重构:
# General import
import pygame as pg
import random as rd
import sys
# Classes
class Snake():
def __init__(self, pos):
self.pos = pos
self.lenght = 0
self.health = 100
self.direction = None
self.img = pg.image.load("snake.png")
def move(self):
if self.direction=="up":
self.pos[1]-=1
elif self.direction=="down":
self.pos[1]+=1
elif self.direction=="right":
self.pos[0]+=1
elif self.direction=="left":
self.pos[0]-=1
def take_damage(self, damage):
self.health -= damage
class Food():
def __init__(self, pos):
self.pos = pos
self.img = pg.image.load("apple.png")
class Enemy:
LIST = []
def __init__(self, pos):
self.pos = pos
self.img = pg.image.load("enemy.png")
w = h = rd.randint(10, 20)
self.size = (w, h)
self.img = pg.transform.scale(self.img, self.size)
# Add the enemy to the list
Enemy.LIST.append(self)
def move(self, snake_pos):
enemy_x, enemy_y = self.pos
snake_x, snake_y = snake_pos
# Move enemy towards the player
# (no need to check if position are equals since it will never happend -> as soon as rects collides)
if enemy_x - snake_x > 0:
enemy_x -= .5
else:
enemy_x += .5
if enemy_y - snake_y > 0:
enemy_y -= .5
else:
enemy_y += .5
self.pos = (enemy_x, enemy_y)
def kill(self):
Enemy.LIST.remove(self)
# Init
pg.init()
# Display
screen = pg.display.set_mode((500, 500))
pg.display.set_caption('Snake - fix')
FPS = 60
clock = pg.time.Clock()
# Set up custom event
SPAWN_ENEMY = pg.USEREVENT
pg.time.set_timer(SPAWN_ENEMY, 1000)
# Main functions
def update():
# Move the snake every frame
snake.move()
# Check if snake ate the food
snake_rect = pg.Rect(snake.pos, snake.img.get_size())
food_rect = pg.Rect(food.pos, food.img.get_size())
if snake_rect.colliderect(food_rect): # (if food_eaten)
food.pos = new_food_pos()
snake.lenght += 1
# Move the enemies
for enemy in Enemy.LIST:
enemy.move(snake.pos)
# Check if enemy reached snake
enemy_rect = pg.Rect(enemy.pos, enemy.img.get_size())
if snake_rect.colliderect(enemy_rect):
snake.take_damage(10)
enemy.kill()
if snake.health <= 0:
game_over()
def draw():
# Clear screen
screen.fill((0,0,0))
# Draw snake
screen.blit(snake.img, snake.pos)
# Draw hp bar
snake_x, snake_y = snake.pos
bar_width, bar_height = 30,5
bar_x, bar_y = snake_x - bar_width // 2 + snake.img.get_width() // 2, snake_y - bar_height - snake.img.get_height() // 2
# hp bar background
pg.draw.rect(screen, (255,0,0), pg.Rect(bar_x, bar_y, bar_width, bar_height))
# hp bar filled part
pg.draw.rect(screen, (0,255,0), pg.Rect(bar_x, bar_y, bar_width * (snake.health / 100), bar_height))
# hp bar contour
pg.draw.rect(screen, (80,80,80), pg.Rect(bar_x, bar_y, bar_width, bar_height), 1)
# Draw food
screen.blit(food.img, food.pos)
# Draw enemies
for enemy in Enemy.LIST:
screen.blit(enemy.img, enemy.pos)
def handle_input():
for evt in pg.event.get():
if evt.type == pg.QUIT:
exit()
if evt.type == pg.KEYDOWN:
if evt.key == pg.K_ESCAPE:
exit()
if evt.type == pg.KEYDOWN:
if evt.key == pg.K_RIGHT:
snake.direction = "right"
elif evt.key == pg.K_LEFT:
snake.direction = "left"
elif evt.key == pg.K_UP:
snake.direction = "up"
elif evt.key == pg.K_DOWN:
snake.direction = "down"
if evt.type == SPAWN_ENEMY:
Enemy((rd.randint(20, 480), rd.randint(20, 480)))
def new_food_pos():
return [rd.randint(25,350),rd.randint(25,350)]
def game_over():
print("You Died From Lack Of Living")
exit()
def exit():
pg.quit()
sys.exit()
# Main loop
if __name__ == "__main__":
snake = Snake([100, 100])
food = Food(new_food_pos())
while True:
handle_input()
update()
draw()
pg.display.update()
clock.tick(FPS)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.