ผลต่างระหว่างรุ่นของ "Oop lab/oop in python"

จาก Theory Wiki
ไปยังการนำทาง ไปยังการค้นหา
แถว 393: แถว 393:
 
                                   20, arcade.color.BLUE)
 
                                   20, arcade.color.BLUE)
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
สร้าง object ของคลาส Python ไว้บริเวณใกล้ ๆ กับที่ประกาศ circles  (การหาร // คือหารปัดเศษ)
 +
 +
<syntaxhighlight lang="python">
 +
circles = []
 +
player = Player(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
 +
n = 10
 +
</syntaxhighlight>
 +
 +
เพิ่มโค้ดให้แสดง player ใน on_draw
 +
 +
<syntaxhighlight lang="python">
 +
def on_draw(delta_time):
 +
    arcade.start_render()
 +
 +
    for c in circles:
 +
        c.move()
 +
        c.draw()
 +
 +
    player.draw()
 +
</syntaxhighlight>
 +
 +
=== ขยับ player ===
 +
 +
=== โดนชน ===
  
 
== ชนและเด้ง ==
 
== ชนและเด้ง ==

รุ่นแก้ไขเมื่อ 02:49, 1 กันยายน 2560

หน้านี้เป็นส่วนหนึ่งของวิชา Oop lab

เราจะหัดเขียน OOP กันผ่านทางตัวอย่าง โดยเราจะเขียนโปรแกรมแสดงผลเป็นกราฟิกส์โดยใช้ไลบรารีชื่อ Arcade ซึ่งต้องการ Python 3.6 เป็นอย่างน้อย

ติดตั้ง Arcade

เราจะใช้ game library ชื่อ arcade ในการพัฒนาเกม

Python 3.6

ทดลองเรียก

python --version

ถ้าระบบตอบว่าเป็นเวอร์ชั่นสูงกว่าหรือเท่ากับ 3.6 ก็ไปทำขั้นตอนการติดตั้ง pip ได้เลย ไม่เช่นนั้นให้ไปติดตั้ง Python 3.6 ตามวิธีต่อไปนี้

ติดตั้ง Python 3.6 บน Windows

ให้ทำตามขั้นตอนในนี้ได้เลย การติดตั้ง arcade อย่าลืมเลือกให้ลง python ใน path ด้วย

ติดตั้ง Python 3.6 บน Ubuntu (รุ่นตั้งแต่ 16.10 ขึ้นไป)

สั่ง

sudo apt-get update
sudo apt-get install python3.6

เวลาเรียกใช้ให้เรียก python3.6

ติดตั้ง Python 3.6 บน Ubuntu เก่า

sudo add-apt-repository ppa:jonathonf/python-3.6
sudo apt-get update
sudo apt-get install python3.6

ติดตั้ง pip/pip3

pip เป็นโปรแกรมสำหรับติดตั้ง library บน Python ในระบบที่มี python3 พร้อม ๆ กับ python2 เราจะเรียก pip3 เพื่อให้ติดตั้ง library ลงในส่วนของ python3

ให้ลองเรียก

pip

หรือ

pip3

ถ้ามีโปรแกรมแล้วก็ข้ามส่วนติดตั้ง pip ได้เลย

1. ติดตั้ง pip บน Windows

pip จะมาพร้อมกับการติดตั้ง python ให้เลือก option ดังกล่าวด้วย ถ้าเปิด command แล้วเรียก pip ขึ้นก็แสดงว่าได้ติดตั้งเรียบร้อย ถ้าไม่มี ให้ลองติดตั้ง python3 อีกครั้ง และให้เลือก pip ในหน้า Optional Features ด้วย

2. ติดตั้ง pip3 บน Linux

ให้ติดตั้ง pip3 และโปรแกรมประกอบอื่น ๆ โดยสั่ง

sudo apt install -y python3-dev python3-pip libjpeg-dev zlib1g-dev

3. ติดตั้ง pip3 บน Mac

ถ้าติดตั้ง python3 แล้ว น่าจะมี pip3 มาแล้ว ทดลองเรียกดูใน terminal

บน Mac จะต้องติดตั้ง libjpg ด้วย ถ้ายังไม่มี ถ้าคุณมี homebrew อยู่แล้ว ให้สั่ง

brew install libjpeg

ถ้าไม่ได้ ให้ไปดาวน์โหลดและ install จาก [1] (เลือก libjpg)

ใช้ pip ติดตั้ง arcade

ถ้าในการติดตั้ง python เราได้ลง pip มาแล้ว เราจะสามารถติดตั้ง arcade ผ่านทาง pip ได้โดยสั่ง

sudo pip3 install arcade

ถ้าเป็น windows ให้สั่ง

pip install arcade

virtualenv

เราติดตั้ง arcade ลงใน library ของระบบเลย ซึ่งทำแบบนี้บ่อย ๆ อาจจะทำให้ library เละและตีกันได้ Python มีระบบสำหรับติดตั้ง library แยกกัน เรียกว่า virtualenv

ยังเขียนส่วนนี้ไม่เสร็จ: to do - how to install with virtualenv

ทดสอบการติดตั้ง

ให้ copy code ด้านล่างนี้ ในไฟล์ชื่อ cirtest.py แล้วทดลองรัน

import arcade
from random import randint

SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600

circle_size = 1
size_direction = 1
circle_xs = []
circle_ys = []
num_circles = 100

def random_locations():
    for i in range(num_circles):
        circle_xs.append(randint(10,SCREEN_WIDTH-10))
        circle_ys.append(randint(10,SCREEN_HEIGHT-10))

def on_draw(delta_time):
    global circle_size, size_direction
    
    circle_size += size_direction
    if circle_size > 50:
        size_direction = -1
    elif circle_size == 1:
        size_direction = 1

    arcade.start_render()

    for x,y in zip(circle_xs, circle_ys):
        arcade.draw_circle_outline(x, y, circle_size, arcade.color.BLACK)

    
def main():
    random_locations()

    arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT,
                       "Circles")
    arcade.set_background_color(arcade.color.WHITE)

    arcade.schedule(on_draw, 1 / 80)
    arcade.run()

if __name__ == '__main__':
    main()

อธิบายบางประเด็น

  • randint -- สุ่มเลขระหว่างเลขที่ระบุ ต้อง import random ก่อน
  • global -- ใน Python ฟังก์ชันจะสามารถอ้างถึงตัวแปรโกลบอลได้ถ้าอ่านค่าอย่างเดียว แต่ถ้าต้องการเปลี่ยนแปลงตัวแปรโกลบอลจะต้องประกาศให้ชัดเจนว่าจะมีการใช้ตัวแปรโกลบอลนั้น ในฟังก์ชัน on_draw เราต้องการเปลี่ยนค่าขนาดวงกลมและทิศทางการเปลี่ยนค่า เราจึงต้องประกาศ
  • zip -- ฟังก์ชัน zip นำลิสต์สองลิสต์มารวมเข้าด้วยกัน เช่น zip([1,2,3],['a','b','c']) ได้ผลลัพธ์เทียบเท่ากับ [(1, 'a'), (2, 'b'), (3, 'c')] มักใช้เวลาต้องการ for ไปในรายการหลายอันพร้อม ๆ กัน
  • ฟังก์ชันจาก arcade
    • arcade.start_render ให้เรียกก่อนจะวาดหน้าจอ จะลบหน้าจอเดิม (มีทดลองในส่วนถัดไป)
    • arcade.draw_circle_outline วาดวงกลม
    • arcade.open_window
    • arcade.set_background_color
    • arcade.schedule
    • arcade.run

วงกลมขยับ

วาดวงกลม

เราจะเริ่มเขียนจากโปรแกรมที่ทำอะไรไม่ค่อยได้ ไปหาโปรแกรมที่ซับซ้อนขึ้น โปรแกรมด้านล่างวาดรูปวงกลมที่กลางหน้าจอ เขียนและเซฟไว้ในไฟล์ชื่อ cir1.py

import arcade

SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600

def on_draw(delta_time):
    arcade.start_render()
    
    x = 300
    y = 300
    arcade.draw_circle_outline(x, y, 20, arcade.color.BLACK)

    
def main():
    arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT,
                       "Circles")
    arcade.set_background_color(arcade.color.WHITE)

    arcade.schedule(on_draw, 1 / 80)
    arcade.run()

if __name__ == '__main__':
    main()

ทดลอง: ให้ทดลองลบบรรทัด arcade.start_render() ออก ผลเป็นอย่างไร?

เคลื่อนที่

เราจะบังคับให้วงกลมเคลื่อนที่ โดยปรับค่าตำแหน่งตามความเร็วสองแกนคือ vx แทนความเร็วในแนวแกน x และ vy แทนความเร็วในแกน y

เนื่องจากเราต้องการเปลี่ยนค่าตำแหน่งวงกลมอย่างต่อเนื่อง เราจึงต้องมีตัวแปรโกลบอลเก็บค่าตำแหน่ง คือ x และ y ในฟังก์ชัน on_draw เราจะระบุว่าจะแก้ค่าตัวแปรดังกล่าวด้วย keyword global เราใส่ vx และ vy เผื่อไว้ด้วยเลย (เพราะว่าเราต้องการให้สะท้อนขอบจอ)

โค้ดของ on_draw เป็นดังนี้ บรรทัดที่ประกาศ x,y,vx,vy เป็นการกำหนดค่าให้กับตัวแปรโกลบอล

vx = 2
vy = 1
x = 300
y = 300

def on_draw(delta_time):
    arcade.start_render()
    
    global x, y, vx, vy

    x += vx
    y += vy
    
    arcade.draw_circle_outline(x, y, 20, arcade.color.BLACK)

แบบฝึกหัด 1: ชนและสะท้อนที่ขอบ

ตอนนี้ลูกบอลเมื่อวิ่งทะลุขอบจอแล้วจะหายไปเลย ให้เพิ่มการตรวจสอบว่าลูกบอลทะลุจอไปแล้วแล้วปรับให้ลูกบอลเสมือนเด้งกลับมา

def on_draw(delta_time):
    arcade.start_render()
    
    global x, y, vx, vy

    x += vx
    y += vy

    # ตรวจสอบตำแหน่ง ถ้าตกขอบให้เปลี่ยนทิศทาง
    # hint: ให้ทำทีละแกน จะง่ายกว่า
    
    arcade.draw_circle_outline(x, y, 20, arcade.color.BLACK)

วงกลมหลาย ๆ วง

เราจะแก้โปรแกรมให้มีลูกบอลวิ่งไปมาหลาย ๆ ลูก ให้เซฟโปรแกรมที่เขียนในไฟล์ใหม่ชื่อ cir2.py

เราจะสุ่มตำแหน่งและความเร็วของลูกบอลด้วยฟังก์ชัน randint ดังนั้นให้เพิ่มบรรทัด import ดังด้านล่าง

import arcade
from random import randint

เราจะเก็บตำแหน่งและความเร็วในแกนต่าง ๆ ของลูกบอลในลิสต์ xs, ys, vxs, vys และจำนวนลูกบอลในตัวแปร n

vxs = []
vys = []
xs = []
ys = []
n = 10

def initialize():
    for i in range(n):
        xs.append(randint(100, SCREEN_WIDTH-100))
        ys.append(randint(100, SCREEN_HEIGHT-100))
        vxs.append(randint(-5,5))
        vys.append(randint(-5,5))

เราจะเขียนฟังก์ชันสำหรับจัดการลูกบอลลูกที่ i ในรายการดังด้านล่าง จากนั้น on_draw จะเรียกฟังก์ชันนี้ สังเกตว่าเรายังไม่ได้จัดการการชนขอบจอ

def draw_and_move_circle(i):
    xs[i] += vxs[i]
    ys[i] += vys[i]

    arcade.draw_circle_outline(xs[i], ys[i], 20, arcade.color.BLACK)

on_draw วนลูบเรียกฟังก์ชัน draw_and_move_circle

def on_draw(delta_time):
    arcade.start_render()

    for i in range(n):
        draw_and_move_circle(i)

อย่าลืมเรียก initialize ใน main

def main():
    initialize()
    
    arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT,
                       "Circles")
    arcade.set_background_color(arcade.color.WHITE)

    arcade.schedule(on_draw, 1 / 80)
    arcade.run()

แยกฟังก์ชัน

ฟังก์ชัน draw_and_move_circle เป็นตัวอย่างของฟังก์ชันที่พยายามทำหลายอย่างมากเกินไป เราจะแยกฟังก์ชันออกเป็นสองฟังก์ชัน แล้วเรียกใน on_draw

def move_circle(i):
    # .... อย่าลืมย้ายโค้ดมา

def draw_circle(i):
    # .... อย่าลืมย้ายโค้ดมา

def on_draw(delta_time):
    arcade.start_render()

    for i in range(n):
        move_circle(i)
        draw_circle(i)

แบบฝึกหัด 2: ชนและสะท้อนที่ขอบ

โปรแกรมที่มีลูกบอลหลายลูกที่เราเขียนยังไม่ได้มีการจัดการเรื่องการชนกับขอบ ให้แก้ฟังก์ชัน move_circle ให้ตรวจสอบและปรับทิศทางของลูกบอลให้สะท้อนกลับมาด้วย

คลาส Circle

สังเกตว่าในโค้ดชุดก่อน เราจะจัดการกับลูกบอลโดยพิจารณาตัวแปรที่เกี่ยวข้องกัน 4 ตัวคือ x, y, vx, และ vy แทบจะตลอดเวลา เราจะ extract ตัวแปรที่เกี่ยวข้องกันนี้ออกมาเป็น object ของคลาส Circle

ให้ย้ายโค้ดจากส่วนที่แล้วมาเขียนใหม่ในไฟล์ cir3.py

คลาสดังกล่าวเขียนบางส่วนได้ดังนี้ อย่าลืมว่าใน python เวลาเขียนเมท็อดจะมีตัวแปร self (ที่เป็นตัวแปรตัวแรก) แทน object ที่ทำงานด้วย และเมท็อด __init__ จะเป็นเมท็อดพิเศษที่ใช้ในการกำหนดค่าเริ่มต้นให้กับ object

class Circle:
    def __init__(self, x, y, vx, vy):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy

    def move(self):
        self.x += self.vx
        self.y += self.vy

        # เพิ่มโค้ดส่วนจัดการการชนขอบจอที่นี่ด้วย

    def draw(self):
        arcade.draw_circle_outline(self.x, self.y,
                                   20, arcade.color.BLACK)

แก้ส่วนสร้าง object

circles = []
n = 10

def initialize():
    for i in range(n):
        circle = Circle(randint(100, SCREEN_WIDTH-100),
                        randint(100, SCREEN_HEIGHT-100),
                        randint(-5,5),
                        randint(-5,5))
        circles.append(circle)

แก้เมท็อด on_draw

def on_draw(delta_time):
    arcade.start_render()

    for c in circles:
        c.move()
        c.draw()

แบบฝึกหัด 3: วงกลมที่มีหลายขนาด

เราจะเพิ่มตัวแปร r ลงใน Circle โดยให้มีค่าเป็น 20 ถ้าไม่มีการระบุตอนสร้าง

class Circle:
    def __init__(self, x, y, vx, vy, r=20):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.r = r
    # ...

ให้แก้โค้ดที่เกี่ยวข้องให้สุ่มมาแล้วมีลูกบอลหลายขนาดเคลื่อนที่ในหน้าจอ

เกมหลบบอล

เราจะเขียนเกมหลบลูกบอล ให้ copy โค้ดจากส่วนก่อน ๆ ลงในไฟล์ชื่อ cir4.py แล้วทำงานต่อที่ไฟล์นี้

เราจะนิยามคลาส Player

class Player:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def draw(self):
        arcade.draw_circle_filled(self.x, self.y,
                                  20, arcade.color.BLUE)

สร้าง object ของคลาส Python ไว้บริเวณใกล้ ๆ กับที่ประกาศ circles (การหาร // คือหารปัดเศษ)

circles = []
player = Player(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
n = 10

เพิ่มโค้ดให้แสดง player ใน on_draw

def on_draw(delta_time):
    arcade.start_render()

    for c in circles:
        c.move()
        c.draw()

    player.draw()

ขยับ player

โดนชน

ชนและเด้ง