[WP]no.014 python独習

艦これやりながら作成していたので時間は測定不能の様子

pythonをやってみたかったので、手軽なシューティングゲームを作ってみました。
ゲームが面白いかどうかは別。

グラフィックを扱うため、pygameライブラリを使用。環境はWindows。
cygwinでやろうと考えていましたが、グラフィックが未インストールだったのでWindows版pythonをインストール。

ここではクラスは殆ど扱いません。(よって非常に読みにくい)。
代わりにリストを使用します。Arrayクラスがあるようですが、リストで記述するのが一般のようです。まじか。
→こちらでクラスを使用した記述をしました。http://shonen9th.blog.fc2.com/blog-entry-52.html

単に自由度の高いゲームを作りたい方はHSPをお勧めします。
はじめに、全体のソースコードを示します。空行含めて130行超え程度。
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pygame
from pygame.locals import *
import sys

import random
import math

pygame.init()
screen = pygame.display.set_mode((480,480))

pygame.display.set_caption(u"a")

random.seed()

clock = pygame.time.Clock()

font = pygame.font.SysFont(None, 20)

gamestatus = 1 # started

playerx = 160
playery = 400
playershottime = 0

enemy = list()
playerbullet = list()
enemybullet = list()

score = 0

frame = 0
while True:
screen.fill((0,128,255))

keys = pygame.key.get_pressed()

pygame.display.set_caption(u"Hello "+str(frame))

# input
if gamestatus == 1:
if keys[K_LEFT] and 0 < playerx:
playerx -= 4
if keys[K_RIGHT] and playerx < 320:
playerx += 4
if keys[K_UP] and 0 < playery:
playery -= 4
if keys[K_DOWN] and playery < 480:
playery += 4
if keys[K_SPACE] and playershottime <= 0:
playershottime = 40
playerbullet.append([playerx,playery])

# enemy generator
if gamestatus == 1:
if frame % 100 == 30:
enemy.append([random.uniform(10,310),-30.0,random.randint(0,3),random.uniform(-1,1),0])

# player bullet update
for bl in playerbullet:
pygame.draw.circle(screen,(255,255,0),(int(bl[0]),int(bl[1])),8)
bl[1] -= 9
for em in enemy:
if (bl[0]-em[0])*(bl[0]-em[0])+(bl[1]-em[1])*(bl[1]-em[1])<256:
enemy.remove(em)
score += 100
if bl[1] < -20:
playerbullet.remove(bl)

# enemy bullet update
for bl in enemybullet:
pygame.draw.circle(screen,(255,0,0),(int(bl[0]),int(bl[1])),8)
bl[0] += bl[3] * math.cos(bl[2])
bl[1] += bl[3] * math.sin(bl[2])
dist = (bl[0]-playerx)*(bl[0]-playerx)+(bl[1]-playery)*(bl[1]-playery)
if dist<224:
gamestatus = 2 # gameover
if dist<2000 and gamestatus == 1:
score += 3
if (bl[0]<-20 or 340<bl[0] or bl[1]<-50 or 500<bl[1]):
enemybullet.remove(bl)

#enemy update
for em in enemy:
pygame.draw.rect(screen,(255,128,0),pygame.Rect(em[0]-15,em[1]-15,30,30))
if em[2] == 0:
em[1] += 2
if em[4]%60 == 40 and em[1]<playery:
enemybullet.append([em[0],em[1],math.atan2(playery-em[1],playerx-em[0]),4.0])
if em[2] == 1:
if em[4] < 60:
em[1] += 3.5
else:
em[0] += 2 if em[3]<0 else -2
if em[4]%60 == 10:
enemybullet.append([em[0],em[1],math.atan2(playery-em[1],playerx-em[0]),4.0])
enemybullet.append([em[0],em[1],math.atan2(playery-em[1],playerx-em[0])+math.pi/6,4.0])
enemybullet.append([em[0],em[1],math.atan2(playery-em[1],playerx-em[0])-math.pi/6,4.0])
if em[2] == 2:
em[0] += 2.0 * math.sin(em[3])
em[1] += 2.0 * math.cos(em[3])
if (em[0]<5 or 315<em[0]):
em[3] = -em[3]
if em[4]%60 == 40 and em[1]<playery:
enemybullet.append([em[0],em[1],math.atan2(playery-em[1],playerx-em[0]),4.0])
if em[2] == 3:
em[1] += 5
if em[4]%22 == int(10+5*em[3]):
enemybullet.append([em[0],em[1],0.0,2.5])
enemybullet.append([em[0],em[1],math.pi,2.5])
if (em[0]<-20 or 340<em[0] or em[1]<-50 or 500<em[1]):
enemy.remove(em)
em[4] += 1

# player draw
if gamestatus == 1:
pygame.draw.rect(screen,(255,255,0),pygame.Rect(playerx-15,playery-15,30,30))

# UI
pygame.draw.rect(screen,(0,0,0),pygame.Rect(320,0,160,480))
screen.blit(font.render("Score:"+str(score), True, (255,255,255)),(330,50))

clock.tick(60)
frame +=1
if 0<playershottime:
playershottime -= 1

pygame.display.update()

for event in pygame.event.get():
if event.type == QUIT:
sys.exit()


次のサイトに、ウィンドウを表示するスケルトンプログラムがあります。これを利用しました。
ウィンドウに関する記述についてはここでは省略。

人工知能に関する断創録:ウィンドウを表示する
http://aidiary.hatenablog.com/entry/20080503/1275694192




乱数と三角関数を使用するため、randomとmathのライブラリをimport。
import random
import math



乱数の初期化。
random.seed()



次のような記述を導入すると、フレームレートを一定に保てるらしい。
clock = pygame.time.Clock()
ループ部に、
clock.tick(60)



ゲーム関連の変数を定義。
bullet=弾、ememy=敵、player=プレイヤー
gamestatusは、1で開始、2で終了を意味する。
playershottimeは後で

gamestatus = 1 # started

playerx = 160
playery = 400
playershottime = 0

enemy = list()
playerbullet = list()
enemybullet = list()

score = 0



surface(screen)の画面を塗りつぶす。
screen.fill((0,128,255))

((r,g,b))という記述が気に入らないかもしれない。これはタプル型という要素の値が変更できない配列のようなもの。


文字の描画は「キャンパスに文字列を載せる」ではなく、「文字列を基に文字列の画像を作成し、キャンパスに添付する」イメージ。
なので、blit関数を使うことに。
    screen.blit(font.render("Score:"+str(score), True, (255,255,255)),(330,50))



キー入力とプレイヤーの移動。複数の条件はand orで結合。
pythonではスコープを{}で囲まない代わりにインデントで判別する。
インデントが無くてぐちゃぐちゃ、なソースコードはpython界には存在しない、はず。
    keys = pygame.key.get_pressed()

if gamestatus == 1:
if keys[K_LEFT] and 0 < playerx:
playerx -= 4
if keys[K_RIGHT] and playerx < 320:
playerx += 4
if keys[K_UP] and 0 < playery:
playery -= 4
if keys[K_DOWN] and playery < 480:
playery += 4



キャラクタは四角形で描画。
    # player draw
if gamestatus == 1:
pygame.draw.rect(screen,(255,255,0),pygame.Rect(playerx-15,playery-15,30,30))



敵の生成、弾の生成は後回しにして、敵の動きの記述
    for em in enemy:
pygame.draw.rect(screen,(255,128,0),pygame.Rect(em[0]-15,em[1]-15,30,30))
if em[2] == 0:

if em[2] == 1:

if em[2] == 2:

if em[2] == 3:

if (em[0]<-20 or 340<em[0] or em[1]<-50 or 500<em[1]):
enemy.remove(em)
em[4] += 1

このfor文は他の言語でいうforeachのような記述をしています。emにenemyの各要素が入ることに。
em[0],em[1]に座標、em[2]にタイプ、em[3]に個体乱数、em[4]は残存時間。
画面外にでたら要素を削除。ここでは要素内容で削除させていますが、もちろん要素番号で削除させることもできます。


面倒なのでタイプ1だけ解説
        if em[2] == 1:
if em[4] << 60:
em[1] += 3.5
else:
em[0] += 2 if em[3]<<0 else -2
if em[4]%60 == 10:
enemybullet.append([em[0],em[1],math.atan2(playery-em[1],playerx-em[0]),4.0])
enemybullet.append([em[0],em[1],math.atan2(playery-em[1],playerx-em[0])+math.pi/6,4.0])
enemybullet.append([em[0],em[1],math.atan2(playery-em[1],playerx-em[0])-math.pi/6,4.0])

残存時間が60count未満ならば画面下に3.5移動。
60count以上であるならば、個体乱数によって右または左に移動。また、一定タイミングで弾を3つ射出。
em[0] += 2 if em[3]<0 else -2という記述は三項演算子で、Cで記述すると、em[0] += (em[3]<0)?2:-2;に該当します。
mathの三角関数はラジアンで記述。
list.appendでlistに要素を追加。追加する要素は[a,b,c,...]で記述していますが、これはリスト型の記述。
つまり、リストにリストを格納している形に。
1つのリストの中に整数と実数が乱立していますが、異なる型が入っても問題ないっぽい。


敵の弾
    for bl in enemybullet:
pygame.draw.circle(screen,(255,0,0),(int(bl[0]),int(bl[1])),8)
bl[0] += bl[3] * math.cos(bl[2])
bl[1] += bl[3] * math.sin(bl[2])
dist = (bl[0]-playerx)*(bl[0]-playerx)+(bl[1]-playery)*(bl[1]-playery)
if dist<224:
gamestatus = 2 # gameover
if dist<2000 and gamestatus == 1:
score += 3
if (bl[0]<-20 or 340<bl[0] or bl[1]<-50 or 500<bl[1]):
enemybullet.remove(bl)

bl[0],bl[1]座標、bl[2]角度、bl[3]速度。
弾とプレイヤーの距離で当たり判定。distは距離の2乗が格納される。
弾に近づくと得点が上がります。グレイズ


プレイヤーの弾。標的も複数あるので2重ループであること、直進しかしない以外は敵の弾と大差ないですね?
    for bl in playerbullet:
pygame.draw.circle(screen,(255,255,0),(int(bl[0]),int(bl[1])),8)
bl[1] -= 9
for em in enemy:
if (bl[0]-em[0])*(bl[0]-em[0])+(bl[1]-em[1])*(bl[1]-em[1])<256:
enemy.remove(em)
score += 100
if bl[1] < -20:
playerbullet.remove(bl)



プレイヤーの弾射出
    if gamestatus == 1:
if keys[K_SPACE] and playershottime <= 0:
playershottime = 40
playerbullet.append([playerx,playery])
if 0<playershottime:
playershottime -= 1

playershottimeが0でなければ射出不可。射出直後、playershottime=40になり、ループの度にplayershottimeは1ずつ減る。
最大でも40countごとにしか弾を撃てない仕様。


敵の生成
    if gamestatus == 1:
if frame % 100 == 30:
enemy.append([random.uniform(10,310),-30.0,random.randint(0,3),random.uniform(-1,1),0])

100countに1回の頻度。



次はクラスを使用してみようかな。いや、使うべきです。




python(pygame)の実行に関して(windows)。
設定次第ではダブルクリックでも起動できますが、コマンドラインからの起動を推奨。エラーが見えないため。

「ダブルクリックで起動して、エラーもみたい!」というわがまま氏はバッチファイル(.bat .cmd)がお勧め。
テキストエディタで次を記述して、拡張子「.bat」で保存。バッチファイルをダブルクリックでおk。
python file.py
pause





pygameリファレンス
http://www.pygame.org/docs/index.html
pythonチュートリアル
http://docs.python.jp/3/tutorial/index.html
python標準ライブラリ
http://docs.python.jp/2/library/index.html

スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

tag : Python

コメントの投稿

非公開コメント

プロフィール

舞葉(ぶよう)

Author:舞葉(ぶよう)
github.io
はてなブログ(競プロ)

古い記事のソースコードは色分けしていないので、高機能テキストエディタに貼り付けたほうが見やすいかも。

検索フォーム
このブログについて
自分がつまづいた話題、なんとなく書きたいと思ったこと、ググったけど殆ど資料なかったぞオイ な話等をアップする予定。通りすがりでも、参考になっていただければと。プログラムの例外入力、メモリリークは責任負いません。投稿された記事は修正・削除する場合があります。
カテゴリ
タグ

HSP3アルゴリズムとデータ構造c++RubyJavaUnity画像解析C機械学習C#LinuxcodeIQKinectMinecraftTonyuSystemraspberrypiPythonHTML5音声制御Simulinkruby俺ルール通信制御Javascriptシミュレーション

counter-shinobi
固定記事
最新記事
最新コメント
月別アーカイブ
ブロとも申請フォーム

この人とブロともになる

アクセスランキング
[ジャンルランキング]
コンピュータ
1253位
アクセスランキングを見る>>

[サブジャンルランキング]
プログラミング
219位
アクセスランキングを見る>>