41455


[たのしいプログラミング Pythonではじめよう！](https://www.ohmsha.co.jp/book/9784274069444/)の第II部
Bounce!ゲーム(第13章、第14章)を変更しましたが、実行した際にpaddleに当たった時のボールの挙動がおかしいという現象が起きています。

バーではなく、ボールがバーに当たった時に上に移動せずに、斜め下に移動してしまうので、ボールがpaddleに反射して上に動くようにしたいと考えていますが、意図したとおりに動作しません。

[code]

    from tkinter import *
    import random
    import time
    from sys import exit
    #必要なモジュールをインポート
    
    
    tk = Tk()
    tk.title('Game')#ウインドウのタイトル
    tk.resizable(0, 0)#ウインドウのサイズ変更を不可能に
    tk.wm_attributes('-topmost', 1)#ウインドウを一番上に表示
    canvas = Canvas(tk, width=1000, height=800, bd=0, highlightthickness=0)#キャンバスを配置
    
    canvas.pack()#キャンバスの表示
    #my_image = PhotoImage(file='bg_yozora_night_sky.gif')
    canvas.create_image(0, 0, anchor=NW)#image=my_image
    tk.update()#アニメーションのためにtkinterを初期化
    
    
    count = 0
    global colors
    colors=['red','blue','black','yellow','green','purple','magenta']
    random.shuffle(colors)
    color=colors[0]
    color2=colors[2]
    color3=colors[3]
    color4=colors[4]
    
    lost = False
    scores = canvas.create_text(800, 10, text="Score: " + str(count), fill = "red",font=('Blackoak Std',10))
    
    ###ボール###
    class Ball:
    #クラスをボールに設定
        def __init__(self, canvas, paddle, board, board2, color):
            #canvas,color,paddle,score引数を受け取る__init__関数を作成
            self.canvas = canvas
            self.paddle = paddle
            self.board = board
            self.board2 = board2
            self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
            #create_oval関数を呼び出し、引数[左上角の座標(10,10)、右下角の座標(25,25)、円の色}]をセット
            self.canvas.move(self.id, 470, 200)
            #円の識別番号（オブジェクトの変数id）を用いてキャンバスのほぼ中央(235,100)に円を移動
            starts = [-3, -2, -1, 1, 2, 3]#数字のリストをセットしたstarts関数をセット
            random.shuffle(starts)#starts関数の値の順序をシャッフル
            self.x = starts[0]#リストの先頭をxにセット
            self.y = -3
            self.canvas_height = self.canvas.winfo_height()
            #キャンバスのwinfo_height関数が返す値をオブジェクトの変数canvas_heightにセット
            self.canvas_width = self.canvas.winfo_width()
            #キャンバスのwinfo_width関数が返す値をオブジェクトの関数canvas_widthにセット
            self.hit_bottom = False
    
        def hit_paddle(self, pos):
            paddle_pos = self.canvas.coords(self.paddle.id)
            if pos[2]>= paddle_pos[0] and pos[0] <= paddle_pos[2]:
                if pos[3] >= paddle_pos[1] and pos[3] <= paddle_pos[3]:
                    self.x += self.paddle.x
                    global count
                    count +=1
                    score()
    
            if pos[3] <= self.canvas_height:
                self.canvas.after(5, self.draw)
            else:
                self.canvas.move(self.id, 245, 200) #added this line
                game_over()
                global lost
                lost = True
    
        def hit_bottom(self, pos):
            paddle2_pos = self.canvas.coords(self.paddle2.id)
            if pos[2] >= paddle2_pos[0] and pos[0] <= paddle2_pos[2]:
                if pos[1] >= paddle2_pos[1] and pos[1] <= paddle2_pos[3]:
                    return True
                return False
    
        def hit_board(self, pos):#障害物の判定
            board_pos = self.canvas.coords(self.board.id)
            if pos[2]>= board_pos[0] and pos[0] <= board_pos[2]:
                if pos[3] >= board_pos[1] and pos[3] <= board_pos[3]:
                    return True
            return False
    
        def hit_board2(self, pos):
            board2_pos = self.canvas.coords(self.board2.id)
            if pos[2]>= board2_pos[0] and pos[0] <= board2_pos[2]:
                if pos[3] >= board2_pos[1] and pos[3] <= board2_pos[3]:
                    return True
            return False
    
        def hit_board_bottom(self, pos):
            board_pos = self.canvas.coords(self.board.id)
            if pos[2]>= board_pos[0] and pos[0] <= board_pos[2]:
                if pos[1] <= board_pos[3] and pos[1] >= board_pos[1]:
                    return True
            return False
    
        def hit_board2_bottom(self, pos):#board2の下側の当たり判定
            board2_pos = self.canvas.coords(self.board2.id)
            if pos[2]>= board2_pos[0] and pos[0] <= board2_pos[2]:
                if pos[1] <= board2_pos[3] and pos[1] >= board2_pos[1]:
                    return True
            return False
    
        def draw(self):
            self.canvas.move(self.id, self.x, self.y)
            pos = self.canvas.coords(self.id)
            speed=[1,2,3,4,5]
            speed2=[-1,-2,-3,-4,-5]
            random.shuffle(speed)
            random.shuffle(speed2)
            colors=['red','blue','black','yellow','green','purple','magenta']
            random.shuffle(colors)
    
    
    
            if pos[1] <= 0:
                self.y = speed[0]
                canvas.itemconfig(self.id,fill=colors[0])
            if self.hit_board(pos)==True:
                self.y=-6
            if self.hit_board_bottom(pos)==True:
                self.y=6
            if self.hit_board2(pos)==True:
                self.y=-6
            if self.hit_board2_bottom(pos)==True:
                self.y=6
    
            if self.hit_paddle(pos) == True:
                self.y = -6
    
    ##            global count
    ##            count +=1
    ##            score()
    ##
    ##            if count == 0:
    ##                canvas.create_text(250, 200, text='YOU WIN!')
    ##                tk.update_idletasks()
    ##                tk.update()
    ##                while 1:
    ##                    if ball.hit_bottom ==True:
    ##                        ball.draw()
    ##                        paddle.draw()
    ##                        paddle2.draw()
    ##                        paddle3.draw()
    
    
            if pos[3] >= self.canvas_height:
                self.hit_bottom = True
                canvas.itemconfig(self.id,fill=colors[0])
            if pos[0] <= 0:
                self.x = speed[1]
                canvas.itemconfig(self.id,fill=colors[0])
            if pos[2] >= self.canvas_width:
                self.x = speed2[1]
                canvas.itemconfig(self.id,fill=colors[0])
                zahyou=self.canvas.coords(self.id)##←壁に当たったらワープ←座標取り出し
                a=[-500,-450,-400,-350]##←壁に当たったらワープ
                random.shuffle(a)##←壁に当たったらワープ
                self.canvas.move(self.id,a[0],-(zahyou[1]))##←壁に当
    
    ###障害物1###
    class Board:
        def __init__(self, canvas, color):
            self.canvas = canvas
            self.id = canvas.create_rectangle(150, -200, 250, -210, fill=color)
            self.canvas.move(self.id, 200, 300)
            self.x = 0
            self.canvas_width = self.canvas.winfo_width()
            self.started = False
    
        def draw(self):#ボードの描画
            self.canvas.move(self.id, self.x, 0)
            pos =self.canvas.coords(self.id)
            if pos[0] <= 0:
                self.x =0
            elif pos[2] >= self.canvas_width:
                self.x = 0
    ###障害物2###
    class Board2:
        def __init__(self, canvas, color):
            self.canvas = canvas
            self.id = canvas.create_rectangle(-100, -150, 0, -160, fill=color)
            self.canvas.move(self.id, 200, 300)
            self.x = 0
            self.canvas_width = self.canvas.winfo_width()
    
        def draw(self):#ボードの描画
            self.canvas.move(self.id, self.x, 0)
            pos =self.canvas.coords(self.id)
            if pos[0] <= 0:
                self.x =0
            elif pos[2] >= self.canvas_width:
                self.x = 0
    
    class Paddle:
        def __init__(self, canvas, color):
            self.canvas = canvas
            self.id = canvas.create_rectangle(0, 0, 150, 10, fill=color)
            self.canvas.move(self.id, 400, 700)
            self.x = 0
            self.y = 0
    
            self.canvas_width = self.canvas.winfo_width()
            self.started = False
            self.canvas.bind_all('<Up>', self.turn_up)
            self.canvas.bind_all('<Down>', self.turn_down)
            self.canvas.bind_all('<Left>', self.turn_left)
            self.canvas.bind_all('<Right>', self.turn_right)
            self.canvas.bind_all('<s>', self.start_game)#クリックしたときにスタート
    
    
    
        def draw(self):#ラケットの描画
            self.canvas.move(self.id, self.x, self.y)
            pos =self.canvas.coords(self.id)
            if pos[0] <= 0:##←パドルが壁を貫通しないようにする
                self.x =0
            elif pos[2] >= self.canvas_width:##←パドルが壁を貫通しないようにする
                self.x = 0
            #こうすべきかな。
                pos =self.canvas.coords(self.id)
            elif pos[1] <= 0:##←パドルが壁を貫通しないようにする
                self.y =0
            elif pos[3]>= 800:##←パドルが壁を貫通しないようにする
                self.y = 0
            global lost
            if lost == False:
                self.canvas.after(10, self.draw)
    
        def turn_up(self, evt):
            self.canvas.move(self.id, self.x, self.y)##←動く範囲指定
            pos = self.canvas.coords(self.id)##←動く範囲指定
            if pos[1] >0:##←動く範囲指定
                self.y = -6
            elif pos[1] <=0:##←動く範囲指定
                self.y =0##←動く範囲指定
    
        def turn_down(self, evt):
            self.canvas.move(self.id, self.x, self.y)##←動く範囲指定
            pos = self.canvas.coords(self.id)##←動く範囲指定
            if pos[3]<800:##←動く範囲指定
                self.y = 6
            elif pos[3]>=800:##←動く範囲指定
                self.y=0##←動く範囲指定
    
        def turn_left(self, evt):
            self.canvas.move(self.id, self.x, self.y)##←動く範囲指定
            pos = self.canvas.coords(self.id)##←動く範囲指定
            if pos[0]>0:
                self.x = -6
            elif pos[0]<=0:##←動く範囲指定
                self.x=0##←動く範囲指定
    
        def turn_right(self, evt):
            self.canvas.move(self.id, self.x, self.y)##←動く範囲指定
            pos = self.canvas.coords(self.id)##←動く範囲指定
            if pos[2]< self.canvas_width:##←動く範囲指定
                self.x = 6
            elif pos[2]>= self.canvas_width:##←動く範囲指定
                self.x=0##←動く範囲指定
    
        def start_game(self, evt):#スタートする関数
            self.started = True
            self.onemore(evt)
    
        def end(self, evt):
            tk.destroy()
    
        def onemore(self, evt):
            while True:#常にループ実行
                if ball.hit_bottom == False and paddle.started == True:#ゲームを続ける条件
                    ball.draw()#ballオブジェクトのdraw関数の呼び出し
                    paddle.draw()#paddleオブジェクトのdraw関数の呼び出し
                    board.draw()
                    board2.draw()
    
                if ball.hit_bottom == True:
                    time.sleep(1)
                    canvas.itemconfig(game_over, state = 'normal')
                    canvas.itemconfig(retry1, state = 'normal')#テキストを表示する条件
                    canvas.itemconfig(retry2, state = 'normal')
                    self.canvas.bind_all('<n>', self.end)
                    self.canvas.bind_all('<y>', self.restart)
    
    
    
                tk.update_idletasks()
                tk.update()
                time.sleep(0.01)
        def restart(self,evt):
            ball.hit_bottom == False
            paddle.started == True
    
    def game_over():
        game_over =canvas.create_text(150, 150, text ='GAME OVER!', font =('Courier', 30), fill='yellow', state ='hidden')
        #ゲームオーバーのテキスト表示,hiddenで隠しておく
        retry1 = canvas.create_text(200, 250, text ='もう一度、ゲームを行いますか', font =('Courier', 20),fill='yellow', state ='hidden')
        retry2 = canvas.create_text(300, 300, text ='(Yes/No)', font =('Courier', 20),fill='yellow', state ='hidden')
    
    
    def start_game_menu():
        global lost, count, ball 
        if lost == True: 
            ball = Ball(canvas, paddle, paddle2,paddle3,"red")
    
    
        lost = False 
        board.draw()
        board2.draw()
        paddle.draw()
        ball.draw()
        count = 0
    
        score()
        canvas.itemconfig(game, text=" ")
        time.sleep(1)
    
    def score():
        canvas.itemconfig(scores, text="Score:"+ str(count))
    
    def start_game(event):
        global lost, count, ball ,paddle
        if lost == True: 
            ball = Ball(canvas, paddle, board2, board,color2)
    
    
        canvas.move(paddle, 400, 600)
        lost = False 
        board.draw()
        board2.draw()
        paddle.draw()
        ball.draw()
        count = 0
    
        score()
        canvas.itemconfig(game, text=" ")
    
        time.sleep(1)
    
    def out():
        tk.destroy()
    def out_keypress(event):
        tk.destroy()
    
    ####
    menu=Menu(tk)
    tk.config(menu=menu)
    filemenu=Menu(menu)
    menu.add_cascade(label="Menu", menu=filemenu)
    filemenu.add_cascade(label="Exit",command=out)
    filemenu.add_cascade(label="Restart",command=start_game_menu)
    
    
    ###各種設定###
    
    paddle = Paddle(canvas, color2)
    board = Board(canvas, color3)
    board2 = Board2(canvas, color4)
    #ラケットの色を青色に設定
    ball = Ball(canvas, paddle, board, board2, color)
    #ボールの色を赤に設定
    
    game = canvas.create_text(500, 300, text=" ", fill="red", font=("Arial", 20))
    
    canvas.bind_all("<s>", start_game)
    canvas.bind_all("<y>", start_game)
    canvas.bind_all("<n>", out_keypress)
    
    
    
    
    tk.mainloop()
    
[/code]

※[原著のページ](https://nostarch.com/pythonforkids)に元となったソースコードのダウンロードリンクがあります


CC BY-SA 3.0
2018-02-04T08:42:51.290
41451
2018-02-04T13:24:40.437
2018-02-04T13:24:40.437
3068
27272
question
python python3
bouncegameの改造　コードのエラーを特定してほしい


画像ファイルがないので実行すると画像エラーでしたが、  
その前に二つのエラーが出てきましたよ。  
0行目を先頭にしてコードをコピーしたとして、  
78行目に

[code]

    self.canvas.move(self.id,a[0],-(zahyou[1])
    
[/code]

とありますが、)が最後につかないと`SyntaxError`になります。

次に、212行目の

[code]

    elif pos[1] <= 0:##←パドルが壁を貫通しないようにする
    
[/code]

ですが、この位置が、elifとelifの間にあります。なぜこの場所に置いたのか  
はっきりとした理由はわかりませんが、これだとエラーになることは間違いあり  
ません。Pythonは、elif文の後は、4マスのインデントを置き、もしそれ以降に  
elif文が続くようなら、elifと同じインデント箇所に置くことはできません。

[code]

        def draw(self):#ラケットの描画
            self.canvas.move(self.id, self.x, self.y)
            pos =self.canvas.coords(self.id)
            if pos[0] <= 0:##←パドルが壁を貫通しないようにする
                self.x =0
            elif pos[2] >= self.canvas_width:##←パドルが壁を貫通しないようにする
                self.x = 0
            #これがおかしいので、
            pos =self.canvas.coords(self.id)
            elif pos[1] <= 0:##←パドルが壁を貫通しないようにする
                self.y =0
            elif pos[3]>= 800:##←パドルが壁を貫通しないようにする
                self.y = 0
            global lost
            if lost == False:
                self.canvas.after(10, self.draw)
    
[/code]

なので、

[code]

    def draw(self):#ラケットの描画
            self.canvas.move(self.id, self.x, self.y)
            pos =self.canvas.coords(self.id)
            if pos[0] <= 0:##←パドルが壁を貫通しないようにする
                self.x =0
            elif pos[2] >= self.canvas_width:##←パドルが壁を貫通しないようにする
                self.x = 0
            #こうすべきかな。
                pos =self.canvas.coords(self.id)
            elif pos[1] <= 0:##←パドルが壁を貫通しないようにする
                self.y =0
            elif pos[3]>= 800:##←パドルが壁を貫通しないようにする
                self.y = 0
            global lost
            if lost == False:
                self.canvas.after(10, self.draw)
    
[/code]

画像ファイルがないというエラーを得たんですが、ネットから適当なgifファイルを取ってきて、コードの名前と一致させました。そのうえで試すと、  

[code]

    NameError: name 'Score' is not defined
    
[/code]

従って、よくコードを見ると、

[code]

    score = Score(canvas, )
    
[/code]

以前にはどこにも`Score`クラスというものが作られておりません。  
これを作ってからの話になりそうです。

[code]

        if self.hit_board(pos)==True:
            self.y=-6
        if self.hit_board_bottom(pos)==True:
            self.y=6
        if self.hit_board2(pos)==True:
            self.y=-6
        if self.hit_board2_bottom(pos)==True:
            self.y=6
        if self.hit_paddle(pos) == True:
            self.y = -6
    
[/code]

ここで、ボールの衝突判定とその後の行動を起こしているのだと思います。  
まずはここを疑うべきですね。この６を＋－反転させたり、ヒットの条件を  
変えたりして、挙動の違いを確認してみてください。また、if文が複数  
並んでいますが、せっかく一つの条件を満たしても、別の条件をすぐ  
下のif文で満たしたら、その影響が及んでしまいます。したがって、  
最初のif文以外はelif文にするのも試してみてください。この辺は、  
コーディネートシステムの基本を考えて、y座標は-だと、上に行き、  
+だと下に行くという逆の動きに注意してください。tkinterでも  
こうなのかはわかりませんが。とりあえずここまで。


CC BY-SA 3.0
2018-02-04T12:19:09.000
41455
2018-02-04T13:19:29.700
2018-02-04T13:19:29.700
24284
24284
41451
answer