pythonista3でゲーム作りにチャレンジ

Pythonista3でインベーダーゲーム風を作った

インベーダー、自機ロケット、ミサイル、敵爆弾のpng ファイルが必要
インベーダーは2枚のキャラを1秒ごとに切り替える。またミサイルがぶつかったら赤くする

ーーーソースコード---
from scene import *
import threading
import random
from math import *
#import dialogs
import console
#
class InvaderSprite(SpriteNode):
	def __init__(self, p):
		super().__init__('INVADER.png')
		self.position = p
		self.size /= 8 
		self.img_no: int = 0
		self.img = [
			Texture('INVADER.png'),
			Texture('INVADER_2.png'),
			Texture('INVADER_3.png')]
	
	def change_img(self):
		self.texture = self.img[self.img_no]
		self.size = self.img[self.img_no].size / 8
		
		if self.img_no == 0:
			self.img_no = 1
		elif self.img_no == 1:
			self.img_no = 0
		elif self.img_no == 2:
			pass
		else:
			pass
	
class RocketSprite(SpriteNode):
	def __init__(self, p):
		super().__init__('ROCKET.png')  #('emj:Rocket')
		self.position = p
		self.size /= 9
		
class MonsterSprite(SpriteNode):
	def __init__(self, p):
		super().__init__('emj:Alien_Monster')
		self.position = p

		
class MisileSprite(SpriteNode):
	def __init__(self, p):
		super().__init__('MISSILE.png')   #('emj:Pushpin_1')
		self.position = p
		self.size /= 30  #3

class FireSprite(SpriteNode):
	def __init__(self, p):
		super().__init__('emj:Fire')
		self.position = p
		self.size /= 2


class AlienSprite(SpriteNode):
	def __init__(self):
		super().__init__('plf:AlienPink_front')
		self.img = [['plf:AlienPink_front','plf:AlienPink_swim1','plf:AlienPink_swim2','plf:AlienPink_walk1','plf:AlienPink_walk2','plf:AlienPink_climb1', 'plf:AlienPink_climb2','plf:AlienPink_duck','plf:AlienPink_hit','plf:AlienPink_jump','plf:AlienPink_stand'],
		['plf:AlienBlue_front','plf:AlienBlue_swim1','plf:AlienBlue_swim2','plf:AlienBlue_walk1','plf:AlienBlue_walk2','plf:AlienBlue_climb1', 'plf:AlienBlue_climb2','plf:AlienBlue_duck','plf:AlienBlue_hit','plf:AlienBlue_jump','plf:AlienBlue_stand'],
		['plf:AlienGreen_front','plf:AlienGreen_swim1','plf:AlienGreen_swim2','plf:AlienGreen_walk1','plf:AlienGreen_walk2','plf:AlienGreen_climb1', 'plf:AlienGreen_climb2','plf:AlienGreen_duck','plf:AlienGreen_hit','plf:AlienGreen_jump','plf:AlienGreen_stand'],
		['plf:AlienYellow_front','plf:AlienYellow_swim1','plf:AlienYellow_swim2','plf:AlienYellow_walk1','plf:AlienYellow_walk2','plf:AlienYellow_climb1', 'plf:AlienYellow_climb2','plf:AlienYellow_duck','plf:AlienYellow_hit','plf:AlienYellow_jump','plf:AlienYellow_stand'],
		['plf:AlienBeige_front','plf:AlienBeige_swim1','plf:AlienBeige_swim2','plf:AlienBeige_walk1','plf:AlienBeige_walk2','plf:AlienBeige_climb1', 'plf:AlienBeige_climb2','plf:AlienBeige_duck','plf:AlienBeige_hit','plf:AlienBeige_jump','plf:AlienBeige_stand']
			]
		self.img_no: int = 0
#		
	def change_img(self, char_no, img_no):
		self.texture = Texture(self.img[char_no][img_no])
#


class GameScene(Scene):
	ALIEN_SP_CHAR_MAX = 5
	ALIEN_SP_NO_MAX = 10
	MONSTER_COUNT_MAX = 5
	ROCKET_COUNT_MAX = 5
	MONSTER_CREATE_COUNT_MAX = 3
	MONSTER_ERASE_COUNT_MAX = 3
	MONSTER_ERASE_POINT = 100
	ROCKET_CREATE_COUNT_MAX = 3
	ROCKET_ERASE_COUNT_MAX = 3
	ALIEN_Y_ORG = 80
	INVADER_ERASE_POINT = 20
	INVADER_X_ADD = 10
	INVADER_Y_ADD = 40
	
	def setup(self):
		#print("setup")
		self.playing = False
		self.char_no: int = 0
		self.background_color = '#e0e0e0'
		self.alien_sp = [[None,None,None,None,None,None,None,None,None,None],[None,None,None,None,None,None,None,None,None,None],
		[None,None,None,None,None,None,None,None,None,None],
		[None,None,None,None,None,None,None,None,None,None],
		[None,None,None,None,None,None,None,None,None,None],
		]
		
		self.scene_w = self.size.w
		self.scene_h = self.size.h
		#self.rocket_sp = Node()
		#self.monster_sp = Node()
		#self.fire_sp = Node()
		#self.alien_sp = Node()
		self.monster_x_add = 10
		self.monster_y_add = 40

		self.alien_data = [[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],[False,False,False,False,False,False,False,False,False,False],]
		self.alien_count: int = self.ALIEN_SP_CHAR_MAX * self.ALIEN_SP_NO_MAX
		self.monster_count: int = self.MONSTER_COUNT_MAX
		self.rocket_count: int = self.ROCKET_COUNT_MAX
		self.rocket_create_count: int = 0
		self.monster_erase_point: int = 999999
		self.rocket_erase_count: int = 0

		
		self.createLabelNode()
		self.createAlienSprite()
		
		
		p = self.size
		p.x = self.scene_w / 2
		p.y = 30 
		self.createRocketSprite(p)
		
		p = self.size
		p.x = 0
		p.y = self.scene_h - 40 #self.size.h / 2
		self.createMonsterSprite(p)
		self.createFireSprite()
		self.createMisileSprite()
		
		# invader
		self.invader_erase_point = 999999
		p = self.size
		p.x = 0. #self.scene_w /2
		p.y = self.scene_h /2 - 80
		self.createInvaderSprite(p)
		self.invader_sp.change_img()
			
		threading.Timer(1.0, self.timerAlien).start()
		self.playing = True
		
#		
	def update(self):
		if self.playing == False:
			return
		
		# 位置移動
		self.setMonsterPosition()
		self.setMisilePosition()
		self.setFirePosition()
		
		# 衝突処理
		self.checkCollisions()
		
		# monster , alien カウント表示
		self.label_node.text="[rocket=%d monster=%02d alien=%02d ]" % (self.rocket_count,self.monster_count,self.alien_count)
		
		# 終了確認
		if self.alien_count==0 and self.monster_count==0 :
			self.label_node.text += "You Win!"
			self.playing = False
			
		if self.rocket_count==0:
			self.label_node.text += "Game Over"
			self.playing = False
		return

#		
	def draw(self):
		#print("draw")
		ellipse(50, 0, 200, 200)
		fill(0.75, 0.6, 1.0, 0.25)
		rect(150, 100, 200, 200)
		ellipse(550, 300, 300, 300)
		fill(0.75,0.55,0.4,0.25)
		rect(50, 350, 300, 300)
		return
#		
	def touch_began(self, touch):
		#print('touch_began()')
		if self.playing == False:
			return
					
		p = touch.location
		#self.label_node.text="[%04d,%04d]:began" % (p.x,p.y)
		#self.label_node.text="[monster=%02d,alien=%02d]:began" % (self.monster_count,self.alien_count)
		
		p.y = 30		
		act = Action.move_to(p.x, p.y, 1.0 , TIMING_LINEAR)
		self.rocket_sp.run_action(act)
		return

	def touch_moved(self, touch):
		p = touch.location
		#self.label_node.text="[%04d,%04d]:moved" % (p.x,p.y)
		#self.label_node.text="[monster=%02d,alien=%02d]:moved" % (self.monster_count,self.alien_count)
		self.rocket_sp.position.x = p.x
		return
		
	def touch_ended(self, touch):
		p = touch.location
		#self.label_node.text="[%04d,%04d]:ended" % (p.x,p.y)
		#self.label_node.text="[monster=%02d,alien=%02d]:ended" % (self.monster_count,self.alien_count)
		return
#

	def cuclPositionAlien(self, sp):
		p = sp.position
		p.x += 10 #X 移動距離
		#右端まで行ったら改行する
		if p.x >= self.scene_w: #self.size.w:
			p.x = 0
			p.y -= 40 #Y 移動距離
			# 最下行まで行ったら1番上に戻る
			if p.y <= 0:
					p.y = self.scene_h #self.size.h
				
		sp.position = p
		
	# Timer
	def timerAlien(self):
		if self.playing == False:
			return
		
		#Invader	
		self.invader_sp.change_img()
		self.setInvaderPosition()
		
		#Alien位置移動とイメージ変更
		for char in range(self.ALIEN_SP_CHAR_MAX):
			for no in range(self.ALIEN_SP_NO_MAX):
				self.cuclPositionAlien(self.alien_sp[char][no])
				image_no = random.randint(0, self.ALIEN_SP_NO_MAX) #self.alien_sp[no].IMG_NO_MAX-1)
				self.alien_sp[char][no].change_img(char, image_no)

		#モンスタ位置移動
		self.setMonsterPosition()		
		
		# ロケット消去
		if self.rocket_erase_count == 1:
			self.rocket_erase_count = 0
			self.rocket_sp.remove_from_parent()
		elif self.rocket_erase_count > 1:
			self.rocket_erase_count -= 1				
				
		# ロケット出現処理
		if self.rocket_create_count == 1:
			#次のロケット出現
			self.rocket_create_count = 0
			self.createRocketSprite(self.rocket_sp.position)
			self.createMisileSprite()
		elif self.rocket_create_count > 1:
			self.rocket_create_count -= 1
		#else:
				
		threading.Timer(1.0,self.timerAlien).start()	
		return
	#
	def createLabelNode(self):
		self.label_node = LabelNode('[rocket=%d monster=%02d alien=%02d]' % (self.rocket_count,self.monster_count,self.alien_count))
		self.label_node.font=('Helvetica',36)
		self.label_node.color='black'
		p = self.label_node.position
		p.y = self.size.h - 26
		p.x = self.size.w / 2
		self.label_node.position = p
		self.add_child(self.label_node)
		#print("createLabelNode")
	#
	def createAlienSpriteSingle(self, char, no):
		sp = AlienSprite()
		self.alien_sp[char][no] = sp
		p = self.alien_sp[char][no].position
		p.x = 40 + (60 * no)
		p.y = self.size.h - (self.ALIEN_Y_ORG + (80 * char))
		self.alien_sp[char][no].position = p
		self.add_child(self.alien_sp[char][no])
		self.alien_sp[char][no].change_img(char, no)
	
	#				
	def createAlienSprite(self):
		#print("createAlienSprite")
		for char in range(self.ALIEN_SP_CHAR_MAX):	
			for no in range(self.ALIEN_SP_NO_MAX):
				self.createAlienSpriteSingle(char, no)

	
	#
	def createRocketSprite(self, p):
		self.rocket_sp = RocketSprite(p)
		
		self.add_child(self.rocket_sp)	
		#print("rocket_sp.position=%s" % self.rocket_sp.position)
	
	#
	def createMonsterSprite(self, p):
		self.monster_sp = MonsterSprite(p)

		self.add_child(self.monster_sp)
		#print("monster_sp.position=%s" % self.monster_sp.position)
	
	def createFireSprite(self):
		p = self.monster_sp.position
		p.x = self.monster_sp.position.x
		p.y = self.monster_sp.position.y
		self.fire_sp = FireSprite(p)

		self.add_child(self.fire_sp) 
		#print("fire_sp.position=%s" % self.fire_sp.position)
		
	def createMisileSprite(self):
		p = self.rocket_sp.position
		p.x = self.rocket_sp.position.x
		p.y = self.rocket_sp.position.y + 60
		#print(self.misile_sp)
		self.misile_sp = MisileSprite(p)

		self.add_child(self.misile_sp)
		#print("misile_sp.position=%s" % self.misile_sp.position)
		
	def createInvaderSprite(self, p):
		self.invader_sp = InvaderSprite(p)
		self.add_child(self.invader_sp)
		
	# invader
	def setInvaderPosition(self):
		p = self.invader_sp.position
		if p.x >= self.invader_erase_point:
			#console.alert("invader erase point%d < %d" % (self.invader_erase_point ,p.x))
			self.invader_sp.remove_from_parent()
			self.invader_erase_point = 99999
			p.x = 0
			self.invader_sp.position = p
			self.invader_sp.img_no = 0
			self.createInvaderSprite(p)
			
		p = self.invader_sp.position
		if p.x > self.scene_w:
			p.x = 0
			p.y -= self.INVADER_Y_ADD
		else:
			p.x += self.INVADER_X_ADD
		self.invader_sp.position = p
			
	# monster位置セット
	def setMonsterPosition(self):			
		# Monster
		p = self.monster_sp.position
		if p.x >= self.monster_erase_point:
			self.monster_sp.remove_from_parent()
			self.monster_erase_point = 99999
			if self.monster_count > 0:
				p.x = 0
				p.y = self.scene_h - 40
				self.createMonsterSprite(p)
			
		if p.x >= self.scene_w: #self.size.w:
			#右端にきたらモンスタを消す
			self.monster_sp.remove_from_parent()
			if self.monster_count > 0:
				p.x = 0 #左端から再スタート
				p.y -= self.monster_y_add #1行下
				self.createMonsterSprite(p)
				#self.createFireSprite()
		else:
			p = self.monster_sp.position
			p.x += self.monster_x_add
			self.monster_sp.position = p
		if p.y <= self.monster_y_add:
			p = self.monster_sp.position
			p.y = self.scene_h - 40 #self.size.y / 2 - 40
			self.misile_sp.position = p

	# Fire位置セット
	def setFirePosition(self):
		p = self.fire_sp.position
		if p.y <= 0:
			self.fire_sp.remove_from_parent()
			self.fire_sp.position = self.monster_sp.position
			if self.monster_count > 0 :
				self.createFireSprite()
		else:
			p.y -=5
			self.fire_sp.position = p

	# misile位置セット
	def setMisilePosition(self):
		p = self.misile_sp.position	
		if p.y >= self.scene_h: #self.size.h:
			self.misile_sp.remove_from_parent()
			if self.rocket_count > 0 and self.rocket_create_count == 0:
				p = self.rocket_sp.position
				self.createMisileSprite()
		else:
			p.y +=10
			
		self.misile_sp.position = p

	# 衝突処理
	def checkCollisions(self):
		# Monster vs Misile		
		if self.is_collided(self.monster_sp,self.misile_sp):
			#print("monster vs misile [%s][%s]" % (self.monster_sp.position,self.misile_sp.position))
			self.misile_sp.remove_from_parent()
			self.misile_sp.position = self.rocket_sp.position
			
			self.monster_sp.color = 'red'
			self.monster_erase_point = self.monster_sp.position.x + self.MONSTER_ERASE_POINT
			if self.monster_count > 0:
				self.monster_count -= 1
					
		# Alien vs Misile
		for char in range(self.ALIEN_SP_CHAR_MAX):
			for no in range(self.ALIEN_SP_NO_MAX):	
				if self.is_collided(self.alien_sp[char][no],self.misile_sp):
					self.misile_sp.remove_from_parent()
					self.misile_sp.position = self.rocket_sp.position
					#self.alien_sp[char][no].color = 'red'
					self.alien_sp[char][no].remove_from_parent()
					self.alien_sp[char][no].position = self.size
					if self.rocket_create_count == 0:
						# 通常はミサイルを作成するが、ロケット作成待ちの間はミサイルを作成しない
						self.createMisileSprite()
					if self.alien_data[char][no]:
						continue
					else:
						self.alien_data[char][no]=True
						if self.alien_count > 0:
							self.alien_count -= 1

	
		# Rocket vs Fire
		if self.rocket_create_count == 0 and self.monster_count > 0:
			if self.is_collided(self.rocket_sp,self.fire_sp):
				#print("rocket vs fire [%s][%s]" % (self.rocket_sp.position,self.fire_sp.position))
				self.fire_sp.remove_from_parent()
				#self.fire_sp.position = self.monster_sp.position
				self.rocket_sp.color = 'red'
				self.rocket_erase_count = self.ROCKET_ERASE_COUNT_MAX
				#act = Action.fade_to(0.0, 1.0,TIMING_LINEAR)
				#self.rocket_sp.run_action(act)
				if self.rocket_count > 0:p
					self.rocket_count -= 1
					if self.rocket_count > 0:
						self.rocket_create_count = self.ROCKET_CREATE_COUNT_MAX
						#self.createRocketSprite()
			
	
		# Rocket vs Monster
		if self.is_collided(self.rocket_sp,self.monster_sp):
			#print("rocket vs monster [%s][%s]" % (self.rocket_sp.position,self.monster_sp.position))
			#self.spMonster.remove_from_parent()
			act = Action.fade_to(0.0, 1.0,TIMING_LINEAR)
			self.monster_sp.run_action(act)
			self.rocket_sp.run_action(act)
			if self.monster_count > 0:
				self.monster_count -= 1
				if self.monster_count > 0:
					self.monster_create_count = self.MONSTER_CREATE_COUNT_MAX
					#self.createMonsterSprite()
			if self.rocket_count > 0:
				self.rocket_count -= 1
				if self.rocket_count > 0:
					self.rocket_create_count = self.ROCKET_CREATE_COUNT_MAX
					#self.createRocketSprite()
		
		# invadar vs misille
		if self.is_collided(self.invader_sp,self.misile_sp):
			if self.invader_sp.img_no == 0 or self.invader_sp.img_no == 1:
				self.misile_sp.remove_from_parent()
				#self.misile_sp.position = self.rocket_sp.position
				self.invader_sp.img_no = 2 # red
				self.invader_erase_point = self.invader_sp.position.x + self.INVADER_ERASE_POINT
				#print("invader vs missile")
				#console.alert('invader vs missile')
				#result = dialogs.text_dialog(title="invader vs missile")

			
		#衝突確認
	def is_collided(self,my, target):
		dx = abs(my.position.x - target.position.x)
		dy = abs(my.position.y - target.position.y)
		return abs(sqrt(dx **2 + dy **2)) < 50
			
if __name__ == '__main__':
	SpriteNode('INVADER.png')
	run(GameScene())