読者です 読者をやめる 読者になる 読者になる

【Programming】Pythonで弾幕シューティングゲームを作らなかった

最近身の回りで「Pythonをやれ」という圧力が凄い事になっております。
ロボットサークルでのシステムにPythonを使うだの、BlenderPythonでどうのこうのできるだの、ゲームもPythonで作ったら楽だの、果てはオライリーからEffective Pythonの邦訳版が出るだの。
丁度冬休みだし、いい機会なのでPythonで弾シューを作る事にしました。
とはいえ、年末だから時間もないしあんまり大したことしてませんが

Pythonには2と3があって、(恐らく)3を使うのが望ましいと思われますが、pygameってゲーム用のライブラリがPython2のみ対応って感じなので(非公式で対応してるっぽいが調べるのが面倒)Python2とpygameを使います。
どうせPythonの文法とかそんなのを覚える為のものですし、拘ることでもありません。
2と3では結構色んな事が違うらしいですが、まぁ大部分は一緒でしょう。
という訳で、サックリと下みたいなの作りました。

f:id:VirtualNoodle:20151227162641p:plain

まぁ見た目は弾幕シューティングです。
当たり判定も、自機から出る弾もありませんが・・・

Pythonをサックリ使ってみた感想

・覚えやすい
学習しやすいとか言われて学習しやすかった事ってあんまりないんですが、学習しやすいと言われてるPythonは入門サイトをサックリと一周するぐらいなら難なくできました。
ライブラリ落としてゲームらしきものも簡単に作れないこともないです。
でも所詮ビギナーなのであんまり複雑なもの作れと言われたらお手上げ。

・なんか・・・classが違う・・・
外から動的に変数を追加できるよ!privateな変数とか無いよ!とか言われるし、全体的にアクセス制限がガバガバもといフリーダムです。
カプセル化とは一体なんだったのか

・インデントで構文を作るよ
下のコード見ると分かるんですが、インデント使って色々しています。
なんでこんな事するんだ・・・?と思いましたが、コードが全体的にシンプルになったり、コードの個人差が少なくなったりするらしいです。
僕的には見やすいのと、ネストの対応のミスが無くなるのが嬉しい。

main.py

import pygame
from pygame.locals import*
import random
import sys
import mover

SCREEN_RECT = Rect(0,0,640,720)

class GameObject:
	def __init__(self):
		pygame.init()
		self.screen = pygame.display.set_mode(SCREEN_RECT.size)
		pygame.display.set_caption("pyShooting")
		
		self.sprite = pygame.sprite.Group()
		mover.Player(self.sprite)
		mover.Enemy(self.sprite)
		
		#clock
		self.clock = pygame.time.Clock()
		
		
	def update(self):
		self.sprite.update()

	def draw(self):
		self.screen.fill((0,0,0))
		self.sprite.draw(self.screen)
	
	def mainLoop(self):
		self.clock.tick(60)
		self.update()
		self.draw()
		pygame.display.update()


if __name__ == "__main__":
	game = GameObject()
	while True: 
		game.mainLoop()
		for event in pygame.event.get():
			if event.type == QUIT:
				pygame.quit()
				sys.exit()

mover.py

import pygame
from pygame.locals import*
import math
import random

class Mover(pygame.sprite.Sprite):
	def __init__(self,containers):
		pygame.sprite.Sprite.__init__(self,containers)
		self.image = None
		self.rect = None
		self.x = 0.0
		self.y = 0.0
		self.vel = 0
		self.ang = 0

	def update(self):
		dx = self.vel * math.cos(self.ang)
		dy = self.vel * math.sin(self.ang)
		self.x += dx
		self.y += dy
		#self.rect.move_ip(dy,dx)
		self.rect.centerx = self.x
		self.rect.centery = self.y
		if self.y < -50 or self.y > 900 or self.x < -50 or self.x > 700:
			pygame.sprite.Sprite.kill(self)

class Bullet (Mover):
	def __init__(self,containers,x,y,vel,ang):
		Mover.__init__(self,containers)
		self.image = pygame.Surface((10,10))
		pygame.draw.circle(self.image,(255,255,255),(5,5),5,0)
		tmp_x = self.image.get_width()
		tmp_y = self.image.get_height()
		self.x = x
		self.y = y
		self.vel = vel
		self.ang = ang
		self.rect = Rect(self.x - tmp_x / 2, self.y - tmp_y / 2,tmp_x,tmp_y)

class Enemy (Mover):
	def __init__(self,containers):
		self.counter = 0
		self.containers = containers
		Mover.__init__(self,self.containers)		
		self.image = pygame.Surface((30,30))
		pygame.draw.circle(self.image,(255,0,0,),(15,15),15,0)
		tmp_x = self.image.get_width()
		tmp_y = self.image.get_height()
		self.x = 320
		self.y = 240
		self.rect = Rect(self.x - tmp_x / 2,self.y - tmp_y / 2 ,tmp_x,tmp_y)

	def update(self):
		self.ang = math.atan2(0,1)
		self.vel = 3
		#Mover.update(self)

		num = 0
		if self.counter % 2 == 0:
			while num < 5:
				num += 1
				Bullet(self.containers,self.rect.centerx,self.rect.centery,5.0,0.1 * self.counter + 1.2 * num)
				Bullet(self.containers,self.rect.centerx,self.rect.centery,5.0,-0.1 * self.counter + 1.2 * num)

		self.counter += 1
		

class Player(Mover):
	def __init__(self,containers):
		Mover.__init__(self,containers)
		self.x = 320.0
		self.y = 620.0
		#self.image = pygame.image.load("./Data/image/w_snake.png")
		self.image = pygame.Surface((30,30))
		pygame.draw.circle(self.image,(0,0,255),(15,15), 15,0)
		tmp_x = self.image.get_width()
		tmp_y = self.image.get_height()
		self.rect = Rect(self.x - tmp_x / 2,self.y - tmp_y / 2,tmp_x,tmp_y)

	def update(self):
		keys = pygame.key.get_pressed()
		speed = 1.0
		vx,vy = 0,0
		self.vel = 0
		if keys[K_LEFT]:
			vx -= speed
			self.vel = 5
		if keys[K_RIGHT]:
			vx += speed
			self.vel = 5
		if keys[K_UP]:
			vy -= speed
			self.vel = 5
		if keys[K_DOWN]:
			vy += speed
			self.vel = 5

		self.ang = math.atan2(vy,vx)

		Mover.update(self)
		if self.rect.left < 0:
			self.x = 10;
		if self.rect.right > 640:
			self.x = 630
		if self.rect.top < 0:
			self.y = 10
		if self.rect.bottom > 720:
			self.y = 710

所々に手抜きが見られますが、まぁこの程度のコードで上みたいな結果が得られます。短いですね。(正直Cとあんまり変わらなくねえ?って感じもしますが)
Pythonのお作法とか規約とかそんな感じの事がまだ分からないし、その辺を勉強してもうちょっとスッキリした感じのモノが書けると良いんですけどね

C++とDXライブラリを使うよりは楽にかけたんですが、複雑化するとどんな感じになるかはちょっと分かりません。