UdemyでPythonを勉強した結果を残すブログ。

40歳でプログラミング始めて転職までいけるのかを実録してみます。

DAY22 Turtleモジュールを使ってピンポンゲームを作る

Angela Yu先生による22日目の講座実践。

今回はTurtleモジュールをさらに応用してレトロゲームのピンポンゲームを作ります。

 

 

f:id:GO-py:20220407121049p:plain

pong game

 

両端にバーがあって、キーボードで動かして、

ボールを跳ね返していき、はねかえせずにバーを通り過ぎてしまったら負け。

ゲーセンのホッケーみたいな感じですね。

 

それでは始めます。

流れとしては

  • スクリーンを用意して、上部にスコアを表示。また負けたらメッセージを表示
  • 両端にバーを表示。キーボードで動かせるようにする
  • 常に動くボールを用意。上下とバーに当たったら跳ね返るようにする。

 

それぞれのクラスの定義とスクリーンの基本表示

 

main.py

from turtle import Screen
from bar import Bar
from scoreboard import ScoreBoard
from ball import Ball
import time

screen = Screen()
screen.bgcolor("black")
screen.setup(800, 600)
screen.title("PONG")

screen.tracer(0)

ball = Ball()
score_board = ScoreBoard()

ざっくりとここまで。

from …のbar,scoreboard, ball部分とそれぞれの定義は

後でそれぞれを書いていくので今書いても、書かなくても。

 

前回と同じくscreenでスクリーンを定義して、setupでサイズを設定。

背景を黒に、ウィンドウのタイトルをPONGにしています。

 

またアニメーションをスムーズにするためのscreen.tracer(0)も記載。

後でscreen.update()も書きます。

 


screen.listen()
screen.onkey(bar_r.go_up, "[")
screen.onkey(bar_r.go_down, "]")
screen.onkey(bar_l.go_up, "q")
screen.onkey(bar_l.go_down, "a")

キーボード入力で動くようにするのはscreen.listen()からの.onkey設定で。

これもとりあえず右のバーは[ ]を押すことで上下へ動くように、

左のバーはq a で動くように設定。

 


game_on = True
while game_on:

screen.update()

screen.exitonclick()

 

とりあえずゲームが進む基本的なところまで。

 

 

両端にバーを表示する

次にbarのクラスをコーディングしていきます。

bar.py

 

from turtle import Turtle

class Bar(Turtle):
def __init__(self,x, y):
super(Bar, self).__init__()
self.shape("square")
self.color("white")
self.penup()
self.shapesize(5, 1)
self.goto(x,y)

turtleをインポートして機能を継承できるようにBar()の中にTurtleを入れる。

super().__init__()を結構忘れてしまうのでセットで覚えておいた方がいい。

以下部分は前回もやったけどshapesize(5,1)部分はやってないので説明すると

そのshapeの縦横比率を設定できる。

 

main.pyにもbarを定義する。

main.pyに書くよ
bar_l = Bar(-360, 40)
bar_r = Bar(350, 40)

 

スネークゲームのときは1つ1つが前を追うように動いてたので設定しなかったけど

今回のバーの場合は上下しか動かないのでfor . in で1こずつ作る必要がない。

shapesize()で設定するだけで縦がブロック5個分の長さを持つバーが出来上がる。

 

上下に動くファンクションを作る

バーのy座標を取得して、それより20上、または下にいくようにする。

今回のdef のなまえはmain.pyに書いたscreen.onkeyの最初の関数と同じもので。


def go_up(self):
new_y = self.ycor() + 20
self.goto(self.xcor(), new_y)

def go_down(self):
new_y = self.ycor() - 20
self.goto(self.xcor(), new_y)

 

これでbar.pyは終わり。

 

 

ball.pyでボールクラスを作る

ball.py

まずは初期値を設定する。ボールの大きさ、色などに加えて

常に動く数値も設定する。

 

from turtle import Turtle

class Ball(Turtle):
def __init__(self):
super(Ball, self).__init__()
self.shape("circle")
self.color("yellow")
self.shapesize(1)
self.penup()
self.x_move = 10
self.y_move = 10

 

次に常に動く機能を実装する

現在のxとyの位置を取得して、上に設定したx_moveとy_move分動くように。


def move(self):
new_x = self.xcor() + self.x_move
new_y = self.ycor() + self.y_move
self.goto(new_x, new_y)

 

当たったら跳ね返る機能を実装する

ycor()でスクリーンサイズからはみ出たら、y_move に -1をかけて逆にいくようにする。

上に行ったら 10 x -1 で-10で下にいくように。

 


def bounce_y(self):
self.y_move *= -1

同じ要領でバーに当たったら跳ね返る機能も実装。


def bounce_x(self):
self.x_move *= -1

 

最後に、どちらかが勝った時はボールが真ん中に戻るようにする機能も実装。


def ball_reset(self):
self.goto(0, 0)
self.ball_speed = 0.1
self.bounce_x()

 

これで終わり。

 

スコア・メッセージを表示するクラスの定義

最後のクラスでスコアボードを実装。

from turtle import Turtle
import time

class ScoreBoard(Turtle):
def __init__(self):
super(ScoreBoard, self).__init__()
self.penup()
self.ht()
self.color("white")
self.l_score = 0
self.r_score = 0
self.write_score()

def write_score(self):
self.clear()
self.goto(-100, 260)
self.write(self.l_score, align="center", font=("Futura", 30, "italic"))
self.goto(100, 260)
self.write(self.r_score, align="center", font=("Futura", 30, "italic"))

 

初期値は0点にして、init ではなく別でスコアを書く機能を実装しています。

勝ったときにスコアを書き換えるので流用するためです。

書くときは毎回文字をクリアしてから指定の場所にいって、書いて、いって、書いて。

 

どちらかが勝ったときに点数を加算し、メッセージを書いて少しポーズする

最後にどちらかが勝ったときにポイントを追加する。

udemyにはなかったけど左の勝ち!みたいなメッセージ欲しいなと思って

昨日追加。

右と左で処理を分けたいので、アーギュメントがrightかleftで動作を変えるようにする。

 


def get_point(self, player):
self.goto(0, 0)
if player == "left":
self.l_score += 1
self.write("Left Player WIN!", align="center", font=("Futura", 45, "italic"))
elif player == "right":
self.r_score += 1
self.write("Right Player WIN!", align="center", font=("Futura", 45, "italic"))
time.sleep(3)
self.write_score()

 

 

再度main.pyにて動作を実装していく

再度main.pyに戻って、whileの内側に作った機能を盛り込んでいきます。

 

  • ボールが動き続ける
  • ボールが上か下の端にいったら跳ね返る
  • バーにあたったら跳ね返る
  • ボールが右端、または左端にいったら勝利者にポイントとメッセージが表示

これをそのままコーディングしていきます。

 


game_on = True
while game_on:
ball.move()

if ball.ycor() >= 280 or ball.ycor() <= -280:
ball.bounce_y()

if ball.xcor() >= 400:
score_board.get_point("left")
ball.ball_reset()

if ball.xcor() <= -400:
score_board.get_point("right")
ball.ball_reset()

if bar_l.distance(ball) < 30 and ball.xcor() <= -340 or bar_r.distance(ball) < 30 and ball.xcor() >= 310:
ball.bounce_x()
screen.update()

screen.exitonclick()

 

これでほぼ完成!

 

 

長く続くほどにボールスピードの上がる機能を実装

同じスピードだと上手くなるごとに延々と続けれる可能性が出てくるので、

ボールのスピードを上げていくようにします。

 

まずball.pyの__init__部分にball_speedを定義。


class Ball(Turtle):
def __init__(self):
super(Ball, self).__init__()
self.shape("circle")
self.color("yellow")
self.shapesize(1)
self.penup()
self.x_move = 10
self.y_move = 10
self.ball_speed = 0.1

 

跳ね返るごとに少しずつ早くしていきます。

ただ、どちらかが負けたときにはボールスピードを初期値に戻します。

 


def bounce_x(self):
self.x_move *= -1
self.ball_speed *= 0.9

def ball_reset(self):
self.goto(0, 0)
self.ball_speed = 0.1
self.bounce_x()

 

この設定を実装するように、main.pyで設定。

 


game_on = True
while game_on:
time.sleep(ball.ball_speed)
ball.move()

 

これで完成!

 

 

全てのコードは

main.py

from turtle import Screen
from bar import Bar
from scoreboard import ScoreBoard
from ball import Ball
import time

screen = Screen()
screen.bgcolor("black")
screen.setup(800, 600)
screen.title("PONG")

screen.tracer(0)

ball = Ball()
score_board = ScoreBoard()

bar_l = Bar(-360, 40)
bar_r = Bar(350, 40)

screen.listen()
screen.onkey(bar_r.go_up, "[")
screen.onkey(bar_r.go_down, "]")
screen.onkey(bar_l.go_up, "q")
screen.onkey(bar_l.go_down, "a")

game_on = True
while game_on:
time.sleep(ball.ball_speed)
ball.move()

if ball.ycor() >= 280 or ball.ycor() <= -280:
ball.bounce_y()

if ball.xcor() >= 400:
score_board.get_point("left")
ball.ball_reset()

if ball.xcor() <= -400:
score_board.get_point("right")
ball.ball_reset()

if bar_l.distance(ball) < 30 and ball.xcor() <= -340 or bar_r.distance(ball) < 30 and ball.xcor() >= 310:
ball.bounce_x()
screen.update()

screen.exitonclick()


ball.py

from turtle import Turtle


class Ball(Turtle):
def __init__(self):
super(Ball, self).__init__()
self.shape("circle")
self.color("yellow")
self.shapesize(1)
self.penup()
self.x_move = 10
self.y_move = 10
self.ball_speed = 0.1

def move(self):
new_x = self.xcor() + self.x_move
new_y = self.ycor() + self.y_move
self.goto(new_x, new_y)

def bounce_y(self):
self.y_move *= -1

def bounce_x(self):
self.x_move *= -1
self.ball_speed *= 0.9

def ball_reset(self):
self.goto(0, 0)
self.ball_speed = 0.1
self.bounce_x()

bar.py

from turtle import Turtle


class Bar(Turtle):
def __init__(self,x, y):
super(Bar, self).__init__()
self.shape("square")
self.color("grey")
self.penup()
self.shapesize(4, 1)
self.goto(x,y)

def go_up(self):
new_y = self.ycor() + 20
self.goto(self.xcor(), new_y)

def go_down(self):
new_y = self.ycor() - 20
self.goto(self.xcor(), new_y)

scoreboard.py

from turtle import Turtle
import time


class ScoreBoard(Turtle):
def __init__(self):
super(ScoreBoard, self).__init__()
self.penup()
self.ht()
self.color("white")
self.l_score = 0
self.r_score = 0
self.write_score()

def write_score(self):
self.clear()
self.goto(-100, 260)
self.write(self.l_score, align="center", font=("Futura", 30, "italic"))
self.goto(100, 260)
self.write(self.r_score, align="center", font=("Futura", 30, "italic"))

def get_point(self, player):
self.goto(0, 0)
if player == "left":
self.l_score += 1
self.write("Left Player WIN!", align="center", font=("Futura", 45, "italic"))
elif player == "right":
self.r_score += 1
self.write("Right Player WIN!", align="center", font=("Futura", 45, "italic"))
time.sleep(3)
self.write_score()