ผลต่างระหว่างรุ่นของ "Prg2/arcade1"
Jittat (คุย | มีส่วนร่วม) |
Jittat (คุย | มีส่วนร่วม) |
||
แถว 40: | แถว 40: | ||
=== Installing pip/pip3 === | === Installing pip/pip3 === | ||
− | pip | + | '''pip''' is a library package manager for Python. In a system with both Python 3 and Python 2, we would call <tt>pip3</tt> to install libraries into Python 3 packages. |
− | + | Try to call | |
pip | pip | ||
− | + | or | |
pip3 | pip3 | ||
− | + | If it runs, you can skip the pip installation part. | |
− | ==== 1. | + | ==== 1. Installing pip on Windows ==== |
pip จะมาพร้อมกับการติดตั้ง python ให้เลือก option ดังกล่าวด้วย ถ้าเปิด command แล้วเรียก pip ขึ้นก็แสดงว่าได้ติดตั้งเรียบร้อย ถ้าไม่มี ให้ลองติดตั้ง python3 อีกครั้ง และให้เลือก pip ในหน้า Optional Features ด้วย | pip จะมาพร้อมกับการติดตั้ง python ให้เลือก option ดังกล่าวด้วย ถ้าเปิด command แล้วเรียก pip ขึ้นก็แสดงว่าได้ติดตั้งเรียบร้อย ถ้าไม่มี ให้ลองติดตั้ง python3 อีกครั้ง และให้เลือก pip ในหน้า Optional Features ด้วย | ||
แถว 58: | แถว 58: | ||
* ดูรายละเอียดการติดตั้งที่นี่ [https://pythonhosted.org/arcade/installation_windows.html arcade installation บน windows] | * ดูรายละเอียดการติดตั้งที่นี่ [https://pythonhosted.org/arcade/installation_windows.html arcade installation บน windows] | ||
− | ==== 2. | + | ==== 2. Installing pip3 on Linux ==== |
ให้ติดตั้ง pip3 และโปรแกรมประกอบอื่น ๆ โดยสั่ง | ให้ติดตั้ง pip3 และโปรแกรมประกอบอื่น ๆ โดยสั่ง | ||
แถว 76: | แถว 76: | ||
=== Use pip to install arcade === | === Use pip to install arcade === | ||
− | ถ้าในการติดตั้ง python เราได้ลง pip มาแล้ว เราจะสามารถติดตั้ง arcade ผ่านทาง pip ได้โดยสั่ง | + | For Ubuntu, ถ้าในการติดตั้ง python เราได้ลง pip มาแล้ว เราจะสามารถติดตั้ง arcade ผ่านทาง pip ได้โดยสั่ง |
sudo pip3 install arcade | sudo pip3 install arcade | ||
− | + | For Mac, use | |
+ | |||
+ | pip3 install PyObjC arcade | ||
+ | |||
+ | For Windows, use | ||
pip install arcade | pip install arcade | ||
แถว 91: | แถว 95: | ||
เราติดตั้ง arcade ลงใน library ของระบบเลย ซึ่งทำแบบนี้บ่อย ๆ อาจจะทำให้ library เละและตีกันได้ Python มีระบบสำหรับติดตั้ง library แยกกัน เรียกว่า virtualenv | เราติดตั้ง arcade ลงใน library ของระบบเลย ซึ่งทำแบบนี้บ่อย ๆ อาจจะทำให้ library เละและตีกันได้ Python มีระบบสำหรับติดตั้ง library แยกกัน เรียกว่า virtualenv | ||
− | : '' | + | : ''Sorry NOT READY: to do - how to install with virtualenv'' |
=== Testing your installation === | === Testing your installation === | ||
แถว 146: | แถว 150: | ||
Quick notes: | Quick notes: | ||
− | * '''randint''' -- | + | * '''randint''' -- Random integers between to specified numbers. You need to <tt>import random</tt>. |
− | * '''global''' -- | + | * '''global''' -- In Python, a function can only read from global variables, by default. If you want to change the variables, you have to declare it explicitly. In function <tt>on_draw</tt> we would like to change the circle size, so we have to declare that. It is '''not''' a good practice to use global variables. |
− | * '''zip''' -- | + | * '''zip''' -- <tt>zip</tt> combines to lists, e.g., <tt>zip([1,2,3],['a','b','c'])</tt> would return <tt>[(1, 'a'), (2, 'b'), (3, 'c')]</tt>. We usually use <tt>zip</tt> when we iterate through two lists in a for loop. |
− | * | + | * Functions from <tt>arcade</tt> |
− | ** arcade.start_render | + | ** arcade.start_render -- Call this before you want to draw a screen. It will clear the screen. (Some experiment below.) |
− | ** arcade.draw_circle_outline | + | ** arcade.draw_circle_outline -- Draw a circle |
** arcade.open_window | ** arcade.open_window | ||
** arcade.set_background_color | ** arcade.set_background_color |
รุ่นแก้ไขเมื่อ 18:21, 17 มกราคม 2562
- This page is a part of Programming 2 (translated from Oop lab/oop in python)
We will start learning how to use the Arcade library and also get a quick review on OOP concepts.
เนื้อหา
Installing Arcade
We will use the arcade game library which requires at least Python 3.6.
Python 3.6
Let's make sure that you have a proper version of Python. Let's call python from the command line (or terminal)
python --version
If its version is at least 3.6, you are good to go and you can skill to the next step (pip installation). Otherwise, you have to get Python 3.6.
Installing Python 3.6 on Windows
You can follow this instruction. Don't forget to click "Add Python 3.6 to PATH".
Installing Python 3.6 on Ubuntu (version 16.10 onward)
Call
sudo apt-get update sudo apt-get install python3.6
You can call python3.6 to start python 3.6.
Installing Python 3.6 on older Ubuntu
sudo add-apt-repository ppa:jonathonf/python-3.6 sudo apt-get update sudo apt-get install python3.6
Installing Python 3.6 on Mac
Follow this instruction.
Installing pip/pip3
pip is a library package manager for Python. In a system with both Python 3 and Python 2, we would call pip3 to install libraries into Python 3 packages.
Try to call
pip
or
pip3
If it runs, you can skip the pip installation part.
1. Installing pip on Windows
pip จะมาพร้อมกับการติดตั้ง python ให้เลือก option ดังกล่าวด้วย ถ้าเปิด command แล้วเรียก pip ขึ้นก็แสดงว่าได้ติดตั้งเรียบร้อย ถ้าไม่มี ให้ลองติดตั้ง python3 อีกครั้ง และให้เลือก pip ในหน้า Optional Features ด้วย
- ดูรายละเอียดการติดตั้งที่นี่ arcade installation บน windows
2. Installing pip3 on 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)
Use pip to install arcade
For Ubuntu, ถ้าในการติดตั้ง python เราได้ลง pip มาแล้ว เราจะสามารถติดตั้ง arcade ผ่านทาง pip ได้โดยสั่ง
sudo pip3 install arcade
For Mac, use
pip3 install PyObjC arcade
For Windows, use
pip install arcade
หมายเหตุ: ถ้าติดตั้งด้วย pip แล้ว pip เอาไปลงกับ Python 3.5 ให้ ให้สั่งแบบนี้แทน (ขอบคุณศิรกร):
python3.6 -m pip install arcade
virtualenv
เราติดตั้ง arcade ลงใน library ของระบบเลย ซึ่งทำแบบนี้บ่อย ๆ อาจจะทำให้ library เละและตีกันได้ Python มีระบบสำหรับติดตั้ง library แยกกัน เรียกว่า virtualenv
- Sorry NOT READY: to do - how to install with virtualenv
Testing your installation
Copy the following code into cirtest.py and try to run it.
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()
Quick notes:
- randint -- Random integers between to specified numbers. You need to import random.
- global -- In Python, a function can only read from global variables, by default. If you want to change the variables, you have to declare it explicitly. In function on_draw we would like to change the circle size, so we have to declare that. It is not a good practice to use global variables.
- zip -- zip combines to lists, e.g., zip([1,2,3],['a','b','c']) would return [(1, 'a'), (2, 'b'), (3, 'c')]. We usually use zip when we iterate through two lists in a for loop.
- Functions from arcade
- arcade.start_render -- Call this before you want to draw a screen. It will clear the screen. (Some experiment below.)
- arcade.draw_circle_outline -- Draw a circle
- arcade.open_window
- arcade.set_background_color
- arcade.schedule
- arcade.run
A moving circle
วาดวงกลม
เราจะเริ่มเขียนจากโปรแกรมที่ทำอะไรไม่ค่อยได้ ไปหาโปรแกรมที่ซับซ้อนขึ้น โปรแกรมด้านล่างวาดรูปวงกลมที่กลางหน้าจอ เขียนและเซฟไว้ในไฟล์ชื่อ 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)
Many circles
เราจะแก้โปรแกรมให้มีลูกบอลวิ่งไปมาหลาย ๆ ลูก ให้เซฟโปรแกรมที่เขียนในไฟล์ใหม่ชื่อ 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 ให้ตรวจสอบและปรับทิศทางของลูกบอลให้สะท้อนกลับมาด้วย
The Circle class
สังเกตว่าในโค้ดชุดก่อน เราจะจัดการกับลูกบอลโดยพิจารณาตัวแปรที่เกี่ยวข้องกัน 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
# ...
ให้แก้โค้ดที่เกี่ยวข้องให้สุ่มมาแล้วมีลูกบอลหลายขนาดเคลื่อนที่ในหน้าจอ
A simple ball game
เราจะเขียนเกมหลบลูกบอล ให้ copy โค้ดจากส่วนก่อน ๆ ลงในไฟล์ชื่อ cir4.py แล้วทำงานต่อที่ไฟล์นี้
เราจะนิยามคลาส Player โดย player จะเป็นวงกลมรัศมี 10 สีน้ำเงิน
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
arcade.draw_circle_filled(self.x, self.y,
10, 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
ขั้นตอนนี้จะยุ่งยากเล็กน้อย เนื่องจากเราจะใช้การอ่านการกดปุ่มแบบอ่านสถานะ ในการเขียนครั้งถัด ๆ ไปจะสะดวกกว่านี้
เตรียมการอ่าน keys
เราจะใช้ส่วนอ่านการกดปุ่มจากไลบรารี pyglet ดังนั้นต้องไป import ก่อน (ใส่ตรงหัวโปรแกรม)
from pyglet.window import key
เราจะประกาศตัวแปร keys เพื่อใช้อ่านสถานะการกดปุ่ม ให้ใส่ไว้ก่อน on_draw โดยอาจจะประกาศไว้แถว ๆ ที่เราประกาศตัวแปร circles, players, และ n ก็ได้
# ... โค้ดเก่า
circles = []
player = Player(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
n = 10
# ประกาศ keys
keys = key.KeyStateHandler()
ในฟังก์ชัน main ไปบอกกับหน้าต่างให้อัพเดทสถานะการกดปุ่มผ่านทาง keys โดยเพิ่มบรรทัด push_handlers ให้ใส่ไว้ก่อน arcade.schedule
arcade.get_window().push_handlers(keys)
# .. โค้ดเก่า ...
arcade.schedule(on_draw, 1 / 80)
ส่งปุ่มให้ player
เพิ่มเมท็อด control ในคลาส Player ที่ตอนนี้ตรวจการกดปุ่มซ้ายอย่างเดียว
class Player:
# ... ละโค้ดเก่าไว้
def control(self, keys):
if keys[key.LEFT]:
self.x -= 5
จากนั้นเรียก player.control จาก on_draw
def on_draw(delta_time):
arcade.start_render()
for c in circles:
c.move()
c.draw()
player.control(keys)
player.draw()
แบบฝึกหัด 4: เพิ่มการอ่านปุ่มขึ้นลงขวาให้ครบ
แก้เมท็อด Player.control ให้ตรวจสอบการเคลื่อนที่ให้ครบทุกทิศทาง
แบบฝึกหัด 5: โดนชน
เขียนเมท็อด is_hit ที่ตรวจสอบว่า player ชนกับ circle หรือไม่
class Player:
# ... โค้ดอื่น ๆ ไม่แสดง
def is_hit(self, circle):
# ... ตรวจสอบว่า self ชนกับ circle หรือเปล่า
# ทำโดยพิจารณาตำแหน่ง self.x, self.y กับ circle.x และ circle.y กับรัศมี circle.r
# อย่าลืมว่า player มีรัศมี 10
#
# method นี้คืนค่า True หรือ False
ใน on_draw ให้เรียก is_hit ถ้าจริงให้จบเลย
def on_draw(delta_time):
arcade.start_render()
for c in circles:
c.move()
c.draw()
if player.is_hit(c):
quit()
player.draw()
player.control(keys)
ถ้าเกมยากไป (เปิดมาตายเลย) ให้ลดจำนวนลูกบอล หรือเปลี่ยนการสุ่มให้ไม่อยู่กลางหน้าจอมากไป
แบบฝึกหัด 6: จบแบบเนียนๆ
แทนที่จะ quit เลย ให้แก้โปรแกรมให้จบได้เนียนกว่าเดิม เช่นลูกบอลหยุด หรือ player หายไป หรืออะไรก็ได้
แบบฝึกหัด 100: ชนและเด้ง
ถ้าทำมาถึงจุดนี้ ลองเขียนให้ลูกบอลเด้งเวลาชนกันเองด้วย