DẠY TRẺ (KIDS) LẬP TRÌNH ĐIỆN TOÁN

(CODING/PROGRAMMING)

NGÔN NGỮ PYTHON

Your kids can do this with Python!

Điều kiện học:

  • Phụ huynh phải biết sử dụng Windows 10 VÀ Internet nếu trẻ chưa biết.
  • Dành cho các máy PC.
  • Dùng Google Chrome
  • Một ít tiếng Anh về điện toán
  • Có bộ gõ tiếng Việt càng tốt (VNI, VPS…)

CHƯƠNG IX

TƯƠNG TÁC (INTERACTION) VỚI TRÒ CHƠI (GAME)

Trong chương trước, ta đã biết cách vẽ hình, làm cho chúng chuyển động, nhưng ta chưa hề biết tương tác với chúng, những đối tượng của chúng ta như trong các trò chơi điện tử (games). Trong các trò chơi này mình phải biết bấm (click), kéo (drag), di chuyển (move), đụng (hit) hay bắn bể (pop) đối tượng trên máy. Trong chương này chúng ta sẽ học cách làm những việc đó.

1) Đầu tiên học BẤM (CLICK) VÀ BẤM & KÉO để tương tác (click & drag)

a) BẤM (CLICK) ĐÂU VẼ ĐẤY

# ClickDots.py
import pygame                           
pygame.init()
screen = pygame.display.set_mode([800,600])
pygame.display.set_caption("Click to draw")   # 1: phụ đề cho cửa sổ
keep_going = True  # Boolean, đầu tiên cho trị là True
RED = (255,0,0)                         # Bộ màu RGB cho màu đỏ (RED)
radius = 15
while keep_going:           # While loop của game. Chỉ ngưng khi keep_going=False. 
    for event in pygame.event.get():    # Máy tìm xem sự kiện (events*) được đưa vào là gì 
        if event.type == pygame.QUIT: # nếu event là bấm nút X đỏ trên hình thì cho:
            keep_going = False  # để ngưng vòng lặp While 
        if event.type == pygame.MOUSEBUTTONDOWN:  # 2
            spot = event.pos   # 3 
            pygame.draw.circle(screen, RED, spot, radius)  # 4
    pygame.display.update()             # Update display
        
pygame.quit()                           # Exit

Chú thích: *sự kiện (event) là hoạt động của người sử dụng mà máy tính có thể  nhận biết như click chuột, nhấn nút trên bàn phím (keypress), bấm giờ…

 #1: hàm pygame.display.set_caption() cho ta tiêu đề của cửa sổ mà đối số là một string. Ở đây tiêu đề là: “Click to draw” (Bấm để vẽ. Tuỳ bạn cho tiêu đề thích hợp).

#2: hàm pygame.MOUSEBUTTONDOWN cho ta biết người sử dụng đã nhấn một nút trên chuột. Khi đó sự kiện ấy sẽ xuất hiện trong danh sách những sự kiện mà chương trình của ta nhận được từ hàm pygame.event.get().

#3: event.pos ghi vị trí toạ độ nơi ta bấm chuột vào một biến tên là spot

#4: hàm pygame.draw.circle(screen, RED, spot, radius): vẽ một vòng tròn trên màn hình sreen, màu RED, tại vị trí spot, có bán kính radius.Những arguments (đối số) trong hàm này đã được gán trị số ở phần trên

Run->Run Module chương trình này rồi bấm chuột để vẽ như hình9-1 dưới đây hay vẽ rồng rắn, voi, chuột…tuỳ ý các bạn.

Hình 9-1

b) BẤM VÀ KÉO LÊ (CLICK & DRAG) con chuột để vẽ

Chúng ta sẽ làm như cầm cây cọ vẽ thật vậy. Ta vừa bấm vừa kéo lê con chuột để vẽ 1 cách trơn tru, trôi chảy.

# DragDots.py
import pygame                           
pygame.init()
screen = pygame.display.set_mode([800,600])
pygame.display.set_caption(“Click and drag to draw”)
keep_going = True # Boolean variable
YELLOW = (255,255,0)                    # bộ màu RGB cho màu vàng YELLOW
radius = 5  # bán kính vòng tròn là 5 pixels
mousedown = False # Boolean variable
while keep_going:                       # Game loop 
    for event in pygame.event.get(): # máy xem sự kiện (events) được đưa vào là gì 
        if event.type == pygame.QUIT: # nếu ta click chữ X đỏ ở góc trên cùng hình vẽ thì:
            keep_going = False # để thôi, dứt vòng lặp While
        if event.type == pygame.MOUSEBUTTONDOWN:  # nếu loại event là nhấn chuột thì:
            mousedown = True
        if event.type == pygame.MOUSEBUTTONUP:  # nếu loại event là buông chuột (release) thì:
            mousedown = False
    if mousedown:                       # Vẽ hoặc cập nhật hình vẽ
        spot = pygame.mouse.get_pos()  # máy lưu toạ độ của vị trí hiện tại của chuột
        pygame.draw.circle(screen, YELLOW, spot, radius) # vẽ vòng tròn trên screen, màu vàng ở tại toạ độ trong spot và bán kính radius
    pygame.display.update()             # cập nhật màn hình
pygame.quit()                           # Exit

Kết quả: Run->Run  Module chương trình sẽ cho ta vẽ 1 hình như ta đang cầm cọ để vẽ: nếu bấm chuột và kéo lê (click & drag) thì vẽ được một đường liên tục, nhưng nếu buông (release) chuột thì đường vẽ bị gían đoạn. Hình 9-2 là một ví dụ. Bạn cho trẻ vẽ quệt cái gì thì quệt. Just for FUN!

Hình 9-2

2) CHO NỔ (EXPLOSION) ĐỐI TƯỢNG (OBJECT)

Ta lập một chương trình cho xuất hiện khắp nơi trên màn hình “thằng quỷ” hay con ma (Sprite) có cái mặt cười, rồi bấm nút hạ chúng (cho nổ).

SPRITE: là ý niệm có từ những ngày đầu của trò chơi video (video games).  Những đối tượng bằng hình vẽ được gọi là sprites (ma quỉ) trên màn hình vì chúng di chuyển, trôi nổi trên nền màn hình, giống như những con ma trong những chuyện thần tiên tưởng tượng.

CLASS (Hạng) và OBJECTS (đối tượng)

CLASS: Trong Pygame là những bản mẫu (template) mà ta có thể sử dụng để dùng lại những đối tượng (objects: những hình vẽ, ảnh…) và mỗi đối tượng này lại có đầy đủ những hàm (functions) cùng những gì riêng biệt (properties) của chúng). Trong chương trình sau này của chúng ta, các bạn sẽ thấy đối tượng là “con ma” (sprite) có cái mặt cười có những điểm riêng biệt cần được chú ý là: vị trí của chúng trên màn hình, kích thước cuả chúng, và tốc độ của chúng khi di chuyển theo hướng x và y. Vì vậy ta lập cho chúng một CLASS riêng với những thuộc tính (attributes) nói trên. Khi cần bạn có thể tự mình lập những class riêng để dùng làm mẫu.

Những class và đối tượng kiểu “ma” (sprites) như thế này có thể được hợp lại thành nhóm, gọi chung là Group Class (nhóm hạng).

a) Ta sẽ lập CLASS cho “con ma” Smiley của chúng ta như sau:

class Smiley(pygame.sprite.Sprite)  # Smiley là tên của class do ta đặt 
	pos = (0,0) # vị trí toạ độ đầu tiên của “con ma”
	xvel = 1  # vận tốc trên hoành độ, theo chiều ngang màn hình, cho là 1 pixel
	yvel = 1  # vận tốc trên tung độ, theo chiều dọc màn hình, cho là 1 pixel. Vel: velocity 
	scale = 100 # mỗi “con ma” to bao nhiêu pixels (size), ở đây cho là 100

Chú thích: pygame.sprite.Sprite được gọi là phần nới rộng (extending) thêm của Pygame. Khi bạn tạo một class cho Sprite bạn cần gọi pygame này:
pygame.sprite.Sprite, trong đó chứa tất cả những gì cần thiết cho đồ hoạ sprite (sprite graphics).   

b) KHỞI ĐỘNG

Sau khi tạo Class cho Smiley, ta cần KHỞI ĐỘNG (INITIALIZE) nó.

Nhờ hàm _init_ của Pygame ta mới có thể khởi động CLASS được. Khởi động ở đây có nghiã ta cho những trị ban đầu (starting values) cho đối tượng sprite của chúng ta. Ta phải cung cấp cho hàm _init_ những thông số (parameters) nó cần. Trong ví dụ này ta phải cung cấp cho nó vị trí mà mình muốn con Smiley xuất hiện (trong pos), cũng như trị của vận tốc khởi đầu trên trục x (xvel) và trục y (yvel), vị trí của hình chữ nhật bao quanh nó (rect.x và rect.y). Vì con Smiley của ta là một class và tất cả những con sprites có mặt cười sẽ là những đối tượng của loại Smiley. Thông số đầu tiên của tất cả những hàm trong class sẽ chính là con ma cười. Ta gọi thông số đó là self vì nó liên kết  hàm _init_ và những hàm khác với dữ liệu riêng của đối tượng. Sau đây là ta tự tạo một hàm để khởi động cái class có tên Smiley:

def _init_(self, pos, xvel, yvel):
	pygame.sprite.Sprite._init_(self)
         self.image = pic  # ảnh từ diã lưu vào ảnh của đối tượng sprite
	self.rect = self.image.get_rect() # cho kích thước của hình chữ nhật bao quanh ảnh 100x100
	self.pos = pos # 
	self.rect.x = pos[0] - self.scale/2  
	self.rect.y = pos[1] - self.scale/2
	self.xvel = xvel
	self.yvel = yvel 

Bạn xem hình 9-3 để hiểu rõ hàm def_init_ trên hơn:

Hình 9-3

c) CẬP NHẬT SPRITES (update sprites)

Cập nhật những sprites có mặt cười (smiley sprites) cho một khung hình (frame)  là thay đổi vị trí của mỗi sprite tuỳ theo vận tốc của nó và kiểm soát xem nó có đụng vào thành của màn hình chưa.  Sau đây là hàm để cập nhật  smiley sprite:

update(self):
self.rect.x += self.xvel
self.rect.y += selv.yvel
if self.rect.x <= 0 or self.rect.x > screen.get¬_width() – self.scale:
	self.xvel = -self.xvel
if self.rect.y <= 0 or self.rect.y > screen.get_height() – self.scale:
	self.yvel = -self.yvel

d) THAY ĐỔI SCALE (SIZE) CỦA HÌNH ẢNH

Đầu tiên phải thay đổi kích thước của biến scale của đối tượng bằng một số ngẫu nhiên trong khoảng 10 và 100. Như thế gọi là transformation (biến dạng) bằng cách dùng hàm pygame.transform.scale() như sau:

Self.scale = random.randrange(10,100)
Self.image = pygame.transform.scale(self.image, (self.scale,self.scale))

Hàm transform.scale() của Pygame sẽ lấy một hình ảnh ( self.image)  và các kích thước mới (là trị ngẫu nhiên của self.scale) làm chiều rộng và chiều cao của hình được biến dạng và nó trả trở lại hình ảnh đã được thay đổi kích thước (lên hay xuống, to hơn hay bé hơn), hình ảnh đang lưu trữ như là self.image mới.

Sau đây là chương trình đầy đủ:

# SmileyExplosion.py
import pygame
import random
BLACK = (0,0,0)
pygame.init()
screen = pygame.display.set_mode([800,600])
pygame.display.set_caption("Pop a Smiley")
mousedown = False
keep_going = True
clock = pygame.time.Clock()
pic = pygame.image.load("CrazySmile.bmp")
colorkey = pic.get_at((0,0)) # 
pic.set_colorkey(colorkey)
sprite_list = pygame.sprite.Group()
class Smiley(pygame.sprite.Sprite):
    pos = (0,0)
    xvel = 1
    yvel = 1
    scale = 100
    def __init__(self, pos, xvel, yvel):
        pygame.sprite.Sprite.__init__(self)
        self.image = pic
        self.scale = random.randrange(10,100)
        self.image = pygame.transform.scale(self.image, (self.scale,self.scale))
        self.rect = self.image.get_rect()
        self.pos = pos
        self.rect.x = pos[0] - self.scale/2
        self.rect.y = pos[1] - self.scale/2
        self.xvel = xvel
        self.yvel = yvel
    def update(self):
        self.rect.x += self.xvel
        self.rect.y += self.yvel
        if self.rect.x <= 0 or self.rect.x > screen.get_width() - self.scale:
            self.xvel = -self.xvel
        if self.rect.y <= 0 or self.rect.y > screen.get_height() - self.scale:
            self.yvel = -self.yvel
while keep_going:
    for event in pygame.event.get(): 
        if event.type == pygame.QUIT: 
            keep_going = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            if pygame.mouse.get_pressed()[0]:    # Regular left mouse button, draw
                mousedown = True
            elif pygame.mouse.get_pressed()[2]:  # Right mouse button, pop
                pos = pygame.mouse.get_pos()
                clicked_smileys = [s for s in sprite_list if s.rect.collidepoint(pos)]
                sprite_list.remove(clicked_smileys)
        if event.type == pygame.MOUSEBUTTONUP:
            mousedown = False
    screen.fill(BLACK)
    sprite_list.update()  # 1
    sprite_list.draw(screen)  # 2
    clock.tick(60)
    pygame.display.update()
    if mousedown:
        speedx = random.randint(-5, 5)
        speedy = random.randint(-5, 5)
        newSmiley = Smiley(pygame.mouse.get_pos(),speedx,speedy)   # 3
        sprite_list.add(newSmiley)  # 4
        
pygame.quit()

Chú thích:

# 1: gọi hàm update() trên danh sách những smiley sprites lưu trữ trong sprite_list; dòng lệnh này sẽ gọi hàm cập nhật để di chuyển mỗi cái mặt cười trên màn hình và kiểm soát coi có chạm các khung màn hình chưa.

# 2: sẽ vẽ mỗi mặt cười trên màn hình vào vị trí thích hợp. Chỉ cần hai dòng mã (codes) là có thể làm chuyển động và vẽ hàng trăm sprites.

# 3: tạo một mặt cười mới có tên newSmiley.

# 4: lấy cái mặt cười mới newSmiley và thêm nó vào Group (nhóm) các sprites có tên sprite_list.

e) BẮN NỔ (pop) SMILEY

Ta sẽ kéo lê nút trái chuột để tạo các mặt cười rồi bấm nút phải chuột để bắn bể chúng như bong bóng.

“May mắn” hơn chương trình trước vì chương trình sau đây mình có hàm có sẵn của Pygame pygame.sprite.collide_rect để kiểm soát xem những hình chữ nhật chứa 2 sprites có va vào nhau không; ta cũng có hàm collide_circle() để kiểm soát xem 2 sprites tròn có chạm nhau không. Và ta cũng có thể kiểm xem một sprite có chạm vào một điểm không (chẳng hạn một pixel mà người sử dụng vừa mới bấm chuột); mình cũng có thể dùng hàm rect.collidepoint() để xét xem sprite có chồng lắp (overlap) hay đụng với điểm ấy trên màn hình không.

Xin xem chương trình sau đây để hiểu rõ hơn về những hàm mới của Pygame cùng những chú thích để tìm hiểu cách bắn bể (pop) một object ra sao.

# SmileyPop.py
import pygame
import random
BLACK = (0,0,0)
pygame.init()
screen = pygame.display.set_mode([800,600])
pygame.display.set_caption("Pop a Smiley")
mousedown = False
keep_going = True
clock = pygame.time.Clock()
pic = pygame.image.load("CrazySmile.bmp")
colorkey = pic.get_at((0,0))
pic.set_colorkey(colorkey)
sprite_list = pygame.sprite.Group()
class Smiley(pygame.sprite.Sprite):
    pos = (0,0)
    xvel = 1
    yvel = 1
    scale = 100
    def __init__(self, pos, xvel, yvel):
        pygame.sprite.Sprite.__init__(self)
        self.image = pic
        self.scale = random.randrange(10,100)
        self.image = pygame.transform.scale(self.image, (self.scale,self.scale))
        self.rect = self.image.get_rect()
        self.pos = pos
        self.rect.x = pos[0] - self.scale/2
        self.rect.y = pos[1] - self.scale/2
        self.xvel = xvel
        self.yvel = yvel
    def update(self):
        self.rect.x += self.xvel
        self.rect.y += self.yvel
        if self.rect.x <= 0 or self.rect.x > screen.get_width() - self.scale:
            self.xvel = -self.xvel
        if self.rect.y <= 0 or self.rect.y > screen.get_height() - self.scale:
            self.yvel = -self.yvel
while keep_going:
    for event in pygame.event.get(): 
        if event.type == pygame.QUIT: 
            keep_going = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            if pygame.mouse.get_pressed()[0]:    # 1 : Regular left mouse button, draw
                mousedown = True
            elif pygame.mouse.get_pressed()[2]:  # 2: Right mouse button, pop
                pos = pygame.mouse.get_pos()  # 3
                clicked_smileys = [s for s in sprite_list if s.rect.collidepoint(pos)]  # 4
                sprite_list.remove(clicked_smileys) # 5
        if event.type == pygame.MOUSEBUTTONUP:
            mousedown = False
    screen.fill(BLACK)
    sprite_list.update()
    sprite_list.draw(screen)
    clock.tick(60)
    pygame.display.update()
    if mousedown:
        speedx = random.randint(-5, 5)
        speedy = random.randint(-5, 5)
        newSmiley = Smiley(pygame.mouse.get_pos(),speedx,speedy)
        sprite_list.add(newSmiley)
        
pygame.quit()

Chú thích:

# 1:check có phải nút trái chuột được bấm không

# 2: check có phải nút phải chuột được bấm không

# 3:Lấy vị trí chuột và lưu trong biến pos

# 4: Đây gọi là lập trình đi tắt (programming shortcut) để tạo ra một danh sách những sprites từ sprite_list mà chúng va chạm hay đè lên điểm mà người sử dụng đã click tại vị trí pos. Nếu một sprite s trong nhóm sprite_list có một hình chữ nhật mà va chạm với điểm ở vị trí pos, phải nhóm nó lại thành một danh sách list [s] và lưu trữ danh sách ấy như là clicked_smileys.  Khả năng tạo ra một danh sách, một tập hơp hay một array từ một cái khác dựa trên một điều kiện if (if condiyion) là một điểm mạnh của Python và nó làm cho việc lập trình của ta ngắn hơn cho ứng dụng này.  

# 5: Hàm sprite_list.remove() sẽ dời bỏ bất cứ bao nhiêu số sprites ra khỏi một danh sách. Trong trường hợp này, nó sẽ dời bỏ tất cả những sprites nào từ sprite_list của ta mà va chạm với điểm mà người sử dụng bấm trên màn hình. Một khi những sprites này bị dời ra khỏi sprite_list, khi mà sprite_list được vẽ trong vòng lặp , thì những sprites bị clicked (bấm) s ẽ không còn trong danh sách nên chúng sẽ không được vẽ, giống như chúng bị biến mất, giống như chúng bị bóp bể như quả bóng hay bọt sà phòng.

Bài tập: Bạn download hình một máy bay j20 của Tàu khựa vào folder chứa các chương trình python py của bạn rồi sửa chương trình này cho bắn rơi chúng.

Sau đây là chương trình mẫu:

# NoF20.py – Tàu bay khựa đến đây tớ bắn cho tan tành
import pygame
import random
BLACK = (0,0,0)
pygame.init()
screen = pygame.display.set_mode([800,600])
pygame.display.set_caption("Pop a Chinese Jet fighter")
mousedown = False
keep_going = True
clock = pygame.time.Clock()
pic = pygame.image.load("j20fighter.jpg")
colorkey = pic.get_at((0,0))  # to make image corners transparent
         pic.set_colorkey(colorkey)  # to make image corners transparent
sprite_list = pygame.sprite.Group()
class Smiley(pygame.sprite.Sprite):
    pos = (0,0)
    xvel = 1
    yvel = 1
    scale = 100
    def __init__(self, pos, xvel, yvel):
        pygame.sprite.Sprite.__init__(self)
        self.image = pic
        self.scale = random.randrange(10,100)
        self.image = pygame.transform.scale(self.image, (self.scale,self.scale))
        self.rect = self.image.get_rect()
        self.pos = pos
        self.rect.x = pos[0] - self.scale/2
        self.rect.y = pos[1] - self.scale/2
        self.xvel = xvel
        self.yvel = yvel
    def update(self):
        self.rect.x += self.xvel
        self.rect.y += self.yvel
        if self.rect.x <= 0 or self.rect.x > screen.get_width() - self.scale:
            self.xvel = -self.xvel
        if self.rect.y <= 0 or self.rect.y > screen.get_height() - self.scale:
            self.yvel = -self.yvel
while keep_going:
    for event in pygame.event.get(): 
        if event.type == pygame.QUIT: 
            keep_going = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            if pygame.mouse.get_pressed()[0]:    # Regular left mouse button, draw
                mousedown = True
            elif pygame.mouse.get_pressed()[2]:  # Right mouse button, pop
                pos = pygame.mouse.get_pos()
                clicked_smileys = [s for s in sprite_list if s.rect.collidepoint(pos)]
                sprite_list.remove(clicked_smileys)
        if event.type == pygame.MOUSEBUTTONUP:
            mousedown = False
    screen.fill(BLACK)
    sprite_list.update()
    sprite_list.draw(screen)
    clock.tick(60)
    pygame.display.update()
    if mousedown:
        speedx = random.randint(-5, 5)
        speedy = random.randint(-5, 5)
        newSmiley = Smiley(pygame.mouse.get_pos(),speedx,speedy)
        sprite_list.add(newSmiley)
        
pygame.quit()

HẾT CHƯƠNG IX

Mới Nhất

Câu Hỏi & Bình Luân

BÌNH LUẬN

Please enter your comment!
Please enter your name here

Ghi danh nhận bài đăng mới nhất

X