へたっぴpythonista

ド素人pythonistaとして、日々の学習成果や気づいたことについて書きます。

アイテムを動かす

前回は画面にアイテムを表示するまでを勉強したので、今回は表示したアイテムを動かして簡単なアニメーションをつくります。

アイテムを動かすためにいじるのは、アイテムの描画位置(x、y)の部分です。

前回のコードで言うとpygame.draw.rectの第三引数にあたるリスト[50,50,50,50]のうち、前二つです。

f:id:ruriohead:20130709234020p:plain

この部分を、例えばrect_x,rect_y等の値に置き換えて、その挙動を指示するコードを書くことになります。

失敗1

f:id:ruriohead:20130709235810p:plain

上のコードを実行しても図形は動きません。なぜならrect_x,rect_yがwhileループ内のローカル変数になっているからです。 このためrect_x,rect_yへの変化はループ内の処理が終わって最初の行に戻った瞬間に破棄され、次のループで再び rect_x,rect_yに50が代入されることになります。

f:id:ruriohead:20130710001551p:plain

これを避けるためにrect_x,rect_yをwhileループの外に記述し、グローバル変数として扱わせます。

成功

f:id:ruriohead:20130710001949p:plain

後はclock.tick()で描写速度を指定すればOKです。

 

メインループの概要と注意点

久々にpygame

 

今回はメインループの作り方と、ウィンドウを閉じさせる方法について。

メインループ

f:id:ruriohead:20130709093126p:plain

 

前回のループではwhile True:を利用してループを作りましたが、上のようにFalseを基準としてループを制御することもできます。

 

本文にはまず、EVENT1として、ユーザーの行動に対して反応するためのforループを書きました。ユーザーの行動をリストに反映させ、それに応じたイベントを起こすという仕組みです。

今のところ、中味はループを終了させるためにdoneの値を変化させるものだけです。

 

その次のsurface.fill(color)はそのまま、画面(surface)を色(color)で満たす(fill)ためのコマンドです。surfaceには自分で作成した画面を、colorにはあらかじめ自分で定義した色があればその引数を、なければRGB法で([50,50,50,])の様に指定します。

追記:surface.fill(color)は必ず描画の前に行うようにしてください。図形等を描いたのちにfillを行うと図形等は見えなくなります。

例えば○ surface.fill() → pygame.draw.circle 画面を塗りつぶした後に円を描く

    × pygame.draw.circle →surface.fill()    描いた円もろとも画面を塗りつぶす

 

さらにその次は図形の描画です、簡単な図形ならpygameに備わった描画メソッドで作れます。特に説明することはないかな。

 

すべて書き終えたら、最後にpygame.display.flip()かpygame.display.update()で画面を更新し、変更を反映します。

 

 

おまけとして、描写速度の調整もしておきます。静止画を描写し続ける場合は書かなくても分かりませんが、アニメーションの作成時には大変重要な項目です。一秒間の描写回数を指定することで、アニメーションの速度を変化させます。これを指定しないと、常に最高速で処理を行うことになり、アニメーションは目に見えない速さで変化してしまいますのでご注意。

描写速度の指定にはpygame.time.Clock()を使います。clock=pygame.time.Clock()等と指定しておくと分かりやすいと思います。後はループ内でclock.tick(描写回数)と記述すれば完了です。

 

これを動作させれば、

f:id:ruriohead:20130709094404p:plain

 

さて、このディスプレイには重大なミスがあります。

 

 

このディスプレイ、閉じられません!!

 

試しに右上の×ボタンをクリックしてみると・・・一向にディスプレイが閉じる気配がありません。

ゲームを作ってるときは、ソースコードを閉じてしまえば一緒に閉じるのでまだいいですが、実際にプレイする時になってAlt+F4でないと閉じられないゲームになっていいわけがないので直しましょう。

 

やることはただ一つ。ループを抜けた後にウィンドウを閉じるためのコードを書くだけです。

上のコードでは×ボタンでループから抜けるコードは書きましたが、抜けた後のコードが書けていたために、「ループは抜けたけど、そのあとは何するの?」という状態になっていたのです。

f:id:ruriohead:20130709095328p:plain

このように最終行にpygame.quit()と記述するだけで、ウィンドウを閉じられるようになります。

projectEuler32をpythonで

Problem32「パンデジタル積」

すべての桁に 1 から n が一度だけ使われている数をn桁の数がパンデジタル (pandigital) であるということにしよう: 例えば5桁の数 15234 は1から5のパンデジタルである.

7254 は面白い性質を持っている. 39 × 186 = 7254 と書け, 掛けられる数, 掛ける数, 積が1から9のパンデジタルとなる.

掛けられる数/掛ける数/積が1から9のパンデジタルとなるような積の総和を求めよ.

HINT: いくつかの積は, 1通り以上の掛けられる数/掛ける数/積の組み合わせを持つが1回だけ数え上げよ.

 

細かいミスはあったもののそこまで苦戦はしませんでした。

解答

f:id:ruriohead:20130630130707p:plain

 

注意するべきは、求める「掛けられる数、掛ける数、積」のパターンには「一桁、四桁、四桁」と「二桁、三桁、四桁」の2パターンが存在することです。

実行すると、

f:id:ruriohead:20130630130351p:plain

となりました。これでOK。

flip()とupdate()

前々回の最後に「display.flip() かdisplay.update()を使う」と書いていたのが、曖昧に思ったので調べてみました。

flip()はDisplay surface全体を更新するメソッドです。それ以前のコードによって画面上に変化があってもなくても、画面全体を完全に更新します。従って、変化が一部で起こっている場合には、更新の必要がない画像やアニメーションも同時に更新することになり、無駄です。

その無駄を解消するために存在するのがupdate()です。update()ではコードによって変化は加えられた部分だけをピンポイントで更新します。更新していない部分は再読み込みすることなくそのままなので、より速く、軽い描写が可能になるのですね。

 

初心者向けのサイトを見ると、全部flip()で良いとする人も見かけますが、あくまで初心者向け。後のことを考えて今から区別をつけておきます。

 

追記:7/9

update()は()に範囲を指定しないとflip()と全く変わらないようです。

図形の位置や大きさから計算した値のリスト等を()に入れて初めて意味のあるコマンドなんですね。知らなかった。

Surfaceって何だ?

今日はSurfaceについて学習しました。

Surface

こちらのサイトを参考にしました。→初心者のためのpygame

まとめると

  • Surfaceは実際のゲーム画面にあたるDisplay surfaceと、その他のSurfaceに分けられる。
  • Surfaceは白紙の紙切れにあたり、その上にテキストや図形、画像等を描くことができる。
  • また、Surfaceはそれ自身も画像として扱うことができる。作成したSurfaceをDisplay surfaceに出力すれば、ゲームプレイヤーがその画像を見ることができるようになる。

 試しにSurfaceを作成してDisplay surfaceに出力してみよう。

f:id:ruriohead:20130627174410p:plain

コードを走らせると、

f:id:ruriohead:20130627174528p:plain

このように出力されます。左側の黒い部分がSurfaceです。(画像はタカミコーポレーションさんの素材をお借りしました。)

 

おそらくSurfaceにいつも同じ動きをするアニメーションを作成して流用といった使い方をするんだろうと思います。

 

pygameを使ってみよう

インストールしたっきり触っていなかったので今日はpygameをいじってみる。

ウィンドウを作る

f:id:ruriohead:20130626171113p:plain

まずpygame全体をimportして、その中ローカル属性からトップレベル全体を取り出す。

次いでpygame.imit()でpygame全体を初期化する。多分複数のモジュールによって構成されたスクリプトとかで、インポートミス等が起きるのを防ぐためだろう。

5,6行目はそれぞれ、メインウィンドウのサイズ設定とウィンドウのタイトル設定。ここまで入力して走らせると、

f:id:ruriohead:20130626172841p:plain

これでメインウィンドウが完成した。

文字やイメージを表示する

さて、作ったウィンドウに文字やイメージを表示してみる。

f:id:ruriohead:20130626181153p:plain

まずはテキスト。

テキストを表示するために、始めに7行目の様にフォントを指定する。

pygameがすでに持っているフォント情報を適用するにはSysFont、自分で作成したり、ネット等でダウンロードしたフォント情報を適用するにはFontと入力します。()内の第一因数は使用するフォントの名前(または場所)、第二因数は文字サイズになる。また、第一因数をNoneに指定するとpygameデフォルトのフォントが適用される。

フォントを指定したら、8行目のようにテキストをレンダリング(画像・映像化)する。()内は第一因数がレンダリングするテキスト、第二因数がアンチエイリアス(文字を滑らかに出力する)の有無、第三因数が色(RGB方式)となっている。

 

次に画像。

画像は9行目のようにファイルの位置を指定すればいい。

convert()は画像ファイルのpygame上でのフォーマット(≠画像ファイルそのもののフォーマット)をプログラムを動作させているPCの環境に合わせるために必要。これが無いとゲームプレイ中に画像が表示されるたびに一々フォーマットを合わせなくてはいけないので、スムーズな描画ができなくなるらしい。だから、わざとでない限り付け加えておくべきだろう。

また、使用する画像がアイコン等のように透明な部分を持つものであった場合は、convert_alpha()と書けば、その透明部分が透明なまま画像がロードされる。

 

 

以上の処理が済んだら、いよいよ出力。ボディ部分を書いていく。

ボディはwhile True: ,while 1:等の常に正しいwhileループを利用して無限ループを形成する形で書くことになる。以降このループの中に動作に関するコードを記載していくことになる。

テキストや画像の出力にはscreen.blit(file,(x,y))を使う。第一因数はレンダリングしたテキスト、あるいは画像を、第二因数には画面左上端を(0,0)とする出力位置を指定する。

最後にpygame.display.flip()、またはpygame.display.update()と書けば完成。

f:id:ruriohead:20130626203355p:plain

 

ProjectEuler11をpythonで解く

ProjectEulerを20番台まで解き進め、そろそろ未解答の問題が目立ってきたので片づけよう。

 

手始めにProblem11。

Project11

上の 20×20 の格子のうち, 対角線に沿って4つの数字が赤くマークされている.
08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48

それらの数字の積は 26 × 63 × 78 × 14 = 1788696 となる.

上の 20×20 の格子のうち, 上下左右斜めのいずれかの方向で連続する4つの数字の積のうち最大のものはいくつか?

 

以前は格子をリストに置き換える方法が分からず断念した。 

失敗

splitメソッドを2回使えば簡単だと思ったんだけど、リストがsplitメソッドに対応していないため、2回目のsplitメソッドがエラーになってしまった。f:id:ruriohead:20130625012622p:plainリスト作成成功

ネストした空のリストを作って、データを入力させることにしたら上手くいった。

f:id:ruriohead:20130625221548p:plain

こうすると・・・

f:id:ruriohead:20130625221651p:plain

こうなる。さぁ次に進もう。

失敗2

縦横斜めに最大の積を求める関数を作ってと(斜めは2通りあることに注意)、

f:id:ruriohead:20130625232100p:plain

これを実行すると・・・あれ?答え違う?なんでだよ~!

 

 

・・・全然解決方法が思い浮かばん。とりあえず保留(再)します。