Deci, am lucrat la câteva jocuri în Python (battleships, tic-tac-toe etc.) și proiectul de săptămâna aceasta este Snake. Am o configurație de bază; șarpele se poate mișca și mănâncă mâncarea, dar nu am programat încă detectarea coliziunilor sau ieșirea de pe margine. Problema este timpul de răspuns. Dacă rulați codul de mai jos, veți vedea că șarpele răspunde la apăsarea tastelor, dar nu pentru câteva – le voi numi cadre – după apăsare. Nu prea înțeleg cum funcționează metoda listen(); o folosesc cumva corect? Dacă nu, cum ar trebui să o folosesc și, dacă da, cum aș putea remedia întârzierea? Știu despre Pygame, dar a) nu găsesc o versiune de 64 de biți ușor de instalat pentru python 3.4 (aceasta http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame nu este ușor de instalat, ce naiba este un fișier whl?) și b) vreau să mă provoc oricum. orice ajutor ar fi apreciat.
import random
import turtle
import time
class Square:
def __init__(self, x, y):
self.x = x
self.y = y
def drawself(self, turtle):
# draw a black box at its coordinates, leaving a small gap between cubes
turtle.goto(self.x - 9, self.y - 9)
turtle.begin_fill()
for i in range(4):
turtle.forward(18)
turtle.left(90)
turtle.end_fill()
class Food:
def __init__(self, x, y):
self.x = x
self.y = y
self.state = "ON"
def changelocation(self):
# I haven't programmed it to spawn outside the snake's body yet
self.x = random.randint(0, 20)*20 - 200
self.y = random.randint(0, 20)*20 - 200
def drawself(self, turtle):
# similar to the Square drawself, but blinks on and off
if self.state == "ON":
turtle.goto(self.x - 9, self.y - 9)
turtle.begin_fill()
for i in range(4):
turtle.forward(18)
turtle.left(90)
turtle.end_fill()
def changestate(self):
# controls the blinking
self.state = "OFF" if self.state == "ON" else "ON"
class Snake:
def __init__(self):
self.headposition = [20, 0] # keeps track of where it needs to go next
self.body = [Square(-20, 0), Square(0, 0), Square(20, 0)] # body is a list of squares
self.nextX = 1 # tells the snake which way it's going next
self.nextY = 0
self.crashed = False # I'll use this when I get around to collision detection
self.nextposition = [self.headposition[0] + 20*self.nextX,
self.headposition[1] + 20*self.nextY]
# prepares the next location to add to the snake
def moveOneStep(self):
if Square(self.nextposition[0], self.nextposition[1]) not in self.body:
# attempt (unsuccessful) at collision detection
self.body.append(Square(self.nextposition[0], self.nextposition[1]))
# moves the snake head to the next spot, deleting the tail
del self.body[0]
self.headposition[0], self.headposition[1] = self.body[-1].x, self.body[-1].y
# resets the head and nextposition
self.nextposition = [self.headposition[0] + 20*self.nextX,
self.headposition[1] + 20*self.nextY]
else:
self.crashed = True # more unsuccessful collision detection
def moveup(self): # pretty obvious what these do
self.nextX = 0
self.nextY = 1
def moveleft(self):
self.nextX = -1
self.nextY = 0
def moveright(self):
self.nextX = 1
self.nextY = 0
def movedown(self):
self.nextX = 0
self.nextY = -1
def eatFood(self):
# adds the next spot without deleting the tail, extending the snake by 1
self.body.append(Square(self.nextposition[0], self.nextposition[1]))
self.headposition[0], self.headposition[1] = self.body[-1].x, self.body[-1].y
self.nextposition = [self.headposition[0] + 20*self.nextX,
self.headposition[1] + 20*self.nextY]
def drawself(self, turtle): # draws the whole snake when called
for segment in self.body:
segment.drawself(turtle)
class Game:
def __init__(self):
# game object has a screen, a turtle, a basic snake and a food
self.screen = turtle.Screen()
self.artist = turtle.Turtle()
self.artist.up()
self.artist.hideturtle()
self.snake = Snake()
self.food = Food(100, 0)
self.counter = 0 # this will be used later
self.commandpending = False # as will this
def nextFrame(self):
while True: # now here's where it gets fiddly...
game.screen.listen()
game.screen.onkey(game.snakedown, "Down")
game.screen.onkey(game.snakeup, "Up")
game.screen.onkey(game.snakeleft, "Left")
game.screen.onkey(game.snakeright, "Right")
turtle.tracer(0) # follow it so far?
self.artist.clear()
if self.counter == 5:
# only moves to next frame every 5 loops, this was an attempt to get rid of the turning delay
if (self.snake.nextposition[0], self.snake.nextposition[1]) == (self.food.x, self.food.y):
self.snake.eatFood()
self.food.changelocation()
else:
self.snake.moveOneStep()
self.counter = 0
else:
self.counter += 1
self.food.changestate() # makes the food flash
self.food.drawself(self.artist) # show the food and snake
self.snake.drawself(self.artist)
turtle.update()
self.commandpending = False
time.sleep(0.05)
def snakeup(self):
print("going up") # put this in for debugging purposes
if not self.commandpending:
# should allow only one turn each frame; I don't think it's working
self.snake.moveup()
self.commandpending = True
def snakedown(self):
print("going down")
if not self.commandpending:
self.snake.movedown()
self.commandpending = True
def snakeleft(self):
print("going left")
if not self.commandpending:
self.snake.moveleft()
self.commandpending = True
def snakeright(self):
print("going right")
if not self.commandpending:
self.snake.moveright()
self.commandpending = True
game = Game()
game.nextFrame()
print("game over!")
game.screen.mainloop()
Ori de câte ori folosiți while True:
(fără break
) în codul de broască țestoasă, înfrângi event hander. În schimb, ar trebui să utilizați un ontimer()
eveniment pentru a vă executa codul în mod compatibil cu gestionarul de evenimente. Mai jos este rescrierea mea a codului dvs. pentru a face acest lucru, împreună cu alte modificări funcționale și de stil:
from turtle import Turtle, Screen
import random
import time
SIZE = 20
class Square:
def __init__(self, x, y):
self.x = x
self.y = y
def drawself(self, turtle):
""" draw a black box at its coordinates, leaving a small gap between cubes """
turtle.goto(self.x - SIZE // 2 - 1, self.y - SIZE // 2 - 1)
turtle.begin_fill()
for _ in range(4):
turtle.forward(SIZE - SIZE // 10)
turtle.left(90)
turtle.end_fill()
class Food:
def __init__(self, x, y):
self.x = x
self.y = y
self.is_blinking = True
def changelocation(self):
# I haven't programmed it to spawn outside the snake's body yet
self.x = random.randint(0, SIZE) * SIZE - 200
self.y = random.randint(0, SIZE) * SIZE - 200
def drawself(self, turtle):
# similar to the Square drawself, but blinks on and off
if self.is_blinking:
turtle.goto(self.x - SIZE // 2 - 1, self.y - SIZE // 2 - 1)
turtle.begin_fill()
for _ in range(4):
turtle.forward(SIZE - SIZE // 10)
turtle.left(90)
turtle.end_fill()
def changestate(self):
# controls the blinking
self.is_blinking = not self.is_blinking
class Snake:
def __init__(self):
self.headposition = [SIZE, 0] # keeps track of where it needs to go next
self.body = [Square(-SIZE, 0), Square(0, 0), Square(SIZE, 0)] # body is a list of squares
self.nextX = 1 # tells the snake which way it's going next
self.nextY = 0
self.crashed = False # I'll use this when I get around to collision detection
self.nextposition = [self.headposition[0] + SIZE * self.nextX, self.headposition[1] + SIZE * self.nextY]
# prepares the next location to add to the snake
def moveOneStep(self):
if Square(self.nextposition[0], self.nextposition[1]) not in self.body:
# attempt (unsuccessful) at collision detection
self.body.append(Square(self.nextposition[0], self.nextposition[1]))
# moves the snake head to the next spot, deleting the tail
del self.body[0]
self.headposition[0], self.headposition[1] = self.body[-1].x, self.body[-1].y
# resets the head and nextposition
self.nextposition = [self.headposition[0] + SIZE * self.nextX, self.headposition[1] + SIZE * self.nextY]
else:
self.crashed = True # more unsuccessful collision detection
def moveup(self): # pretty obvious what these do
self.nextX, self.nextY = 0, 1
def moveleft(self):
self.nextX, self.nextY = -1, 0
def moveright(self):
self.nextX, self.nextY = 1, 0
def movedown(self):
self.nextX, self.nextY = 0, -1
def eatFood(self):
# adds the next spot without deleting the tail, extending the snake by 1
self.body.append(Square(self.nextposition[0], self.nextposition[1]))
self.headposition[0], self.headposition[1] = self.body[-1].x, self.body[-1].y
self.nextposition = [self.headposition[0] + SIZE * self.nextX, self.headposition[1] + SIZE * self.nextY]
def drawself(self, turtle): # draws the whole snake when called
for segment in self.body:
segment.drawself(turtle)
class Game:
def __init__(self):
# game object has a screen, a turtle, a basic snake and a food
self.screen = Screen()
self.artist = Turtle(visible=False)
self.artist.up()
self.artist.speed("slowest")
self.snake = Snake()
self.food = Food(100, 0)
self.counter = 0 # this will be used later
self.commandpending = False # as will this
self.screen.tracer(0) # follow it so far?
self.screen.listen()
self.screen.onkey(self.snakedown, "Down")
self.screen.onkey(self.snakeup, "Up")
self.screen.onkey(self.snakeleft, "Left")
self.screen.onkey(self.snakeright, "Right")
def nextFrame(self):
self.artist.clear()
if (self.snake.nextposition[0], self.snake.nextposition[1]) == (self.food.x, self.food.y):
self.snake.eatFood()
self.food.changelocation()
else:
self.snake.moveOneStep()
if self.counter == 10:
self.food.changestate() # makes the food flash slowly
self.counter = 0
else:
self.counter += 1
self.food.drawself(self.artist) # show the food and snake
self.snake.drawself(self.artist)
self.screen.update()
self.screen.ontimer(lambda: self.nextFrame(), 100)
def snakeup(self):
if not self.commandpending:
self.commandpending = True
self.snake.moveup()
self.commandpending = False
def snakedown(self):
if not self.commandpending:
self.commandpending = True
self.snake.movedown()
self.commandpending = False
def snakeleft(self):
if not self.commandpending:
self.commandpending = True
self.snake.moveleft()
self.commandpending = False
def snakeright(self):
if not self.commandpending:
self.commandpending = True
self.snake.moveright()
self.commandpending = False
game = Game()
screen = Screen()
screen.ontimer(lambda: game.nextFrame(), 100)
screen.mainloop()
Oferă acest lucru tipul de răspuns pe care îl căutați?
Probabil că este o provocare pentru a face un joc rapid în acest fel (dar asta nu e chiar atât de rău). Cred că listen()
ar trebui să meargă după onkey()
s.
De asemenea, ar trebui să te uiți să scapi de tot codul duplicat. S-ar putea părea ușor pe termen scurt să faci doar copy/paste apoi să modifici. Dar dacă trebuie să faceți schimbări majore (cum ar fi după ce ați întrebat pe un forum), va fi anevoios și predispus la erori.
PS (EDIT) de asemenea, metoda ta Snake.moveOneStep() face o nouă instanță de Square doar pentru a verifica autocoliziunea, acest lucru pare extravagant de dragul eleganței. Mai bine să păstrați doar o listă de locații pe care python (ho, ho, ho) le poate verifica prin ea. (În afară de faptul că acest lucru probabil nu funcționează. Încercați print(Square(1,2) in [Square(1,2)])
)
def check_self_collision(self, x, y):
for s in self.body:
if s.x == x and s.y == y:
return False
return True
def moveOneStep(self):
if self.check_self_collision(self.nextposition[0], self.nextposition[1]):
# attempt (unsuccessful) at collision detection
self.body.append(Square(self.nextposition[0], self.nextposition[1]))
Versiunea mea:
#coding: utf-8
from Tkinter import *
import random
import time
class Levely:
def __init__(self):
self.urovne=[
[[0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1]],
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 1, 1, 1, 0, 1, 1, 1, 0, 1], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 0, 0, 1, 0, 0, 0], [1, 1, 1, 0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 1, 1, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 1, 1, 1, 1, 0, 1], [0, 0, 1, 0, 0, 0, 1, 0, 0, 1], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 1, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 1, 1, 0, 0, 0, 0, 0, 0, 1], [0, 0, 1, 0, 0, 0, 0, 0, 1, 1], [0, 0, 1, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 1], [0, 0, 0, 1, 0, 0, 1, 0, 0, 1], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 1, 1, 0], [0, 0, 1, 1, 1, 0, 0, 0, 0, 0]],
]
self.data=[[400,13],[400,10],[400,13],[400,13],[400,13],[400,13]]
print "Choose from", len(self.urovne), "levels"
self.vyber=input("Level: ")
self.vyber-=1
h=Had(self)
class Had:
def __init__(self,Levely):
self.l=Levely
self.level=self.l.urovne[self.l.vyber]
self.mrizka=len(self.level[0])
self.velikost=self.l.data[self.l.vyber][0]
self.vtelo=100
self.r=self.l.data[self.l.vyber][1]
self.x=0
self.y=0
self.u=0
self.k=self.velikost
self.c=(self.velikost/self.mrizka)
self.poprve=0
self.neco=[[0,0],0,0,0]
self.ukonceni=None
self.aakce1=None
self.aakce2=None
self.aakce3=None
self.aakce4=None
self.s=[0,0,0,0]
self.j=[]
self.konec=0
self.score=0
self.pocet_zelenych=0
self.okno=Tk()
self.platno=Canvas(self.okno,width=self.velikost,height=self.velikost,bg="white")
self.platno.pack()
self.tl=Button(self.okno, text="Restart", command=self.start)
self.tl.pack(fill=BOTH)
self.start()
self.okno.bind("<Key-d>", self.akce1)
self.okno.bind("<Key-w>", self.akce2)
self.okno.bind("<Key-s>", self.akce3)
self.okno.bind("<Key-a>", self.akce4)
self.okno.bind("<Key-r>", self.start1)
def akce1(self, klik):
self.akce11()
def akce2(self, klik):
self.akce21()
def akce3(self, klik):
self.akce31()
def akce4(self, klik):
self.akce41()
def start1(self, klik):
self.start()
def akce11(self):
if int(self.s[1])%self.c!=0:
self.aakce1=self.okno.after(9,self.akce11)
if int(self.s[1])%self.c==0:
self.x=self.c
self.y=0
self.u=0
if self.poprve==1:
self.okno.after_cancel(self.aakce1)
self.stop()
self.pohyb()
def akce21(self):
if int(self.s[0])%self.c!=0:
self.aakce1=self.okno.after(9,self.akce21)
if int(self.s[0])%self.c==0:
self.x=0
self.y=-self.c
self.u=0
if self.poprve==1:
self.okno.after_cancel(self.aakce2)
self.stop()
self.pohyb()
def akce31(self):
if int(self.s[0])%self.c!=0:
self.aakce1=self.okno.after(9,self.akce31)
if int(self.s[0])%self.c==0:
self.x=0
self.y=self.c
self.u=1
if self.poprve==1:
self.okno.after_cancel(self.aakce3)
self.stop()
self.pohyb()
def akce41(self):
if int(self.s[1])%self.c!=0:
self.aakce1=self.okno.after(9,self.akce41)
if int(self.s[1])%self.c==0:
self.x=-self.c
self.y=0
self.u=0
if self.poprve==1:
self.okno.after_cancel(self.aakce4)
self.stop()
self.pohyb()
def pohyb(self):
self.smrt()
if self.konec==1:
return None
self.test()
s=self.platno.coords(self.hlava)
self.s=self.platno.coords(self.hlava)
self.platno.delete(ALL)
self.hlava=self.platno.create_rectangle(s[0],s[1],s[2],s[3], fill="green4", outline="white")
self.jablko=self.platno.create_rectangle(self.j[0],self.j[1],self.j[2],self.j[3], fill="red", outline="red")
for x in range(self.mrizka):
for y in range(self.mrizka):
if self.level[x][y]==0:
continue
if self.level[x][y]==1:
#KURVVAAAAA x,y,x,y
self.block=self.platno.create_rectangle(y*self.c,(x*self.c),(y*self.c)+self.c,(x*self.c)+self.c, fill="black")
self.test()
s=self.platno.coords(self.hlava)
self.poloha.append(s)
self.delka=len(self.poloha)
if s[self.u]<=self.k:
self.dx=self.x
self.dy=self.y
self.platno.move(self.hlava,self.dx/10,self.dy/10)
s=self.platno.coords(self.hlava)
self.nahrada=self.platno.create_rectangle(s[0],s[1],s[2],s[3], fill="green4", outline="green4")
if s[self.u]>=self.k:
self.dx=0
self.dy=0
bla="Restart-Score:", int(self.score)
self.tl.config(text=bla)
for a in range(self.delka):
if 1==1:
self.ocas=self.platno.create_rectangle(self.poloha[a][0],self.poloha[a][1],self.poloha[a][2],self.poloha[a][3], fill="green2", outline="green2")
self.poloha_zeleny=self.platno.coords(self.ocas)
self.zeleny.append(self.poloha_zeleny)
self.pocet_zelenych=len(self.zeleny)
if self.pocet_zelenych>=self.delka:
del self.zeleny[0]
if self.delka>=self.vtelo:
self.neco=self.poloha[0]
del self.poloha[0]
self.s=self.platno.coords(self.hlava)
self.nahrada=self.platno.create_rectangle(s[0],s[1],s[2],s[3], fill="green4", outline="green4")
self.ukonceni=self.okno.after(self.r,self.pohyb)
def smrt(self):
s=self.platno.coords(self.hlava)
bla="Restart-Score:", int(self.score)
if self.level[int(s[1]/self.c)][int(s[0]/self.c)]==1:
self.platno.delete(self.hlava)
self.tl.config(text=bla)
self.konec=1
self.smrtak=self.platno.create_rectangle(s[0],s[1],s[2],s[3], fill="brown", outline="brown")
for b in range(len(self.zeleny)):
if s==self.zeleny[(b-1)]:
self.platno.delete(self.hlava)
self.tl.config(text=bla)
self.konec=1
self.smrtak=self.platno.create_rectangle(s[0],s[1],s[2],s[3], fill="brown", outline="brown")
def stop(self):
if self.poprve==1:
self.okno.after_cancel(self.ukonceni)
self.poprve=1
def start(self):
self.vtelo=60
self.platno.delete("all")
self.tl.config(text="Restart")
self.poloha=[]
self.zeleny=[]
self.konec=0
self.pocet_zelenych=0
self.score=0
self.poprve=0
self.dx=0
self.dy=0
if self.aakce1!=None:
self.okno.after_cancel(self.aakce1)
self.aakce1=None
if self.aakce2!=None:
self.okno.after_cancel(self.aakce2)
self.aakce2=None
if self.aakce3!=None:
self.okno.after_cancel(self.aakce3)
self.aakce3=None
if self.aakce4!=None:
self.okno.after_cancel(self.aakce4)
self.aakce4=None
for x in range(self.mrizka):
for y in range(self.mrizka):
if self.level[x][y]==0:
continue
if self.level[x][y]==1:
#KURVVAAAAA x,y,x,y
self.block=self.platno.create_rectangle(y*self.c,(x*self.c),(y*self.c)+self.c,(x*self.c)+self.c, fill="black")
self.hlava=self.platno.create_rectangle(0,0,self.c,self.c, fill="green4", outline="green4")
self.generace()
s=self.platno.coords(self.hlava)
self.dx=self.c
self.dy=self.c
def generace(self):
self.nx=random.randint(0,self.mrizka-1)
self.ny=random.randint(0,self.mrizka-1)
for x in self.zeleny:
if int(x[0]/self.c)==self.nx and int(x[1]/self.c)==self.ny:
self.generace()
if self.level[self.ny][self.nx]==1:
self.generace()
if self.level[self.ny][self.nx]!=1:
self.jablko=self.platno.create_rectangle(self.nx*self.c,self.ny*self.c,self.nx*self.c+self.c,self.ny*self.c+self.c, fill="red", outline="red")
def test(self):
s=self.platno.coords(self.hlava)
self.j=self.platno.coords(self.jablko)
if s==self.j:
self.vtelo+=5
self.score+=0.5
self.generace()
def mezery(self):
for x in range(30):
print ""
levliky=Levely()
mainloop()
- Vă rugăm să furnizați o descriere mai detaliată a ceea ce ați schimbat și de ce ați schimbat. Doar postarea unui zid de cod nu este nici utilă, nici plăcută la citit sau ușor de înțeles. – > .
__eq__
este utilă, dar nu puteți implementa comparațiile x și y cu==
deoarece broaștele țestoase se târăsc pe un plan în virgulă mobilă și este ușor să ajungi să compari2.0 == 2.001
. În schimb,turtle.distance()
trebuie să fie folosit cu o valoare mică „suficient de apropiată”. – > Por cdlane.