ゲーム実装で身に付くプログラミング 2

色ブロックを表示する

色ブロックを表示する

次の「color.py」「main.py」ファイルをコーディングして「main.py」を「▷」で実行し、タイトル画像をクリックすると図4のように色ブロックが現れます。

図4:色ブロック

Colorクラスを実装する

新たにcolor.pyファイルを新規作成します。color.pyファイルでは色ブロック1個ずつの「Color」クラスを実装します。まずはコンストラクタだけ実装します。

・サンプルコード「color.py」ファイル

import random
# 色ブロック1つ分のクラス
class Color():
  image_id = -1
  def __init__(self,color,width,height):
    self.x = random.randint(100,width-100)
    self.y = random.randint(100,height-100)
    self.dx = random.choice([-3,-2,-1,1,2,3])
    self.dy = random.choice([-3,-2,-1,1,2,3])
    self.color = color

【サンプルコードの解説】
乱数のrandomモジュールを読み込みます。
ユニークな(一意の)image_idプロパティを宣言します。ただしデフォルトではユニークではない-1です。
コンストラクタで色ブロック1つのランダムな(x,y)座標プロパティ、増分(Delta)のランダムな(dx,dy)プロパティ、色番号のcolorプロパティをセットします。

メインコードをコーディングする

赤緑青の色ブロック画像を読み込んで表示します。色ブロックは画像ですが、ラベルではなく1つのキャンバスの中に複数表示します。まだ色ブロックは移動しませんが、クリックして背景色に色を混ぜて真っ白になったらステージクリアです(図5)。

図5:ステージクリア

・サンプルコード「main.py」ファイル

import tkinter as tk
import random
from color import Color
(中略)
# ゲームクラス
class Game():
  # コンストラクタ
  def __init__(self):
    self.stage = 2
  # タイトル表示
  def title(self):
(中略)
  # ゲーム開始
  def on_start(self,event):
    self.label.destroy()
    # 画像読み込み
    files = ["images/0.png", "images/1.png", "images/2.png"]
    self.colors = []
    # PhotoImage保持
    self.images = []
    # CanvasアイテムID
    self.image_ids = []  
    self.canvas = tk.Canvas(root, bg="white")
    self.canvas.pack(fill="both", expand=True)
    for i in range(self.stage*3):
      self.colors.append(Color(i%3,width,height))
      self.img = tk.PhotoImage(file=files[i%3])
      self.images.append(self.img)
      self.colors[i].image_id = self.canvas.create_image(self.colors[i].x, self.colors[i].y, image=self.img)
      self.image_ids.append(self.colors[i].image_id)
      self.canvas.tag_bind(self.colors[i].image_id,"<Button-1>", self.on_click)
    self.rgb = [0,0,0]
    num = self.stage-1
    while self.rgb[0] == self.rgb[1]:
      self.rgb = [random.randint(0,num),random.randint(0,num),random.randint(0,num)]
    self.bg_color()
    self.update()
  # 背景色
  def bg_color(self):
(中略)
    if self.rgb[0] == self.rgb[1] and self.rgb[0] == self.rgb[2]:
      self.stage_clear()
  # ゲームのメインループ
  def update(self):
    if not self.canvas.winfo_exists():
      return
  # 色ブロックのクリック
  def on_click(self,event):
    item_id = event.widget.find_withtag("current")
    if item_id:
      for c in self.colors:
        if c.image_id == item_id[0]:
          self.rgb[c.color] += 1
          c.x += c.dx*width
          break
    self.bg_color()
  # ステージクリア
  def stage_clear(self):
    self.canvas.destroy()
    self.stage += 1
    img = tk.PhotoImage(file="images/StageClear.png")
    self.label = tk.Label(root, image=img)
    self.label.pack(expand=True)
    self.label.image = img
    self.label.bind("<Button-1>", self.on_start)
# ゲームクラスのインスタンス生成
game = Game()
# ゲームクラスのタイトル画面へ
game.title()
# tkinterのメインループ
root.mainloop()

【サンプルコードの解説】
色ブロックごとにColorクラスのインスタンスを生成し、colorsプロパティ(配列)に追加します。
PhotoImageクラスで色ブロック画像を読み込み、参照をimagesプロパティに保持しておくことで画像が表示されなくなるのを防ぎます。
image_idプロパティで画像のユニークなイメージID(整数)を取得します。
イメージIDの画像がクリックされたら「on_click」メソッドを呼び出します。
bg_colorメソッドではrgb配列の3要素がすべて同じ値になった場合、真っ白になったと判定してステージクリアとします。
on_clickメソッドではクリックされたブロックのアイテム番号を取得し、colors配列からimage_idが一致する要素を特定します。該当する色の値をインクリメントして背景色に反映させます。あわせて、クリックされたブロックは画面外へ移動させ非表示にします。
stage_clearメソッドはtitleメソッドと同様にラベルへステージクリア画像を表示します。画像をクリックするとメインゲームが再開されます。

色ブロックを移動しゲームを完成させる

色ブロックを移動させるようにし、色ブロックが1個もなくなったらゲームオーバーです(図6)。これでゲームは完成です。サウンドがないのは寂しかったでしょうか?

図6:ゲームオーバー

色ブロックの移動

Colorクラスのupdateメソッドで色ブロックの座標を増分だけ加算し、画面外に出たら戻り値として0を返します。それ以外は1を返します。

・サンプルコード「color.py」ファイル

import random
# 色ブロック1つ分のクラス
class Color():
  image_id = -1
  def __init__(self,color,width,height):
(中略)
  # 毎フレーム更新
  def update(self,width,height):
    self.x += self.dx
    self.y += self.dy
    if self.x < -50 or self.y < -50 or self.x > width+50 or self.y > height+50:
      return 0
    return 1

毎フレームupdateメソッドを呼び出す

毎フレームupdateをメインループで呼び出すには、afterメソッドで50ミリ秒後に自分自身(updateメソッド)を再度呼び出すだけです。

・サンプルコード「main.py」ファイル

(前略)
# ゲームクラス
class Game():
  # コンストラクタ
  def __init__(self):
    self.stage = 2
  # タイトル表示
  def title(self):
(中略)
  # ゲーム開始
  def on_start(self,event):
(中略)
  # 背景色
  def bg_color(self):
(中略)
  # ゲームのメインループ
  def update(self):
    if not self.canvas.winfo_exists():
      return
    flg = 0
    for i in range(len(self.image_ids)):
      flg += self.colors[i].update(width,height)
      self.canvas.coords(self.image_ids[i], self.colors[i].x, self.colors[i].y)
    if flg == 0:
      self.game_over()
    else:
      # 約20FPS
      root.after(50, self.update)
  # 色ブロックのクリック
  def on_click(self,event):
(中略)
  # ステージクリア
  def stage_clear(self):
(中略)
  # ゲームオーバー
  def game_over(self):
    self.canvas.destroy()
    img = tk.PhotoImage(file="images/GameOver.png")
    self.label = tk.Label(root, image=img)
    self.label.pack(expand=True)
    self.label.image = img
    self.label.bind("<Button-1>", self.on_title)
  # タイトル画像クリック時
  def on_title(self,event):
    self.label.destroy()
    self.title()
# ゲームクラスのインスタンス生成
game = Game()
# ゲームクラスのタイトル画面へ
game.title()
# tkinterのメインループ
root.mainloop()

【サンプルコードの解説】
ゲームのメインループのupdateメソッドでColorクラスのupdateを呼び出して移動し、画面外に出ていなければ戻り値でflg変数に加算して0より大きくなります。
coordsメソッドでイメージIDの色ブロックをその位置に配置します。
flgが0なら全ての色ブロックが画面外に出ているのでゲームオーバー処理し、そうでなければupdateメソッドをループします。
「game_over」メソッドではtitleメソッドと似たようなことをします。「GameOver.png」を表示し、それをクリックしたら「on_title」メソッドを呼び出し、ラベルを削除してタイトル画面に遷移します。

【コラム】携帯電話パソコン

スマホは電話番号で個人を特定して認証できるSMSなどがあります。そこでパソコンにも電話機能があれば? スマホアプリをパソコンで使えるAndroid PCが出るそうですが電話機能なしです。SIMが入れられるノートパソコンもありますがSMS的なことはできないようです。

おわりに

今回は2026年で最も人気のあるプログラミング言語の1つであるPythonでデスクトップのColorゲームをプログラミングしました。Pythonが使えたら様々な用途に使えるのでとても便利です。

この記事をシェアしてください

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored