วันจันทร์ที่ 28 กันยายน พ.ศ. 2558

Chapter 5 : Invaders Revenge – An Interactive Multitouch Game



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, 
# 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() # เล่นไฟล์เสียง
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

ผลลัพธ์ที่ได้...




วันจันทร์ที่ 21 กันยายน พ.ศ. 2558

Chapter 2 : Graphics : The Canvas

Basic shapes

Code Python file name : drawing
 
   from kivy.app import App
   from kivy.uix.relativelayout import RelativeLayout

   class DrawingSpace(RelativeLayout):
      pass

   class DrawingApp(App):
      def build(self):
         return DrawingSpace()
    
   if __name__=="__main__":
      DrawingApp().run()
       
File name : drawing.kv
 
  <DrawingSpace>:
  canvas:
  Rectangle: 
   pos: self.x+10,self.top-80
   size: self.width*0.15, self.height*0.3
  Ellipse: 
   angle_start: 120 
   angle_end: 420 
   pos: 110, 110
   size: 80,80
  Ellipse:
   segments: 3 
   pos: 210,110
   size: 60,80
  Triangle: 
   points: 310,110,340,190,380,130
  Quad: 
   points: 410,110,430,180,470,190,490,120
  Line: 
   points: 10,30, 90,90, 90,10, 10,60
  Point: 
   points: 110,30, 190,90, 190,10, 110,60
   pointsize: 3 
  Bezier: 
   points: 210,30, 290,90, 290,10, 210,60
   segments: 360
   dash_length: 10
   dash_offset: 5 
  Mesh: 
   mode: 'triangle_fan'
   vertices: 310,30,0,0, 390,90,0,0, 390,10,0,0, 310,60,0,0
   indices: 0,1,2,3
  Mesh:
   mode: 'triangle_fan'
   vertices: 430,90,0,0, 470,90,0,0, 490,70,0,0, 450,10,0,0,410,70,0,0
   indices: 0,1,2,3,4
   

ผลลัพธ์ที่ได้...

Basic shapes

Code Python file name : drawing
 
   from kivy.app import App
   from kivy.uix.relativelayout import RelativeLayout

   class DrawingSpace(RelativeLayout):
      pass

   class DrawingApp(App):
      def build(self):
         return DrawingSpace()
    
   if __name__=="__main__":
      DrawingApp().run()
 
 File name : drawing.kv
 
 <DrawingSpace>:
  canvas:
   Line:
    ellipse: 10, 20, 80, 60, 120, 420, 180
    width: 2
   Line:
    circle: 150, 50, 40, 0, 360, 180
   Line:
    rectangle: 210,10,80,80
   Line:
    points: 310,10,340,90,390,20
    close: True
   

ผลลัพธ์ที่ได้...

Images, colors, and backgrounds

Code Python file name : drawing
 
   from kivy.app import App
   from kivy.uix.relativelayout import RelativeLayout

   class DrawingSpace(RelativeLayout):
      pass

   class DrawingApp(App):
      def build(self):
         return DrawingSpace()
    
   if __name__=="__main__":
      DrawingApp().run()
 
 File name : drawing.kv
 
<DrawingSpace>:
 canvas:
  Ellipse:
   pos: 10,10
   size: 80,80
   source: 'kivy.png'
  Rectangle:
   pos: 110,10
   size: 80,80
   source: 'kivy.png'
  Color:
   rgba: 0,0,1,.75
  Line:
   points: 10,10,390,10
   width: 10
   #cap: 'square'
   #cap: 'round'
   cap: 'none'
  Color:
   rgba: 0,1,0,1
  Rectangle:
   pos: 210,10
   size: 80,80
   source: 'kivy.png'
  Rectangle:
   pos: 310,10
   size: 80,80
   

ผลลัพธ์ที่ได้...

Images, colors, and backgrounds

Code Python file name : drawing
 
   from kivy.app import App
   from kivy.uix.relativelayout import RelativeLayout

   class DrawingSpace(RelativeLayout):
      pass

   class DrawingApp(App):
      def build(self):
         return DrawingSpace()
    
   if __name__=="__main__":
      DrawingApp().run()
 
 File name : drawing.kv
 
<DrawingSpace>:
 canvas.before:
  Color:
   rgba: 1,0,0,1
  Rectangle:
   pos: 0,0
   size: 100,100
 canvas:
  Color:
   rgba: 0,1,0,1
  Rectangle:
   pos: 100,0
   size: 100,100
 canvas.after:
  Color:
   rgba: 0,0,1,1
  Rectangle:
   pos: 200,0
   size: 100,100
 Button:
  text: 'A very very very long button'
  pos_hint: {'center_x': .5, 'center_y': .5}
  size_hint: .9,.1
   

ผลลัพธ์ที่ได้...

สรุป


  # รูปที่ 1 เเถว บน
  Rectangle: # รูปสี่เหลี่ยม
   pos: self.x+10,self.top-80
   size: self.width*0.15, self.height*0.3

  # รูปที่ 2 เเถว บน  
  Ellipse: # วงรี
   angle_start: 120 # เริ่มที่ *มุม
   angle_end: 420 # สิ้นสุดที่ *มุม
   pos: 110, 110
   size: 80,80

  # รูปที่ 3 เเถว บน
  Ellipse:
   segments: 3 # จำนวนของมุม
   pos: 210,110
   size: 60,80

  # รูปที่ 4 เเถว บน
  Triangle: # สามเหลี่ยม
   points: 310,110,340,190,380,130

  # รูปที่ 5 เเถว บน  
  Quad: # สี่เหลี่ยมคางหมู
   points: 410,110,430,180,470,190,490,120

  # รูปที่ 6 เเถว ล่าง
  Line: # เส้นตรง
   points: 10,30, 90,90, 90,10, 10,60

  # รูปที่ 7 เเถว ล่าง
  Point: # จุด
   points: 110,30, 190,90, 190,10, 110,60
   pointsize: 3 # ขนาดของจุด

  # รูปที่ 8 เเถว ล่าง
  Bezier: # เส้นโค้ง
   points: 210,30, 290,90, 290,10, 210,60
   segments: 360
   dash_length: 10 # ความยาวของเส้นที่วาด
   dash_offset: 5 # ความยาวของการปล่อยว่าง

  # รูปที่ 9 เเถว ล่าง
  Mesh: # ตาข่าย
   mode: 'triangle_fan'
   vertices: 310,30,0,0, 390,90,0,0, 390,10,0,0, 310,60,0,0
   indices: 0,1,2,3

  # รูปที่ 10 เเถว ล่าง
  Mesh:
   mode: 'triangle_fan'
   vertices: 430,90,0,0, 470,90,0,0, 490,70,0,0, 450,10,0,0,410,70,0,0
   indices: 0,1,2,3,4



  ที่มา : 
www3.ntu.edu.sg/home/ehchua/programming/opengl/cg_basicstheory.html
 

  # ไฟล์ภาพ ชื่อ "kivy.png" ใน Folder งาน
  Ellipse:
   pos: 10,10
   size: 80,80
   source: 'kivy.png' # โหลดภาพ
  Rectangle:
   pos: 110,10
   size: 80,80
   source: 'kivy.png'
# โหลดภาพ
   Line:
    points: 10,10,390,10
    width: 10
    #cap: 'square' # ตามขนาดของกรอบ
    #cap: 'round' # ตัดมุมขอบ  
    #cap: 'none' # ตัดขอบพอดี
 
   Color:
    rgba: 0,1,0,1 # R,G,B,ความสว่างของเเสง





วันพุธที่ 16 กันยายน พ.ศ. 2558

Chapter 1 : GUI Basics – Building an Interface

Hello World!

Code Python
 
   import kivy
   kivy.require('1.7.0')
   from kivy.app import App
   from kivy.uix.button import Label
   class HelloApp(App):
       def build(self):
           return Label(text = 'Hello World!')
   if __name__=="__main__":
       HelloApp().run()
     

ผลลัพธ์ที่ได้...


Hello World! เเบบที่ 2

Code Python
 
      from kivy.app import App
   from kivy.uix.button import Label
   class Hello2App(App):
       def build(self):
           return Label()
   if __name__=="__main__":
       Hello2App().run()
   
File name : hello2.kv
 
     <Label>:
      text: 'Hello World!'
   

ผลลัพธ์ที่ได้...

Layouts

Code Python
 
      from kivy.app import App
   from kivy.uix.floatlayout import FloatLayout
   class FloatLayoutApp(App):
       def build(self):
            return FloatLayout()
   if __name__=="__main__":
       FloatLayoutApp().run()
   
File name : floatlayout.kv
 
   <Button>:
      color: .8,.9,0,1
      font_size: 32
      size_hint: .4, .3
   <FloatLayout>:
     Button:
       text: 'Hello'
       pos_hint: {'x': 0, 'top': 1}
     Button:
       text: 'World!'
       pos_hint: {'right': 1, 'y': 0}
   

ผลลัพธ์ที่ได้...

Embedding layouts

Code Python
 
      from kivy.app import App
   from kivy.uix.gridlayout import GridLayout
   class MyGridLayout(GridLayout):
       pass
   class LayoutsApp(App):
       def build(self):
           return MyGridLayout()
   if __name__=="__main__":
       LayoutsApp().run()
   
File name : layouts.kv
 
  <MyGridLayout>:
  rows: 2
  FloatLayout:
    Button:
      text: 'F1'
      size_hint: .3, .3
      pos: 0, 0
  RelativeLayout:
    Button:
      text: 'R1'
      size_hint: .3, .3
      pos: 0, 0
  GridLayout:
    cols: 2
    spacing: 10
    Button:
      text: 'G1'
      size_hint_x: None
      width: 50
    Button:
      text: 'G2'
    Button:
      text: 'G3'
      size_hint_x: None
      width: 50
  AnchorLayout:
    anchor_x: 'right'
    anchor_y: 'top'
    Button:
      text: 'A1'
      size_hint: [.5, .5]
    Button:
      text: 'A2'
      size_hint: [.2, .2]
  BoxLayout:
    orientation: 'horizontal'
    Button:
      text: 'B1'
    Button:
      text: 'B2'
      size_hint: [2, .3]
      pos_hint: {'y': .4}
    Button:
      text: 'B3'
  StackLayout:
    orientation: 'rl-tb'
    padding: 10
    Button:
      text: 'S1'
      size_hint: [.6, .2]
    Button:
      text: 'S2'
      size_hint: [.4, .4]
    Button:
      text: 'S3'
      size_hint: [.3, .2]
    Button:
      text: 'S4'
      size_hint: [.4, .3]
   

ผลลัพธ์ที่ได้...