Angela Yu先生による22日目の講座実践。
今回はTurtleモジュールをさらに応用してレトロゲームのピンポンゲームを作ります。
両端にバーがあって、キーボードで動かして、
ボールを跳ね返していき、はねかえせずにバーを通り過ぎてしまったら負け。
ゲーセンのホッケーみたいな感じですね。
それでは始めます。
流れとしては
- スクリーンを用意して、上部にスコアを表示。また負けたらメッセージを表示
- 両端にバーを表示。キーボードで動かせるようにする
- 常に動くボールを用意。上下とバーに当たったら跳ね返るようにする。
それぞれのクラスの定義とスクリーンの基本表示
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()