Python プログラムで動かすフェアリーチェスアプリ開発、連載第6回です。
で機能面はある程度整いました。しかし、現在はコマンドラインでテキストで絵を描いて表示し、ユーザのコマンドで動かしている状態なので、
インタフェース的にもう少し楽しくしたいですねー。
ということで、今回からは OpenGL というものを使って、画面に絵を描いたりマウス入力を受け付けたりできるようにしましょう!
OpenGL とは
OpenGL はコンピュータグラフィックスライブラリのひとつです。
競合は DirectX とか。
Windows と Python で OpenGL をインストールする方法はこちらが参考になります。
ちょっとはまったのですが、ダウンロードしてきた freeglut の中の dll ファイルを C:\Windows\SysWOW64
にコピーしてこないと GLUT が認識されないもようです。
私の場合、いろいろいじりすぎてよくわからないんですが、最終的にSysWOW64
にglut32.dll
を入れれば動いてくれました。
ちなみにglut32.dll
を取り除くとこんなエラーが出ました。
OpenGL.error.NullFunctionError: Attempt to call an undefined function glutInit, check for bool(glutInit) before calling
これは後述のプログラム中のglutInit()
関数を認識できていないことを示します。
何をどこに入れればいいのかはビット数によっても変わってくるみたいなので、
とりあえず当たって砕けろ方式でダウンロードしてきた dll を全部System32
かSysWOW64
にぶち込めば成功した、なんてこともあります。
ちなみに私の実行環境は Windows 10 64bit と Python 3.7.4 on win32 です。
後に Python 3.8.3 / PyOpenGL 3.1.5 に変更するとまた動かなくなってしまいました。
PyOpenGL を公式サイトから直接ダウンロード・解凍して、
OpenGL/DLLS/
以下のfreeglut64.vc9.dll
, freeglut64.vc10.dll
, freeglut64.vc14.dll
を
System32
に入れると動きました。
どれが効いているのかはわかりません。
ちなみに、freeglut32.vc9.dll
とかもありました。
ウィンドウ表示
描いた絵を表示する領域を作ります。
main.py1from OpenGL.GL import *2from OpenGL.GLUT import *34...56WSIZE = 720 # 画面サイズ78...910class Game():11def __init__(self):12...13self.glmain()14...15def main(self):16# while True17...18...19def draw(self):20'''描画コールバック'''21glClearColor(0.6, 0.4, 0.2, 1.0)22glClear(GL_COLOR_BUFFER_BIT)2324def glmain(self):25glutInit(sys.argv)26glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA) # 表示設定27glutInitWindowSize(WSIZE, WSIZE) # 画面サイズ28glutInitWindowPosition(0, 0) # 画面の表示位置29glutCreateWindow(b'Chess') # ウィンドウの名前30glutDisplayFunc(self.draw) # 描画31glutMainLoop()
何をやっているのか詳しく知りたい方は、こちらのサイトなどをご覧ください。
glutMainLoop()
がループ処理をしてくれるので、main()
内のwhile
文は不要になります。
これで茶色い画面が表示されるはずです。
盤面の描画
チェス盤、いわゆるチェッカーボードを描くために、ちょっと準備がいります。
四角形の描画
とりあえず、新しいファイルを作り、四角形を描画する関数を定義します。
utils.py1from OpenGL.GL import *2from OpenGL.GLUT import *34def square(x, y):5'''6正方形を描画する78Parameters9----------10x, y : float11中心の座標.12'''13glPushMatrix() # 変形が及ぶ範囲の開始14glTranslate(x, y, 0) # 以下の対象を平行移動15glBegin(GL_QUADS) # 四角形の描画を宣言16glVertex(-1.0 / 2, -1.0 / 2) # 頂点1の座標17glVertex(1.0 / 2, -1.0 / 2) # 頂点218glVertex(1.0 / 2, 1.0 / 2) # 頂点319glVertex(-1.0 / 2, 1.0 / 2) # 頂点420glEnd() # 描画終了21glPopMatrix() # 変形が及ぶ範囲の終了
glPushMatrix()
とglPopMatrix()
は行列スタックを扱う関数なのですが、
コメントにあるぐらいの理解で使っています。
これがないと変形(glTranslate()
とか)が後ろの図形にまで及んでしまいます。
あと、座標ですが、特に指定しない限り画面は左下(-1.0, -1.0)、右上(1.0, 1.0)となっています。
とりあえず、これを使って四角形を表示してみましょう。
main.py1from utils import *23...45class Game():6...7def draw(self):8'''描画コールバック'''9glClearColor(0.6, 0.4, 0.2, 1.0)10glClear(GL_COLOR_BUFFER_BIT)11glColor(1, 0, 0) # 色の指定12square(0, 0)13glutSwapBuffers() # 強制描画
中心に赤い正方形が表示されたと思います。
さて、チェス盤は 8x8 の計 64 の正方形が並んでいるので、大きさを合わせていきたいのですが、
正方形を縮小するという手段の他にも、描画範囲を広げるという手もあります。
フェアリーチェスでは盤面のサイズが広がる可能性が十二分にあるので、そこへの拡張を考えて後者の方法にしたいと思います。
さきほど、特に指定しない限り画面は左下(-1.0, -1.0)、右上(1.0, 1.0)となっていると言いましたが、
glOrtho()
を用いてこの範囲を指定することができます。
main.py1def glmain(self):2...3glOrtho(-1.0, 8.0, -1.0, 8.0, -4, 4)4glutMainLoop()
これで左下(-1.0, -1.0)、右上(8.0, 8.0)になりました。
この大きさの正方形なら、左下(-0.5, -0.5)、右上(7.5, 7.5)の盤面ができるので、
ちょうどいい感じにマージンもできますね。
互い違いに四角形を配置
チェス盤は暗い色のマスと明るい色のマスが互い違いになって並んでいます。
暗い色のマスの位置を示すリストを利用することにします。
utils.py1dark_squares_list = ([(i, j) for i in range(0, 8, 2) for j in range(0, 8, 2)]2+ [(i, j) for i in range(1, 8, 2) for j in range(1, 8, 2)])34def draw_squares():5'''マス目を描画する'''6for i in range(8):7for j in range(8):8if (i, j) in dark_squares_list:9glColor(0.82, 0.55, 0.28)10square(i, j)11else:12glColor(1.00, 0.81, 0.62)13square(i, j)
リスト内包表記というものを使っています。
range()
の第三引数はスキップで、range(0, 8, 2)
の場合「0-7までひとつおき」という意味になります。
さきほどdraw()
の中で四角形を描画していた部分をdraw_squares()
に書き換えれば、盤面が表示されます。
main.py1class Game():2...3def draw(self):4'''描画コールバック'''5glClearColor(0.6, 0.4, 0.2, 1.0)6glClear(GL_COLOR_BUFFER_BIT)7draw_squares()8glutSwapBuffers() # 強制描画
文字を設置
前後左右がわかりやすいように、
との文字を入れましょう。OpenGL 単体はあまり文字描画に強くない印象ですが、それでも一応間に合ってはいるので大丈夫だと思います。
またやりたくなったら何か手を打つかもしれません。
まずは文字列を描画する関数を定義します。
utils.py1def draw_str(x, y, string, font=GLUT_BITMAP_HELVETICA_18, gap=0.25):2'''3文字列を描画する45Parameters6----------7x, y : float8描画する座標.9string : str10描画する文字列.11font : , default GLUT_BITMAP_HELVETICA_1812フォント.以下から指定.13GLUT_BITMAP_8_BY_1314GLUT_BITMAP_9_BY_1515GLUT_BITMAP_TIMES_ROMAN_1016GLUT_BITMAP_TIMES_ROMAN_2417GLUT_BITMAP_HELVETICA_1018GLUT_BITMAP_HELVETICA_1219GLUT_BITMAP_HELVETICA_1820gap : float, default 0.2521文字間隔.22'''23for k in range(len(string)):24glRasterPos2f(x + gap*k, y) # 描画位置指定25glutBitmapCharacter(font, ord(string[k])) # 文字列描画
文字列描画に関してはこちらが参考になります。
→ 情報メディア実験
次にファイルとランクの文字を描画する関数です。
utils.py1def draw_file():2'''ファイルの文字を描画する'''3glColor(1.0, 1.0, 1.0)4for x in range(8):5draw_str(x, -0.75, chr(x + 97))67def draw_rank():8'''ランクの文字を描画する'''9glColor(1.0, 1.0, 1.0)10for y in range(8):11draw_str(-0.75, y, str(y + 1))
chr()
は文字の番号をもとに文字に変換するもので、
>>> chr(97) == 'a'True
となります(ord()
はその逆です)。
これをdraw()
の中に書き加えていけばOKです。
main.py1class Game():2...3def draw(self):4'''描画コールバック'''5glClearColor(0.6, 0.4, 0.2, 1.0)6glClear(GL_COLOR_BUFFER_BIT)7draw_squares()8draw_file()9draw_rank()10glutSwapBuffers() # 強制描画
これで盤面が表示されたので、は駒の画像をこの上に配置していきます!
最後までご覧いただきありがとうございました