Chapter 5 : Invaders Revenge – An Interactive Multitouch Game
เเบ่งพื้นที่ออกเป็น 2 ส่วน
1. Enemy Area = เป็นพื้นที่ของผู้รุกราน
2. Shooter Area = เป็นพื้นที่ของผู้เล่น
ส่วนอื่นๆ
1. Boom = การยิง
2. Invader = ผู้รุกราน
3. Dock = ยาน
4. Fleet = ฐานทัพ ยานเเม่
5. Missile = จรวจ
6. Shot = กระสุน
7. Shooter = ปืน
Code Python
Code Python file name : main # สร้างหน้า Window
# File name: main.py
import kivy
kivy.require('1.7.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.animation import Animation
from kivy.clock import Clock
from fleet import Fleet
from shooter import Shooter
Builder.load_file('images.kv')
class Invasion(FloatLayout):
def __init__(self, **kwargs):
super(Invasion, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(self.close, self)
self._keyboard.bind(on_key_down=self.press)
self.start_game()
def close(self):
self._keyboard.unbind(on_key_down=self.press)
self._keyboard = None
def press(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'left':
self.shooter.center_x -= 30
elif keycode[1] == 'right':
self.shooter.center_x += 30
return True
def start_game(self):
label = Label(text='Ready!')
animation = Animation(font_size=72, d=2)
animation.bind(on_complete=self.fleet.start_attack)
self.add_widget(label)
animation.start(label)
def end_game(self, message):
label = Label(markup=True, size_hint=(.2, .1),pos=(0, self.parent.height/2), text=message)
self.add_widget(label)
self.composed_animation().start(label)
def composed_animation(self):
animation = Animation(center=self.parent.center)
animation &= Animation(font_size=72, d=3)
animation += Animation(font_size=24, y=0, d=2)
return animation
class InvasionApp(App):
def build(self):
return Invasion()
if __name__ == "__main__":
InvasionApp().run()
Animation(font_size,y,x,d) # (ขนาด,ตำเเหน่ง y ,ตำเเหน่ง x ,หน่วงเวลา)
# keycodes
# specials keys
'backspace': 8, 'tab': 9, 'enter': 13, 'rshift': 303, 'shift': 304,
'alt': 308, 'rctrl': 306, 'lctrl': 305,
'super': 309, 'alt-gr': 307, 'compose': 311, 'pipe': 310,
'capslock': 301, 'escape': 27, 'spacebar': 32, 'pageup': 280,
'pagedown': 281, 'end': 279, 'home': 278, 'left': 276, 'up':
273, 'right': 275, 'down': 274, 'insert': 277, 'delete': 127,
'numlock': 300, 'print': 144, 'screenlock': 145, 'pause': 19,
# a-z keys
'a': 97, 'b': 98, 'c': 99, 'd': 100, 'e': 101, 'f': 102, 'g': 103, 'h': 104, 'i': 105, 'j': 106, 'k': 107, 'l': 108, 'm': 109, 'n': 110, 'o': 111, 'p': 112, 'q': 113, 'r': 114, 's': 115, 't': 116, 'u': 117, 'v': 118, 'w': 119, 'x': 120, 'y': 121, 'z': 122, # 0-9 keys '0': 48, '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55, '8': 56, '9': 57,
# a-z keys
'a': 97, 'b': 98, 'c': 99, 'd': 100, 'e': 101, 'f': 102, 'g': 103, 'h': 104, 'i': 105, 'j': 106, 'k': 107, 'l': 108, 'm': 109, 'n': 110, 'o': 111, 'p': 112, 'q': 113, 'r': 114, 's': 115, 't': 116, 'u': 117, 'v': 118, 'w': 119, 'x': 120, 'y': 121, 'z': 122, # 0-9 keys '0': 48, '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55, '8': 56, '9': 57,
# numpad
'numpad0': 256, 'numpad1': 257, 'numpad2': 258, 'numpad3': 259,
'numpad4': 260, 'numpad5': 261, 'numpad6': 262, 'numpad7': 263,
'numpad8': 264, 'numpad9': 265, 'numpaddecimal': 266,
'numpaddivide': 267, 'numpadmul': 268, 'numpadsubstract': 269,
'numpadadd': 270, 'numpadenter': 271,
# F1-15
'f1': 282, 'f2': 283, 'f3': 284, 'f4': 285, 'f5': 286, 'f6': 287,
'f7': 288, 'f8': 289, 'f9': 290, 'f10': 291, 'f11': 292, 'f12': 293,
'f13': 294, 'f14': 295, 'f15': 296,
# other keys
'(': 40, ')': 41,
'[': 91, ']': 93,
'{': 123, '}': 125,
':': 59, ';': 59,
'=': 61, '+': 43,
'-': 45, '_': 95,
'/': 47, '*': 42,
'?': 47,
'`': 96, '~': 126,
'´': 180, '¦': 166,
'\\': 92, '|': 124,
'"': 34, "'": 39,
',': 44, '.': 46,
'<': 60, '>': 62,
'@': 64, '!': 33,
'#': 35, '$': 36,
'%': 37, '^': 94,
'&': 38, '¬': 172,
'¨': 168, '…': 8230,
'ù': 249, 'à': 224,
'é': 233, 'è': 232
Code Python file name : shooter # ปืน
# File name: shooter.py
import kivy
kivy.require('1.7.0')
from kivy.clock import Clock
from kivy.uix.image import Image
from ammo import Shot
class Shooter(Image):
reloaded = True
def on_touch_down(self, touch):
if self.parent.collide_point(*touch.pos):
self.center_x = touch.x
touch.ud['move'] = True
elif self.enemy_area.collide_point(*touch.pos):
self.shoot(touch.x, touch.y)
touch.ud['shoot'] = True
def on_touch_move(self, touch):
if self.parent.collide_point(*touch.pos):
self.center_x = touch.x
elif self.enemy_area.collide_point(*touch.pos):
self.shoot(touch.x, touch.y)
def on_touch_up(self, touch):
if 'shoot' in touch.ud and touch.ud['shoot']:
self.reloaded = True
def shoot(self, fx, fy):
if self.reloaded:
self.reloaded = False
Clock.schedule_once(self.reload_gun, .5)
shot = Shot()
shot.center = (self.center_x, self.top)
self.invasion.add_widget(shot)
(fx, fy) = self.project(self.center_x, self.top, fx, fy)
shot.shoot(fx, fy, self.invasion.fleet)
def reload_gun(self, dt):
self.reloaded = True
def collide_ammo(self, ammo):
if self.collide_widget(ammo) and self.parent:
self.parent.remove_widget(self)
self.invasion.end_game("Game Over")
return True
return False
def project(self, ix, iy, fx, fy):
(w, h) = self.invasion.size
if ix == fx: return (ix, h)
m = (fy-iy) / (fx-ix)
b = iy - m*ix
x = (h-b)/m
if x < 0: return (0, b)
elif x > w: return (w, m*w+b)
return (x, h)
Code Python file name : invader # ผู้รุกราน
# File name: invader.py
import kivy
kivy.require('1.7.0')
from kivy.core.window import Window
from kivy.uix.image import Image
from kivy.animation import Animation
from random import choice, randint
from ammo import Missile
class Invader(Image):
pre_fix = ['in_', 'out_', 'in_out_']
functions = ['back', 'bounce', 'circ', 'cubic', 'elastic',
'expo', 'quad', 'quart', 'quint', 'sine']
formation = True
def solo_attack(self):
if self.formation:
self.parent.unbind_invader()
animation = self.trajectory()
animation.bind(on_complete=self.to_dock)
animation.start(self)
def trajectory(self):
fleet = self.parent.parent
area = fleet.parent
x = choice((-self.width, area.width+self.width))
y = randint(round(area.y), round(fleet.y))
t = choice(self.pre_fix) + choice(self.functions)
return Animation(x=x, y=y, d=randint(2, 7), t=t)
def to_dock(self, instance, value):
self.y = Window.height
self.center_x = Window.width/2
animation = Animation(pos=self.parent.pos, d=2)
animation.bind(on_complete=self.parent.bind_invader)
animation.start(self)
def drop_missile(self):
missile = Missile()
missile.center = (self.center_x, self.y)
fleet = self.parent.parent
fleet.invasion.add_widget(missile)
missile.shoot(self.center_x, 0, fleet.shooter)
Code Python file name : fleet # ยานเเม่
# File name: fleet.py
import kivy
kivy.require('1.7.0')
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ListProperty
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.core.window import Window
from random import randint, random
from dock import Dock
class Fleet(GridLayout):
survivors = ListProperty(())
def __init__(self, **kwargs):
super(Fleet, self).__init__(**kwargs)
for x in range(0, 32):
dock = Dock()
self.add_widget(dock)
self.survivors.append(dock)
self.center_x = Window.width/4
def start_attack(self, instance, value):
self.invasion.remove_widget(value)
self.go_left(instance, value)
self.schedule_events()
def go_left(self, instance, value):
animation = Animation(x=0)
animation.bind(on_complete=self.go_right)
animation.start(self)
def go_right(self, instance, value):
animation = Animation(right=self.parent.width)
animation.bind(on_complete=self.go_left)
animation.start(self)
def schedule_events(self):
Clock.schedule_interval(self.solo_attack, 2)
Clock.schedule_once(self.shoot, random())
def solo_attack(self, dt):
if len(self.survivors):
rint = randint(0, len(self.survivors)-1)
child = self.survivors[rint]
child.invader.solo_attack()
def shoot(self, dt):
if len(self.survivors):
rint = randint(0, len(self.survivors)-1)
child = self.survivors[rint]
child.invader.drop_missile()
Clock.schedule_once(self.shoot, random())
def collide_ammo(self, ammo):
for child in self.survivors:
if child.invader.collide_widget(ammo):
child.canvas.clear()
self.survivors.remove(child)
return True
return False
def on_survivors(self, instance, value):
if len(self.survivors) == 0:
Clock.unschedule(self.solo_attack)
Clock.unschedule(self.shoot)
self.invasion.end_game("You Win!")
Code Python file name : dock # ยานลูก
# File name: dock.py
import kivy
kivy.require('1.7.0')
from kivy.uix.widget import Widget
from invader import Invader
class Dock(Widget):
def __init__(self, **kwargs):
super(Dock, self).__init__(**kwargs)
self.invader = Invader()
self.add_widget(self.invader)
self.bind_invader()
def bind_invader(self, instance=None, value=None):
self.invader.formation = True
self.bind(pos=self.on_pos)
def unbind_invader(self):
self.invader.formation = False
self.unbind(pos=self.on_pos)
def on_pos(self, instance, value):
self.invader.pos = self.pos
Code Python file name : boom # โหลดไฟล์ เสียง
# File name: boom.py
import kivy
kivy.require('1.7.0')
from kivy.uix.image import Image
from kivy.core.audio import SoundLoader
class Boom(Image):
sound = SoundLoader.load('boom.wav')
def __init__(self, **kwargs):
self.__class__.sound.play()
super(Boom, self).__init__(**kwargs)
sound = SoundLoader.load('boom.wav') # โหลดไฟล์เสียง
__class__.sound.play() # เล่นไฟล์เสียง
__class__.sound.play() # เล่นไฟล์เสียง
Code Python file name :ammo # กระสุน
# File name: ammo.py
import kivy
kivy.require('1.7.0')
from kivy.animation import Animation
from kivy.uix.image import Image
from boom import Boom
class Ammo(Image):
def shoot(self, tx, ty, target):
self.target = target
self.animation = Animation(x=tx, top=ty)
self.animation.bind(on_start=self.on_start)
self.animation.bind(on_progress=self.on_progress)
self.animation.bind(on_complete=self.on_stop)
self.animation.start(self)
def on_start(self, instance, value):
self.boom = Boom()
self.boom.center = self.center
self.parent.add_widget(self.boom)
def on_progress(self, instance, value, progression):
if progression >= .1:
self.parent.remove_widget(self.boom)
if self.target.collide_ammo(self):
self.animation.stop(self)
def on_stop(self, instance, value):
self.parent.remove_widget(self)
class Shot(Ammo):
pass
class Missile(Ammo):
pass
File .kv
File name : images.kv # โหลดภาพ
# File name: images.kv
#:kivy 1.7.0
<Invader@Image>:
source: 'atlas://img/invasion/invader'
size_hint: None,None
size: 40,40
<Shooter@Image>:
source: 'atlas://img/invasion/shooter'
size_hint: None,None
size: 40,40
pos: self.parent.width/2, 0
<Boom@Image>:
source: 'atlas://img/invasion/boom'
size_hint: None,None
size: 26,30
<Shot@Ammo>:
source: 'atlas://img/invasion/shot'
size_hint: None,None
size: 12,15
<Missile@Ammo>:
source: 'atlas://img/invasion/missile'
size_hint: None,None
size: 12,27
File name : invasion.kv # สร้าง Layout
# File name: invasion.kv
#:kivy 1.7.0
<Invasion>:
id: _invasion
shooter: _shooter
fleet: _fleet
AnchorLayout:
anchor_y: 'top'
anchor_x: 'center'
FloatLayout:
id: _enemy_area
size_hint: 1, .7
Fleet:
id: _fleet
invasion: _invasion
shooter: _shooter
cols: 8
spacing: 40
size_hint: .5, .4
pos_hint: {'top': .9}
x: root.width/2-root.width/4
AnchorLayout:
anchor_y: 'bottom'
anchor_x: 'center'
FloatLayout:
size_hint: 1, .3
Shooter:
id: _shooter
invasion: _invasion
enemy_area: _enemy_area
Workspace
ผลลัพธ์ที่ได้...