0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Pyxel】短いプログラムで遊ぶ②

Last updated at Posted at 2024-11-28

Pyxel使ってシューティングゲーム作ってみた。

ゲームらしく作ると少し長くなってしまうのはしょうがないか。

リソースは使ってません。
また、プログラムはクラスを使ってません。
お手軽でゲーム作れるのはPyxelの良いところ!
invdemo.png

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 )

動作確認はこちら

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?