日付:2021/07/21
Pythonのpygameで簡単なゲームを作成していきます。
チートシートそのままコピペ
まずはチートシートを開き、最初のサンプルコードをそのまま持ってきます。
https://qiita.com/bkh4149/items/25169ffbd375dcc19740
import pygame
from pygame.locals import *
import sys
def main():
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 800*600の画面
px=120
py=100
while True:
screen.fill((255,255,255)) # 背景を白
pygame.draw.circle(screen,(10,10,10),(px,py),50) # ●
pygame.draw.rect(screen, (255,0,0), Rect(10,100,50,50), 1) # ■
pygame.draw.line(screen, (0,255,0), (0,200), (100,300), 2) # 線
pygame.display.update() # 画面更新
# イベント処理
for event in pygame.event.get(): # イベントを取得
if event.type == QUIT: # 閉じるボタンが押されたら
pygame.quit() # 全てのpygameモジュールの初期化を解除
sys.exit() # 終了(ないとエラーで終了することになる)
if __name__ == "__main__":
main()
チートシートのテンプレートはざっくりいうと
①初期化
②ループ
をやっています。
初期化ではPygameの初期化や各種import、変数の初期化をおこない、ループは描画一回分をwhile文でぐるぐるまわしています。本来なら30fpsとか60fpsになるように調整するのですが、いまは適当です。
ループの中では
①画面を消す
②いろんなキャラを描く
③イベント入力
ということをやっています。
まずは、矢印キーの←と→で赤い枠が左右に動くようにする
イベント処理の部分を書き換え、変数名pvxはpxにします。
for event in pygame.event.get():
if event. type == QUIT:
pygame.quit()
sys.exit()
+ elif event.type == KEYDOWN:
+ if event.key==K_LEFT:
+ px -= 5 # 横方向、変数名pvxはpxにします。
+ elif event.key==K_RIGHT:
+ px += 5 # 横方向、変数名pvxはpxにします。
変数名の変更
登場人物は以下の通り
UFO 上の方に出てくる緑のやつ
プレイヤー 赤いやつ(UFOに向けてタマを打てる)
タマ プレイヤーの売ったタマ
座標を表す変数名は以下のように決めました。
UFO ux,uy
プレイヤー px,py
タマ tx,ty
インデントに気をつけて注意深く書き換えます。
import pygame
from pygame.locals import *
import sys
def main():
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 800*600の画面
#プレイヤ
px=120
py=500
#タマ
tx=px
ty=py
while True:
screen.fill((255,255,255)) # 背景を白
pygame.draw.circle(screen,(10,10,10),(tx,ty),5) # ●
pygame.draw.rect(screen, (255,0,0), Rect(px,py,50,50), 1) # ■
#pygame.draw.line(screen, (0,255,0), (0,200), (100,300), 2) # 線
pygame.display.update() # 画面更新
# イベント処理
for event in pygame.event.get():
if event. type == QUIT:
pygame.quit()
sys. exit()
elif event.type == KEYDOWN:
if event.key==K_LEFT:
px -=5 # 横方向
tx=px
elif event.key==K_RIGHT:
px +=5 # 横方向
tx=px
if __name__ == "__main__":
main()
スペースキーでタマが上に飛んでいくようにする
タマの位置に速度を足しこむようにします。また、スペースキーが押されたら球の速度を-1にします。
import pygame
from pygame.locals import *
import sys
def main():
#pygameの初期設定
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 800*600の画面
#プレイヤの初期設定
px = 120
py = 500
#タマの初期設定
tx = px
ty = py
tvy = 0
while True:
#画面を消去
screen.fill((255,255,255)) # 背景を白
#タマの処理と描画
ty = ty + tvy
pygame.draw.circle(screen,(10,10,10),(tx,ty),5) # ●
#プレイヤの処理と描画
pygame.draw.rect(screen, (255,0,0), Rect(px,py,50,50), 1) # ■
# イベント処理
for event in pygame.event.get():
if event. type == QUIT:
pygame.quit()
sys. exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
px -= 5 # 横方向
tx = px
elif event.key == K_RIGHT:
px += 5 # 横方向
tx = px
elif event.key == K_SPACE:
ty = py
tvy = -1
pygame.display.update() # 画面更新
if __name__ == "__main__":
main()
発射位置の修正
プレイヤーの左端から飛んでいくのでまんなから発射されるように変更します。
import pygame
from pygame.locals import *
import sys
def main():
#pygameの初期設定
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 800*600の画面
#プレイヤの初期設定
px = 120
py = 500
#タマの初期設定
tx = px+25
ty = py
tvy = 0
while True:
#画面を消去
screen.fill((255,255,255)) # 背景を白
#タマの処理と描画
ty = ty + tvy
pygame.draw.circle(screen,(10,10,10),(tx,ty),5) # ●
#プレイヤの処理と描画
pygame.draw.rect(screen, (255,0,0), Rect(px,py,50,50), 1) # ■
# イベント処理
for event in pygame.event.get():
if event. type == QUIT:
pygame.quit()
sys. exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
px -= 5 # 横方向
tx = px+25
elif event.key == K_RIGHT:
px += 5 # 横方向
tx = px+25
elif event.key == K_SPACE:
ty = py
tx = px+25
tvy = -1
pygame.display.update() # 画面更新
if __name__ == "__main__":
main()
UFOの描画
画面上部にUFOを描画します。とりあえず位置は固定で動きません。
import pygame
from pygame.locals import *
import sys
def main():
#pygameの初期設定
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 800*600の画面
#UFOの初期設定
ux = 200
uy = 100
#プレイヤの初期設定
px = 120
py = 500
#タマの初期設定
tx = px+25
ty = py
tvy = 0
while True:
#画面を消去
screen.fill((255,255,255)) # 背景を白
#UFOの処理と描画
pygame.draw.rect(screen, (0,255,0), Rect(ux,uy,150,50), 1) # ■
#タマの処理と描画
ty = ty + tvy
pygame.draw.circle(screen,(10,10,10),(tx,ty),5) # ●
#プレイヤの処理と描画
pygame.draw.rect(screen, (255,0,0), Rect(px,py,50,50), 1) # ■
# イベント処理
for event in pygame.event.get():
if event. type == QUIT:
pygame.quit()
sys. exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
px -= 5 # 横方向
tx = px+25
elif event.key == K_RIGHT:
px += 5 # 横方向
tx = px+25
elif event.key == K_SPACE:
ty = py
tx = px+25
tvy = -1
pygame.display.update() # 画面更新
if __name__ == "__main__":
main()
UFOを左右に動かす
とまっていたUFOを左右に動かします。
import pygame
from pygame.locals import *
import sys
def main():
#pygameの初期設定
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 800*600の画面
#UFOの初期設定
ux = 200
uy = 100
uvx = 1
#プレイヤの初期設定
px = 120
py = 500
#タマの初期設定
tx = px+25
ty = py
tvy = 0
while True:
#画面を消去
screen.fill((255,255,255)) # 背景を白
#UFOの処理と描画
ux = ux + uvx
if ux>700 or ux <0:
uvx *= -1
pygame.draw.rect(screen, (0,255,0), Rect(ux,uy,150,50), 1) # ■
#タマの処理と描画
ty = ty + tvy
pygame.draw.circle(screen,(10,10,10),(tx,ty),5) # ●
#プレイヤの処理と描画
pygame.draw.rect(screen, (255,0,0), Rect(px,py,50,50), 1) # ■
# イベント処理
for event in pygame.event.get():
if event. type == QUIT:
pygame.quit()
sys. exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
px -= 5 # 横方向
tx = px+25
elif event.key == K_RIGHT:
px += 5 # 横方向
tx = px+25
elif event.key == K_SPACE:
ty = py
tx = px+25
tvy = -1
pygame.display.update() # 画面更新
if __name__ == "__main__":
main()
当たり判定
タマがUFOにあたったら、UFOを消します。
import pygame
from pygame.locals import *
import sys
def main():
#pygameの初期設定
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 800*600の画面
#UFOの初期設定
ux = 200
uy = 100
uw = 150
uh = 50
uvx = 1
uhp = 100
#プレイヤの初期設定
px = 120
py = 500
#タマの初期設定
tx = px+25
ty = py
tvy = 0
while True:
#画面を消去
screen.fill((255,255,255)) # 背景を白
#UFOの処理と描画
ux = ux + uvx
if ux>700 or ux <0:
uvx *= -1
#当たり判定
if (ux <= tx <= ux + uw) and (uy <= ty <= uy + uh):
uhp=0
if uhp > 0:
pygame.draw.rect(screen, (0,255,0), Rect(ux,uy,uw,uh), 1) # ■
#タマの処理と描画
ty = ty + tvy
pygame.draw.circle(screen,(10,10,10),(tx,ty),5) # ●
#プレイヤの処理と描画
pygame.draw.rect(screen, (255,0,0), Rect(px,py,50,50), 1) # ■
# イベント処理
for event in pygame.event.get():
if event. type == QUIT:
pygame.quit()
sys. exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
px -= 5 # 横方向
tx = px+25
elif event.key == K_RIGHT:
px += 5 # 横方向
tx = px+25
elif event.key == K_SPACE:
ty = py
tx = px+25
tvy = -1
pygame.display.update() # 画面更新
if __name__ == "__main__":
main()
とりあえず最初のステップはこれで完成です!
クラスを使って書き換えてみる
動きは同じですが、クラスを使うとプログラムがスッキリします。
import pygame
from pygame.locals import *
import sys
class Player():
def __init__(self):
self.px = 120
self.py = 500
def update(self,screen,T):
pygame.draw.rect(screen, (255,0,0), Rect(self.px,self.py,50,50), 1) # ■
# イベント処理
for event in pygame.event.get():
if event. type == QUIT:
pygame.quit()
sys. exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
self.px -= 5 # 横方向
T.x = self.px+25
elif event.key == K_RIGHT:
self.px += 5 # 横方向
T.x = self.px+25
elif event.key == K_SPACE:
T.y = self.py
T.x = self.px+25
T.vy = -1
class UFO():
def __init__(self):
self.ux = 200
self.uy = 100
self.uw = 150
self.uh = 50
self.uvx = 1
self.uhp = 100
def update(self,screen,T):
self.ux = self.ux + self.uvx
if self.ux>700 or self.ux <0:
self.uvx *= -1
#当たり判定
if (self.ux <= T.x <= self.ux + self.uw) and (self.uy <= T.y <= self.uy + self.uh):
self.uhp=0
if self.uhp > 0:
pygame.draw.rect(screen, (0,255,0), Rect(self.ux,self.uy,self.uw,self.uh), 1) # ■
class Tama():
def __init__(self):
#プレイヤの初期設定
self.x = 120
self.y = 500
self.vy = 0
def update(self,screen):
#タマの処理と描画
self.y = self.y + self.vy
pygame.draw.circle(screen,(10,10,10),(self.x,self.y),5) # ●
def main():
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 800*600の画面
T1=Tama()
P1=Player()
U1=UFO()
while True:
screen.fill((255,255,255)) # 背景を白
T1.update(screen)
P1.update(screen,T1)
U1.update(screen,T1)
pygame.display.update() # 画面更新
if __name__ == "__main__":
main()
なんとrepl.itで動作確認できました!
https://replit.com/@bkh4149/HappyPerfumedListeners#main.py
ただし、初期状態で画面が小さいです。
下が見えない人は下に引き伸ばして画面を大きくしてみてください。
また、キーボードは動作している画面上でクリックしないと有効になりません。
宇宙船を増やし、動きを換えてみる
せっかくクラスを使ったので、UFOを増やし動きを変えてみました
import pygame
from pygame.locals import *
import sys
class Player():
def __init__(self):
self.px = 120
self.py = 500
def update(self,screen,T):
pygame.draw.rect(screen, (255,0,0), Rect(self.px,self.py,50,50), 1) # ■
# イベント処理
for event in pygame.event.get():
if event. type == QUIT:
pygame.quit()
sys. exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
self.px -= 5 # 横方向
T.x = self.px+25
elif event.key == K_RIGHT:
self.px += 5 # 横方向
T.x = self.px+25
elif event.key == K_SPACE:
T.y = self.py
T.x = self.px+25
T.vy = -1
class UFO():
def __init__(self,x):
self.ux = x
self.uy = x
self.uw = 20
self.uh = 20
self.uvx = 1
self.uvy = 1
self.uhp = 100
def update(self,screen,T):
self.ux = self.ux + self.uvx
self.uy = self.uy + self.uvy
if self.ux>700 or self.ux <0:
self.uvx *= -1
if self.uy>600 or self.uy <0:
self.uvy *= -1
#当たり判定
if (self.ux <= T.x <= self.ux + self.uw) and (self.uy <= T.y <= self.uy + self.uh):
self.uhp=0
if self.uhp > 0:
pygame.draw.rect(screen, (0,255,0), Rect(self.ux,self.uy,self.uw,self.uh), ) # ■
class Tama():
def __init__(self):
#プレイヤの初期設定
self.x = 120
self.y = 500
self.vy = 0
def update(self,screen):
#タマの処理と描画
self.y = self.y + self.vy
pygame.draw.circle(screen,(10,10,10),(self.x,self.y),5) # ●
def main():
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 800*600の画面
T1=Tama()
P1=Player()
Us=[]
for i in range(10):
Us.append(UFO(i*50))
while True:
screen.fill((255,255,255)) # 背景を白
T1.update(screen)
P1.update(screen,T1)
for U in Us:
U.update(screen,T1)
pygame.display.update() # 画面更新
if __name__ == "__main__":
main()
その後の改善 2023-07-19
上記プログラムにいろいろ改善点があり、修正しました
症状:PCを変えると以上に早くなったり遅くなったりする
対策:フレームレート調整を入れる
結果:ok
症状:弾を撃って次弾を打つと、前の弾が消えてしまう
#対策:フラグにする
#結果:ok
症状:弾を撃って、砲台を左右に動かすと弾も左右に動いてしまう
#原因:L,Rキーのところで弾の位置を変えていた
#対策:上を削除でok
症状:弾が当たったとき、ufoは消えるが弾が消えない
#対策:hit時に弾をリセットする
#結果:ok
import pygame
from pygame.locals import *
import sys
class Player():
def __init__(self):
self.px = 120
self.py = 500
def update(self,screen,T):
pygame.draw.rect(screen, (255,0,0), Rect(self.px,self.py,50,50), 1) # ■
# イベント処理
for event in pygame.event.get():
if event. type == QUIT:
pygame.quit()
sys. exit()
elif event.type == KEYDOWN:
if event.key == K_LEFT:
self.px -= 5 # 横方向
elif event.key == K_RIGHT:
self.px += 5 # 横方向
elif event.key == K_SPACE:
if T.isFire==False:
T.isFire=True
T.y = self.py
T.x = self.px+25
class UFO():
def __init__(self,x):
self.ux = x
self.uy = x
self.uw = 20
self.uh = 20
self.uvx = 3
self.uvy = 2
self.uhp = 100
def update(self,screen,T):
self.ux = self.ux + self.uvx
self.uy = self.uy + self.uvy
if self.ux>700 or self.ux <0:
self.uvx *= -1
if self.uy>600 or self.uy <0:
self.uvy *= -1
#当たり判定
if (self.ux <= T.x <= self.ux + self.uw) and (self.uy <= T.y <= self.uy + self.uh):
self.uhp=0
T.isFire=False
if self.uhp > 0:
pygame.draw.rect(screen, (0,255,0), Rect(self.ux,self.uy,self.uw,self.uh), ) # ■
class Tama():
def __init__(self):
#プレイヤの初期設定
self.x = 120
self.y = 500
self.vy = -5
self.isFire = False
def update(self,screen):
print("isFile",self.isFire,"self.x=",self.x,"self.y=",self.y)
#タマの処理と描画
if self.isFire:
self.y += self.vy
if self.y<-10:
self.isFire=False
pygame.draw.circle(screen,(10,10,10),(self.x,self.y),5) # ●
def main():
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 800*600の画面
T1=Tama()
P1=Player()
Us=[]
ck = pygame.time.Clock()
for i in range(10):
Us.append(UFO(i*50))
while True:
ck.tick(60) #1秒間で60フレーム
screen.fill((255,255,255)) # 背景を白
T1.update(screen)
P1.update(screen,T1)
for U in Us:
U.update(screen,T1)
pygame.display.update() # 画面更新
if __name__ == "__main__":
main()
お疲れさまでした!