Pyxel使ってシューティングゲーム作ってみた。
ゲームらしく作ると少し長くなってしまうのはしょうがないか。
リソースは使ってません。
また、プログラムはクラスを使ってません。
お手軽でゲーム作れるのはPyxelの良いところ!
import pyxel
o_dottbl0 = [0x30,0x40,0x21,0x31,0x41,0x51,0x12,0x22,0x32,0x42,0x52,0x62,0x13,0x33,0x43,0x63,0x14,0x24,0x34,0x44,0x54,0x64,0x15,0x65,0x06,0x76,0x17,0x67,0xff]
o_dottbl1 = [0x30,0x40,0x21,0x31,0x41,0x51,0x12,0x22,0x32,0x42,0x52,0x62,0x13,0x33,0x43,0x63,0x14,0x24,0x34,0x44,0x54,0x64,0x25,0x55,0x16,0x66,0x07,0x77,0xff]
o_dottbl2 = [0x70,0x71,0x62,0x72,0x82,0x53,0x63,0x73,0x83,0x93,0x54,0x64,0x74,0x84,0x94,0x35,0x55,0x65,0x75,0x85,0x95,0xb5,0x36,0x46,0x56,0x66,0x76,0x86,0x96,0xa6,0xb6,0x37,0x47,0x57,0x67,0x77,0x87,0x97,0xa7,0xb7,0xff]
o_dottbl3 = [0x01,0x02,0x03,0x04,0x05,0x06,0xff]
o_dottbl4 = [0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xff]
o_dottbl5 = [0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x08,0x18,0x28,0x19,0xff]
o_dottbl6 = [0x11,0x12,0x13,0x14,0x05,0x15,0x25,0x16,0x17,0x18,0x19,0xff]
o_dottbl7 = [0x11,0x02,0x12,0x22,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xff]
o_dottbl8 = [0x21,0x51,0x91,0xc1,0x32,0x62,0x82,0xb2,0x43,0xa3,0x14,0x24,0xc4,0xd4,0x45,0xa5,0x36,0x66,0x86,0xb6,0x27,0x57,0x97,0xc7,0xff]
#-------------------------------------------------------------------------------
# 定数
SCREEN_WIDTH = 128
SCREEN_HEIGHT = 128
CWORK_SIZE = 6
E_MAX = 5 #インベーダー総数
ET_MAX = 10 #インベーダーの弾総数
WORK_TOP = 0
WORK_END = 0x03 + ( ( E_MAX + ET_MAX + 2 ) * CWORK_SIZE ) #0x02:変数領域最大サイズ
_ass = WORK_TOP
GWK = [WORK_TOP for _ass in range( WORK_END )] #変数管理(RAM領域)
#キャラクタワーク(CWORK)内訳
id = 0 #ID
cond = 1 #状態フラグ
xpos = 2 #X座標
ypos = 3 #Y座標
mcnt = 4 #移動カウンタ
wait = 5 #待ちカウンタ
#状態フラグ内容
F_LIVE = 0x01 #bit0 0/1=存在しない/存在する
F_HIT = 0x02 #bit1 0/1=当たってない/当たった
F_BOMB = 0x04 #bit2 0/1=爆発してない/爆発中
F_MOVE = 0x08 #bit3 0/1=右移動/左移動
#-------------------------------------------------------------------------------
#変数
game_status = WORK_TOP + 0x00 #ゲームステータス
GS_TITLE = 0 #タイトル
GS_INIT = 1 #初期化
GS_GAME = 2 #ゲーム
GS_GAMEOVER = 3 #ゲームオーバー
game_score = WORK_TOP + 0x01 #スコア保存用
game_wait = WORK_TOP + 0x02 #ウエイトカウンタ
#変数領域最大サイズ:0x03
#-------------------------------------------------------------------------------
#ワーク
P_WORK = WORK_TOP + 0x03 #プレイヤー
PT_WORK = P_WORK + CWORK_SIZE #プレイヤーの弾、画面内一発
E_WORK = PT_WORK + CWORK_SIZE #インベーダー、最大数:E_MAX
ET_WORK = E_WORK + ( E_MAX * CWORK_SIZE ) #インベーダーの弾、最大数:ET_MAX
WORK_MAX = ET_WORK + ( ET_MAX * CWORK_SIZE )
#-------------------------------------------------------------------------------
#関数群
#-------------------------------------------------------------------------------
#ドット描画(セロハンモード用)
def dot_put( xp, yp ):
col = 0
offset = int( SCREEN_HEIGHT / 7 )
if( yp < ( offset * 1 ) ):
col = 7 #WHITE
elif( ( ( offset * 1 ) <= yp ) and ( yp < ( offset * 2 ) ) ):
col = 6 #BLUE
elif( ( ( offset * 2 ) <= yp ) and ( yp < ( offset * 3 ) ) ):
col = 1 #GREEN
elif( ( ( offset * 3 ) <= yp ) and ( yp < ( offset * 4 ) ) ):
col = 2 #MIZUIRO
elif( ( ( offset * 4 ) <= yp ) and ( yp < ( offset * 5 ) ) ):
col = 3 #PURPLE
elif( ( ( offset * 5 ) <= yp ) and ( yp < ( offset * 6 ) ) ):
col = 4 #YELLOW
else: #elif( ( offset * 6 ) <= yp ) ):
col = 5 #RED
pyxel.pset(xp, yp, col)
#-------------------------------------------------------------------------------
#ドット削除
def dot_erase( xp, yp ):
pyxel.pset( xp, yp, 0 )
#-------------------------------------------------------------------------------
#その位置のドットの存在をチェックする
#ret : 0/1 = no(black)/yes(not black)
def dot_check( xp, yp ):
col = pyxel.pget(xp,yp)
if col == 0:
return 0
else:
return 1
#-------------------------------------------------------------------------------
#キャラクタセット
#_type=0/1:削除/描画
def dot_pattern( _type, dx, dy, dn ):
chara_tbl = [
o_dottbl0, #タコ1
o_dottbl1, #タコ2
o_dottbl2, #砲台
o_dottbl3, #砲台の弾
o_dottbl4, #敵弾1
o_dottbl5, #敵弾2
o_dottbl6, #敵弾3
o_dottbl7, #敵弾4
o_dottbl8, #敵やられ
]
if( dn < 9 ): #キャラクタセット総数
adr = chara_tbl[dn]
_cnt = 0
while (adr[_cnt] != 0xff):
_xp = int( adr[_cnt] / 0x10 )
_yp = int( adr[_cnt] % 0x10 )
if( type == 0 ):
dot_erase(dx + _xp, dy + _yp)
else:
dot_put(dx + _xp, dy + _yp)
_cnt += 1
#-------------------------------------------------------------------------------
#指定された位置にパターン削除します
def dot_pattern_erase( dx, dy, dn ):
dot_pattern( 0, dx, dy, dn )
#-------------------------------------------------------------------------------
#指定された位置にパターンで置きます
def dot_pattern_put( dx, dy, dn ):
dot_pattern( 1, dx, dy, dn )
#===============================================================================
#インベーダー初期化
def e_init(_wk):
_minpos = int( SCREEN_HEIGHT / 7 )
GWK[_wk + cond] = 0
GWK[_wk + id] = 0
GWK[_wk + xpos] = _minpos * (-1)
GWK[_wk + ypos] = _minpos
GWK[_wk + mcnt] = pyxel.rndi(_minpos, SCREEN_WIDTH)
GWK[_wk + wait] = pyxel.rndi(1,_minpos) * E_MAX
#-------------------------------------------------------------------------------
#更新
def update():
if( GWK[game_status] == GS_TITLE ):
if getInputA():
#ゲーム開始
GWK[game_status] = GS_INIT
elif getInputB():
#終了
pyxel.quit()
elif( GWK[game_status] == GS_INIT ):
#初期設定
GWK[game_score] = 0
#プレイヤー
GWK[P_WORK + cond] = F_LIVE
GWK[P_WORK + id] = 2
GWK[P_WORK + xpos] = SCREEN_WIDTH/2
GWK[P_WORK + ypos] = SCREEN_HEIGHT * 7/8
#インベーダー
for _cnt in range(E_MAX):
_wk = E_WORK + ( CWORK_SIZE * _cnt )
e_init(_wk)
GWK[game_status] = GS_GAME
elif( GWK[game_status] == GS_GAME ):
#プレイヤーの動き
if( ( GWK[P_WORK + cond] & ( F_LIVE + F_BOMB ) ) == ( F_LIVE + F_BOMB ) ):
#爆発中
GWK[P_WORK + wait] -= 1
if( GWK[P_WORK + wait] < 0 ):
GWK[P_WORK + cond] = 0
#ゲームオーバーセット
GWK[game_status] = GS_GAMEOVER
GWK[game_wait] = 300
elif( ( GWK[P_WORK + cond] & ( F_LIVE + F_HIT ) ) == ( F_LIVE + F_HIT ) ):
#当たったら爆発
GWK[P_WORK + cond] |= F_BOMB
GWK[P_WORK + id] = 8
GWK[P_WORK + wait] = 4
elif( GWK[P_WORK + cond] & F_LIVE ):
if getInputA():
#弾発射:プレイヤーの弾発生
if( ( GWK[PT_WORK + cond] & F_LIVE ) == 0 ):
GWK[PT_WORK + cond] = F_LIVE
GWK[PT_WORK + id] = 3
GWK[PT_WORK + xpos] = GWK[P_WORK + xpos] + 7
GWK[PT_WORK + ypos] = GWK[P_WORK + ypos]
if getInputRIGHT():
#右移動
GWK[P_WORK + xpos] += 1
if( GWK[P_WORK + xpos] > ( SCREEN_WIDTH - 0x10 ) ):
GWK[P_WORK + xpos] = ( SCREEN_WIDTH - 0x10 )
elif getInputLEFT():
#左移動
GWK[P_WORK + xpos] -= 1
if( GWK[P_WORK + xpos] < 0 ):
GWK[P_WORK + xpos] = 0
#プレイヤーの弾の動き
if( GWK[PT_WORK + cond] & F_LIVE ):
GWK[PT_WORK + ypos] -= 4
if( GWK[PT_WORK + ypos] < (-8) ):
GWK[PT_WORK + cond] = 0
#インベーダーの動き
for _cnt in range(E_MAX):
_wk = E_WORK + ( CWORK_SIZE * _cnt )
if( ( GWK[_wk + cond] & F_LIVE ) == 0 ):
#登場待ち
GWK[_wk + wait] -= 1
if( GWK[_wk + wait] < 0 ):
GWK[_wk + cond] = F_LIVE
GWK[_wk + wait] = pyxel.rndi( 50, 300 ) #弾発射間隔をセット
elif( ( GWK[_wk + cond] & ( F_LIVE + F_BOMB ) ) == ( F_LIVE + F_BOMB ) ):
#爆発中
GWK[_wk + wait] -= 1
if( GWK[_wk + wait] < 0 ):
#爆発終わったので得点加算して初期化
e_init(_wk)
elif( ( GWK[_wk + cond] & ( F_LIVE + F_HIT ) ) == ( F_LIVE + F_HIT ) ):
#得点加算
GWK[game_score] += 10
#撃たれたら爆発
GWK[_wk + cond] |= F_BOMB
GWK[_wk + id] = 8
GWK[_wk + wait] = 4
GWK[_wk + xpos] -= 4 #爆発サイズの中心を合わせる
elif( GWK[_wk + cond] & F_LIVE ):
_change = 0
if( GWK[_wk + cond] & F_MOVE ):
GWK[_wk + xpos] -= 1
if( GWK[_wk + xpos] < 10 ):
GWK[_wk + xpos] = 10
GWK[_wk + cond] &= ~F_MOVE
_change = 1
else:
GWK[_wk + xpos] += 1
if( GWK[_wk + xpos] > SCREEN_WIDTH-10-8 ):
GWK[_wk + xpos] = SCREEN_WIDTH-10-8
GWK[_wk + cond] |= F_MOVE
_change = 1
GWK[_wk + mcnt] -= 1
if( GWK[_wk + mcnt] < 0 ):
GWK[_wk + mcnt] = pyxel.rndi(20, SCREEN_WIDTH)
if( GWK[_wk + cond] & F_MOVE ):
GWK[_wk + cond] &= ~F_MOVE
else:
GWK[_wk + cond] |= F_MOVE
_change = 1
if( _change ):
GWK[_wk + ypos] += 8
if( GWK[_wk + ypos] > SCREEN_HEIGHT ):
#Y方向フレームアウト
e_init(_wk)
#ヒットチェック
if( GWK[_wk + cond] & F_LIVE ):
#プレイヤーとインベーダーのヒットチェック
#プレイヤーの中心座標
_px = GWK[P_WORK + xpos] + 7
_py = GWK[P_WORK + ypos] + 4
#インベーダーの中心座標
_ex = GWK[_wk + xpos] + 4
_ey = GWK[_wk + ypos] + 4
#プレイヤーとインベーダーの距離を出して当たる距離内ならヒットセット
_k = pyxel.sqrt( ( _ex - _px ) * ( _ex - _px ) + ( _ey - _py ) * ( _ey - _py ) )
if( _k < 8 ):
#インベーダーヒット
GWK[_wk + cond] |= F_HIT
#プレイヤーヒット
GWK[P_WORK + cond] |= F_HIT
#プレイヤーの弾とインベーダーのヒットチェック
#プレイヤーの弾の上先端座標
_px = GWK[PT_WORK + xpos]
_py = GWK[PT_WORK + ypos]
#プレイヤーの弾とインベーダーの距離を出して当たる距離内ならヒットセット
_k = pyxel.sqrt( ( _ex - _px ) * ( _ex - _px ) + ( _ey - _py ) * ( _ey - _py ) )
if( _k < 4 ):
#インベーダーヒット
GWK[_wk + cond] |= F_HIT
#プレイヤーの弾はヒットした瞬間消す
GWK[PT_WORK + cond] = 0
#インベーダーが生きているなら弾を出す
if( GWK[_wk + cond] & F_LIVE ):
#弾発射間隔を減算
GWK[_wk + wait] -= 1
if( GWK[_wk + wait] < 0 ):
GWK[_wk + wait] = pyxel.rndi( 50, 300 ) #次回の弾発射間隔をセット
#弾発射:インベーダーの弾発生
for _cnt_et in range(ET_MAX):
_wk_et = ET_WORK + ( CWORK_SIZE * _cnt_et )
if( ( GWK[_wk_et + cond] & F_LIVE ) == 0 ):
GWK[_wk_et + cond] = F_LIVE
GWK[_wk_et + id] = 4
GWK[_wk_et + xpos] = GWK[_wk + xpos] + 2
GWK[_wk_et + ypos] = GWK[_wk + ypos] + 3
break
#インベーダーの弾の動き
for _cnt in range(ET_MAX):
_wk_et = ET_WORK + ( CWORK_SIZE * _cnt )
if( GWK[_wk_et + cond] & F_LIVE ):
GWK[_wk_et + ypos] += 1
if( GWK[_wk_et + ypos] > SCREEN_HEIGHT ):
GWK[_wk_et + cond] = 0
else:
#インベーダーの弾とプレイヤーのヒットチェック
if( GWK[P_WORK + cond] & F_LIVE ):
#プレイヤーの中心座標
_px = GWK[P_WORK + xpos] + 7
_py = GWK[P_WORK + ypos] + 4
#インベーダーの弾の下先端座標
_ex = GWK[_wk_et + xpos] + 1
_ey = GWK[_wk_et + ypos] + 9
_k = pyxel.sqrt( ( _ex - _px ) * ( _ex - _px ) + ( _ey - _py ) * ( _ey - _py ) )
if( _k < 4 ):
#インベーダーの弾ヒットしたら消滅
GWK[_wk_et + cond] = 0
#プレイヤーヒット
GWK[P_WORK + cond] |= F_HIT
elif( GWK[game_status] == GS_GAMEOVER ):
#ゲームオーバー
GWK[game_wait] -= 1
if( GWK[game_wait] < 0 ):
#ワーククリア
for _cnt in range( P_WORK, WORK_END - P_WORK ):
GWK[_cnt] = 0
GWK[game_status] = GS_TITLE
#-------------------------------------------------------------------------------
#描画
def draw():
pyxel.cls(0)
#プレイヤーの弾を表示
if( GWK[PT_WORK + cond] & F_LIVE ):
dot_pattern_put( GWK[PT_WORK + xpos], GWK[PT_WORK + ypos], GWK[PT_WORK + id] )
#プレイヤーを表示
if( GWK[P_WORK + cond] & F_LIVE ):
dot_pattern_put( GWK[P_WORK + xpos], GWK[P_WORK + ypos], GWK[P_WORK + id] )
#インベーダーを表示
for _cnt in range(E_MAX):
_wk = E_WORK + ( CWORK_SIZE * _cnt )
if( ( GWK[_wk + cond] & ( F_LIVE + F_BOMB ) ) == ( F_LIVE + F_BOMB ) ):
dot_pattern_put( GWK[_wk + xpos], GWK[_wk + ypos], GWK[_wk + id] )
elif( GWK[_wk + cond] & F_LIVE ):
_an = ( pyxel.frame_count >> 3 ) & 1
dot_pattern_put( GWK[_wk + xpos], GWK[_wk + ypos], GWK[_wk + id] + _an )
#インベーダーの弾を表示
for _cnt_et in range(ET_MAX):
_wk_et = ET_WORK + ( CWORK_SIZE * _cnt_et )
if( GWK[_wk_et + cond] & F_LIVE ):
_an = ( pyxel.frame_count >> 3 ) & 0x03
dot_pattern_put( GWK[_wk_et + xpos], GWK[_wk_et + ypos], GWK[_wk_et + id] + _an )
#スコア表示
pyxel.text( SCREEN_WIDTH/2 - (4*10/2), 0, 'SCORE ' + str(GWK[game_score]), 7 )
#テキスト表示
if( GWK[game_status] == GS_TITLE ):
pyxel.text( SCREEN_WIDTH/2 - (4*5/2), SCREEN_HEIGHT/4, 'TITLE', 7 )
pyxel.text( SCREEN_WIDTH/2 - (4*26/2), SCREEN_HEIGHT/4 * 3, 'Z-KEY OR A-BUTTON IS START', 7 )
pyxel.text( SCREEN_WIDTH/2 - (4*26/2), SCREEN_HEIGHT/4 * 3 + 6, 'X-KEY OR B-BUTTON IS EXIT ', 7 )
elif( GWK[game_status] == GS_GAMEOVER ):
pyxel.text( SCREEN_WIDTH/2 - (4*8/2), SCREEN_HEIGHT/2, 'GAMEOVER', 7 )
#-----------------------------------------------------------------
#入力(キーボード&ジョイパッド)
#上
def getInputUP():
if pyxel.btn(pyxel.KEY_UP) or pyxel.btn(pyxel.GAMEPAD1_BUTTON_DPAD_UP):
return 1
else:
return 0
#下
def getInputDOWN():
if pyxel.btn(pyxel.KEY_DOWN) or pyxel.btn(pyxel.GAMEPAD1_BUTTON_DPAD_DOWN):
return 1
else:
return 0
#左
def getInputLEFT():
if pyxel.btn(pyxel.KEY_LEFT) or pyxel.btn(pyxel.GAMEPAD1_BUTTON_DPAD_LEFT):
return 1
else:
return 0
#右
def getInputRIGHT():
if pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.GAMEPAD1_BUTTON_DPAD_RIGHT):
return 1
else:
return 0
#button-A(決定)
def getInputA():
if pyxel.btnp(pyxel.KEY_Z) or pyxel.btnp(pyxel.GAMEPAD1_BUTTON_A):
return 1
else:
return 0
#button-B(キャンセル)
def getInputB():
if pyxel.btnp(pyxel.KEY_X) or pyxel.btnp(pyxel.GAMEPAD1_BUTTON_B):
return 1
else:
return 0
#-----------------------------------------------------------------
pyxel.init( SCREEN_WIDTH, SCREEN_HEIGHT, fps=60 )
#パレット変更
pyxel.colors[0] = 0x000000 #BLACK
pyxel.colors[1] = 0x00FF00 #GREEN
pyxel.colors[2] = 0x00FFFF #MIZUIRO
pyxel.colors[3] = 0xFF00FF #PURPLE
pyxel.colors[4] = 0xFFFF00 #YELLOW
pyxel.colors[5] = 0xFF0000 #RED
pyxel.colors[6] = 0x0000FF #BLUE
pyxel.colors[7] = 0xFFFFFF #WHITE
#ワーククリア
for _cnt in range( WORK_END - WORK_TOP ):
GWK[_cnt] = 0
#実行
pyxel.run( update, draw )