前提・実現したいこと
小林 郁夫・佐々木 晃共著の「Pythonによるプログラミング」p160の練習問題6.4に取り組んでいます。最終的にボールがブロックで跳ね返りブロックが消えるアニメーションを作成したいです。しかし、check_blocksのblockが正しく反映されません。自分でも挑戦してみましたが、上手くいきません。ご教授願います。
発生している問題・エラーメッセージ
Traceback (most recent call last):
File "C:\Users\user\Desktop\python\ex06-block.py", line 182, in <module>
box.animate()
File "C:\Users\user\Desktop\python\ex06-block.py", line 162, in animate
self.check_blocks(self.ball,self.block)
File "C:\Users\user\Desktop\python\ex06-block.py", line 145, in check_blocks
if(block.y<ball.y+ball.d/2<block.y+block.h
AttributeError: 'NoneType' object has no attribute 'y'
該当のソースコード
from tkinter import *
from dataclasses import dataclass,field
import time
@dataclass
class Ball:
id:int
x:int
y:int
d:int
vx:int
vy:int
c:str="black"
def move(self): self.x=self.x+self.vx self.y=self.y+self.vy def redraw(self): d=self.d canvas.coords(self.id,self.x,self.y, self.x+d,self.y+d)
@dataclass
class Paddle:
id:int
x:int
y:int
w:int
h:int
vy:int = field(init=False,default=0)
c:str="green"
def move(self): self.y=self.y+self.vy def redraw(self): canvas.coords(self.id,self.x,self.y, self.x+self.w,self.y+self.h) def set_v(self,v): self.vy=v def stop(self): self.vy=0
@dataclass
class Block:
id:int
x:int
y:int
w:int
h:int
c:str
def redraw(self): canvas.coords(self.id,self.x,self.y, self.x+self.w,self.y+self.h) def delete(self): canvas.delete(self.id)
@dataclass
class Box:
id:int
west:int
north:int
east:int
south:int
ball:Ball
paddle:Paddle
block:Block
paddle_v:int
duration:float
def __init__(self,x,y,w,h,duration): self.west,self.north=(x,y) self.east,self.south=(x+w,y+h) self.ball=None self.paddle=None self.block=None self.blocks=[] self.duration=duration self.paddle_v=5 def make_ball(self,x,y,d,vx,vy): id=canvas.create_oval(x,y,x+d,y+d, fill="black",outline="black") return Ball(id,x,y,d,vx,vy) def make_walls(self): canvas.create_rectangle(self.west,self.north,self.east,self.south) def check_wall(self,ball): if ball.x+ball.vx<=self.west or ball.x+ball.d+ball.vx>=self.east: ball.vx=-ball.vx if ball.y+ball.vy<=self.north or ball.y+ball.d+ball.vy>=self.south: ball.vy=-ball.vy def set_ball(self): self.ball=self.make_ball(self.west+10,self.north+10,10,4,2) def make_paddle(self,x,y,w,h,c): id=canvas.create_rectangle(x,y,x+w,y+h, fill=c,outline=c) return Paddle(id,x,y,w,h) def check_paddle(self,ball,paddle): if(ball.y+ball.d/2>=paddle.y and paddle.x<=ball.x+ball.d and ball.y+ball.d/2<=paddle.y+paddle.h): ball.vx=-ball.vx def set_paddle(self): self.paddle=self.make_paddle(self.east-20,(self.north+self.south)/2-10, 10,20, "green") def up_paddle(self,event): self.paddle.set_v(-self.paddle_v) def down_paddle(self,event): self.paddle.set_v(self.paddle_v) def stop_paddle(self,event): self.paddle.stop() def make_block(self,x,y,w,h,c): id=canvas.create_rectangle(x,y,x+w,y+h, fill=c,outline=c) return Block(id,x,y,w,h,c) def set_blocks(self,n,s): for x in range(n): block=self.make_block(self.west+10*x,self.south-30, 5,20,"red") for x in range(s): block=self.make_block(self.west+10*x,self.south-60, 5,20,"blue") self.blocks.append(block) def check_blocks(self,ball,block): if(block.y<ball.y+ball.d/2<block.y+block.h and(block.x<=ball.x<=block.x+block.w or block.x<=ball.x+ball.d<=block.x+block.w)): ball.vx=-ball.vx self.block.delete() self.blocks.remove() def animate(self): while True: self.ball.move() self.paddle.move() self.check_wall(self.ball) self.check_paddle(self.ball,self.paddle) self.ball.redraw() self.paddle.redraw() for block in self.blocks: self.check_blocks(self.ball,self.block) self.block.redraw() time.sleep(self.duration) tk.update()
tk=Tk()
canvas=Canvas(tk,width=800,height=800,bd=0)
canvas.pack()
box=Box(100,100,300,200,0.05)
box.make_walls()
canvas.bind_all("<KeyPress-Up>",box.up_paddle)
canvas.bind_all("<KeyPress-Down>",box.down_paddle)
canvas.bind_all("<KeyRelease-Up>",box.stop_paddle)
canvas.bind_all("<KeyRelease-Down>",box.stop_paddle)
box.set_ball()
box.set_paddle()
box.set_blocks(4,4)
box.animate()