Pygameで弾幕(超入門編)
タスクシステムをPythonで実験しようとしていたら、
知らない間に弾幕を作っていました…。
そこで気がついたことをメモ。
util.py
class Vec2D: def __init__(self, x, y): self.x = x self.y = y def __add__(self, pos): return Vec2D(self.x + pos.x, self.y + pos.y) def __iadd__(self, pos): return Vec2D(self.x + pos.x, self.y + pos.y) def __sub__(self, pos): return Vec2D(self.x - pos.x, self.y - pos.y) def __mul__(self, vec): return self.x*vec.x + self.y*vec.y def __eq__(self, vec): return (self.x==vec.x and self.y==vec.y) def nomalize(self): d = math.sqrt(self.x ** 2 + self.y ** 2) * 1.0 if(d == 0): d = 1 return Vec2D(self.x / d, self.y / d) def cross(self, scalar): return Vec2D(self.x*scalar, self.y*scalar) def distance(self, vec): return math.sqrt(pow(self.x - vec.x, 2) + pow(self.y - vec.y, 2)) def distance2(self, vec): return pow(self.x - vec.x, 2) + pow(self.y - vec.y, 2) def getOrthogonalVector(self): return Vec2D(self.y, -self.x) def reverse(self): return Vec2D(self.x*-1, self.y*-1) def getTuble(self): return (self.x, self.y) def debug(self): print "(x,y)=(%f,%f)" % (self.x, self.y)
main.py
import pygame import math import random from util import * # 文字列の描画 def drawMessage(pos, message): font = pygame.font.SysFont("", 25) ren = font.render(message, 0, (255, 255, 255, 255)) pygame.display.get_surface().blit(font.render(message, 0, (255, 255, 255, 255)), pos.getTuble()) # 線分描画 def drawLine(pos1, pos2, color=(0, 0, 255, 255)): pygame.draw.line(pygame.display.get_surface(), color, pos1.getTuble(), pos2.getTuble()) # 円描画 def drawCircle(pos, radius, color=(0, 0, 255, 255)): pygame.draw.circle(pygame.display.get_surface(), color, pos.getTuble(), radius) # 矩形描画 # rect = (pos.x, pos.y, size.x, size.y) def drawRect(rect, color=(0, 0, 255, 255)): pygame.draw.rect(pygame.display.get_surface(), color, rect) # タスク基底クラス class Task: def __init__(self, pos=Vec2D(0,0), mov=Vec2D(0,0)): self.pos = pos self.mov = mov self.alive = True def update(self): self.pos += self.mov if self.isOut(): self.alive = False def isOut(self): return (self.pos.x < 0 or 640 < self.pos.x or self.pos.y < 0 or 640 < self.pos.y) def debug(self): print "p(x,y)=(%d,%d) m(x,y)=(%d,%d) a=%d" % (self.pos.x, self.pos.y, self.mov.x, self.mov.y, self.alive) # 敵クラス class Enemy(Task): def __init__(self, pos=Vec2D(0,0), mov=Vec2D(0,0), bullet=None): self.pos = pos self.mov = mov self.alive = True self.bullet = bullet self.rot = 0 # 度 def update(self): self.pos += self.mov if self.isOut(): self.alive = False self.bullet(self) g_tamaList = [] g_enemyList = [] def bulletRoll(enemy): mov = Vec2D( 5*math.cos(math.radians(enemy.rot)), 5*math.sin(math.radians(enemy.rot))) tama = Task(pos=Vec2D(enemy.pos.x+8,enemy.pos.y+8), mov=mov) g_tamaList.append(tama) enemy.rot += 12.34 if(enemy.rot > 360): enemy.rot -= 360 def bulletCircle(enemy): for i in range(5): mov = Vec2D( 5*math.cos(math.radians(enemy.rot)), 5*math.sin(math.radians(enemy.rot))) tama = Task(pos=Vec2D(enemy.pos.x+8,enemy.pos.y+8), mov=mov) g_tamaList.append(tama) enemy.rot += 12.34 if(enemy.rot > 360): enemy.rot -= 360 def bulletWave(enemy): mov = Vec2D( 10*math.cos(math.radians(enemy.rot)), 5) tama = Task(pos=Vec2D(enemy.pos.x+8,enemy.pos.y+8), mov=mov) g_tamaList.append(tama) enemy.rot += 12.34 if(enemy.rot > 360): enemy.rot -= 360 def bulletWide(enemy): if(enemy.rot % 33 == 0): for j in range(5): for i in range(5): mov = Vec2D( (i**2*0.3 + 3)*math.cos(math.radians(enemy.rot)), i**2*0.3 + 3) tama = Task(pos=Vec2D(enemy.pos.x+8,enemy.pos.y+8), mov=mov) g_tamaList.append(tama) enemy.rot += 20 if(enemy.rot > 360): enemy.rot -= 360 enemy.rot += 1 if(enemy.rot > 360): enemy.rot = 0 SCREEN = Vec2D(480, 640) def main(): pygame.init() pygame.display.set_mode(SCREEN.getTuble(), 0, 32) screen = pygame.display.get_surface() global g_tamaList global g_enemyList flgInit = True while True: for e in pygame.event.get(): if(e.type == pygame.QUIT): return pygame.display.update() pygame.time.wait(10) screen.fill((0, 0, 0)) mouseL, mouseR, mouseM = pygame.mouse.get_pressed() # ここから # 初期化 if(flgInit): g_enemyList.append(Enemy(pos=Vec2D(240, 240), bullet=bulletCircle)) flgInit = False # 弾の更新・描画 for enemy in g_enemyList: enemy.update() drawRect((enemy.pos.x, enemy.pos.y, 16, 16), (0, 255, 0, 255)) if(not enemy.alive): g_enemyList.remove(enemy) # 弾の更新・描画 for tama in g_tamaList: tama.update() drawCircle(tama.pos, 3, (255, 0, 0, 255)) if(not tama.alive): g_tamaList.remove(tama) # Debug drawMessage(Vec2D(0, 0), "Fire:%d" % (len(g_tamaList))) if(__name__ == "__main__"): main()
まず、基本は回転ですね。
def bulletRoll(enemy): mov = Vec2D( 5*math.cos(math.radians(enemy.rot)), 5*math.sin(math.radians(enemy.rot)))
5の半径で、三角関数を使った回転運動をしています。
enemy.rot += 12.34
で、角度を適当に増やしていけば、回転撃ちの完成です。
ただ、これだと、1方向に撃つのを回転しているだけなので、
ちょっと味気ないです。
そこで、1フレーム内に複数の方法に撃つ方法を採用します。
def bulletCircle(enemy): for i in range(5): mov = Vec2D( 5*math.cos(math.radians(enemy.rot)), 5*math.sin(math.radians(enemy.rot))) tama = Task(pos=Vec2D(enemy.pos.x+8,enemy.pos.y+8), mov=mov) g_tamaList.append(tama) enemy.rot += 12.34 if(enemy.rot > 360): enemy.rot -= 360
…まあ、、、1フレーム内に5回ループを回しているだけですが…。