จากตอนที่แล้วเราได้วาดฉากหลัง การสุ่มวัตถุ การวาดวัตถุ การเลื่อนซ้ายขวา และการหมุนไปแล้ว ในบทความตอนที่ 2 ซึ่งเป็นตอนก่อนตอนสุดท้ายของชุดการทำเกม Tetris โดยเนื้อหาเป็นเรื่องของการสร้างฉากหลังเป็นโครงสร้างข้อมูลตาราง ถ้าวัตถุตกลงมาจนถึงล่างสุดจะแปลงวัตถุนั้นให้เป็นข้อมูลหนึ่งของตาราง ดังภาพที่ 1และปรับปรุงเรื่องวิธีการตกลงมาของวัตถุและการควบคุม/แสดงผลวัตถุใหม่ด้วยการใช้ตัวตั้งเวลา โดยยังไม่ตรวจสอบการชนจากการเลื่อนซ้าย/ขวา การตรวจสอบว่าวัตถุตกลงมาซ้อนกับวัตถุก่อนหน้านี้หรือไม่การหมุน และการตัดแถวซึ่งจะกล่าวถึงในบทความตอนสุดท้ายหรือ Simple Tetris Ep.3
โครงสร้างข้อมูล
ในบทความนี้มีการเพิ่มโครงสร้างข้อมูลสำหรับเก็บแผนที่ของฉากหลังซึ่งใช้สำหรับเก็บสถานะของเกมว่าตำแหน่งใดมีวัตถุค้างอยู่บ้าง โดยสร้างตัวแปรชื่อ field เป็นข้อมูลประเภทลิสต์ และมีการสร้างข้อมูลภายในเป็น 16 แถว แถวละ 10 ชุดข้อมูล พร้อมทั้งกำหนดให้มีค่าเริ่มต้นเป็น 0 ดังนี้
field = []
for i in range(maxRow):
row = []
for j in range(maxCol):
row.append(0)
field.append(row)
นอกจากปรับเรื่องของโครงสร้างข้อมูลแล้ว ในตอนที่ 2 นี้ได่ปรับเปลี่ยนเรื่องของวงรอบการทำงาน คือ แยกการแสดงผลและการรับค่าไปไว้ในตัวตั้งเวลาชื่อ Render และใช้ตัวตั้งเวลาชื่อ Tmr สำหรับเป็นตัวกำหนดตั้งค่าการตกลงมาของวัตถุจากบนลงล่าง
การให้วัตถุเลื่อนลงมา
การให้วัตถุที่สุ่มมานั้นตกลงมาจากด้านบนลงด้านล่างใช้วิธีการสร้างตัวตั้งเวลาให้ทำงานทุก 1 วินาที และปรับเปลี่ยนค่า posY ของตัวแปรระบบ โดยการใช้ตัวตั้งเวลามีโค้ดตัวอย่างดังนี้
from machine import Timer
Tmr = Timer(0)
Tmr.init( period=1000, mode=Timer.PERIODIC, callback=cbFalling)
...
Tmr.deinit()
จากโค้ดข้างต้นได้เรียกใช้ฟังก์เรียกกลับหรือ callback function ชื่อ cbFalling เพื่อให้ทำงานทุก 1000 มิลลิวินาที หรือ 1 วินาที โดยขั้นตอนการทำงานหลักของฟังก์ชันตัวนี้คือ ปรับค่า posY ให้มากขึ้น และวาดวัตถุนั้นในตำแหน่งแถว posY ในตาราง และถ้าเลื่อนลงมาจนถึงแถวสุดท้ายของตารางจะเปลี่ยนข้อมูลในตารางจากที่เป็น 0 ในตอนสร้างค่าให้กลายเป็น 1 หลังจากเปลี่ยนค่าในตารางจะเริ่มต้นการสุ่มวัตถุชิ้นใหม่ที่จะตกลงมา โดยหลักการทำงานของแต่ละส่วนเป็นดังนี้
การเพิ่มค่าและกำหนดค่าแถวสุดท้าย
การเพิ่มค่าแถวทำด้วยการกำหนดให้ตัวแปร posY มีค่าเพิ่มขึ้น 1 ด้วยการเขียนโค้ดดังนี้
posY += 1
ประเด็นสำคัญต่อมาคือ ด้วยวัตถุมีหลายประเภท และแต่ละประเภทมีความสูงแตกต่างกัน นอกจากนี้ในบางประเภทเมื่อทำการหมุนวัตถุนั้นกลับส่งผลต่อความสูงของวัตถุ ดังนั้นจึงต้องหาว่าวัตถุแต่ละชิ้นนั้นสูงเท่าไร และระบุค่าแถวสุดท้ายที่วัตถุนั้นจะตกลงมาได้ โดยคำนวณจาก posY และ maxRow
การหาค่าแถวสุดท้ายที่วัตถุจะอยู่ได้ หรือตัวแปร lastRow ของแต่ละวัตถุมีหลักการดังนี้
- วัตถุแบบที่ 1 พิจารณาจากค่าการหมุนดังนี้
- หมุนแบบที่ 1 ให้ lastRow = 15 เนื่องจากสูง 1 หน่วย
- หมุนแบบที่ 2 ให้ lastRow เป็น 12 เนื่องจากสูง 4 หน่วย
- วัตถุแบบที่ 2 พิจารณาจากค่าการหมุนดังนี้
- หมุนแบบที่ 1 ให้ lastRow เป็น 14 เนื่องจากสูง 2 หน่วย
- หมุนแบบที่ 2 ให้ lastRow เป็น 13 เนื่องจากสูง 3 หน่วย
- หมุนแบบที่ 3 ให้ lastRow เป็น 14 เนื่องจากสูง 2 หน่วย
- หมุนแบบที่ 4 ให้ lastRow เป็น 13 เนื่องจากสูง 3 หน่วย
- วัตถุแบบที่ 3 พิจารณาจากค่าการหมุนดังนี้
- หมุนแบบที่ 1 ให้ lastRow เป็น 14 เนื่องจากสูง 2 หน่วย
- หมุนแบบที่ 2 ให้ lastRow เป็น 13 เนื่องจากสูง 3 หน่วย
- หมุนแบบที่ 3 ให้ lastRow เป็น 14 เนื่องจากสูง 2 หน่วย
- หมุนแบบที่ 4 ให้ lastRow เป็น 13 เนื่องจากสูง 3 หน่วย
- วัตถุแบบที่ 4 พิจารณาจากค่าการหมุนดังนี้
- หมุนแบบที่ 1 ให้ lastRow เป็น 14 เนื่องจากสูง 2 หน่วย
- หมุนแบบที่ 2 ให้ lastRow เป็น 13 เนื่องจากสูง 3 หน่วย
- หมุนแบบที่ 3 ให้ lastRow เป็น 14 เนื่องจากสูง 2 หน่วย
- หมุนแบบที่ 4 ให้ lastRow เป็น 13 เนื่องจากสูง 3 หน่วย
- วัตถุแบบที่ 5 พิจารณาจากค่าการหมุนดังนี้
- หมุนแบบที่ 1 ให้ lastRow เป็น 14 เนื่องจากสูง 2 หน่วย
- หมุนแบบที่ 2 ให้ lastRow เป็น 13 เนื่องจากสูง 3 หน่วย
- วัตถุแบบที่ 6 พิจารณาจากค่าการหมุนดังนี้
- หมุนแบบที่ 1 ให้ lastRow เป็น 14 เนื่องจากสูง 2 หน่วย
- หมุนแบบที่ 2 ให้ lastRow เป็น 13 เนื่องจากสูง 3 หน่วย
- วัตถุแบบที่ 7 มีความสูง 2 หน่วยจึงได้ค่า lastRow เป็น 14
โค้ดโปรแกรมของการหาค่า lastRow เป็นดังนี้
lastRow = 14
if actorNo == 0:
if actorRotate == 0:
lastRow = 15
else:
lastRow = 12
elif actorNo == 1:
if actorRotate == 0:
lastRow = 14
elif actorRotate == 1:
lastRow = 13
elif actorRotate == 2:
lastRow = 14
else:
lastRow = 13
elif actorNo == 2:
if actorRotate == 0:
lastRow = 14
elif actorRotate == 1:
lastRow = 13
elif actorRotate == 2:
lastRow = 14
else:
lastRow = 13
elif actorNo == 3:
if actorRotate == 0:
lastRow = 14
elif actorRotate == 1:
lastRow = 13
elif actorRotate == 2:
lastRow = 14
else:
lastRow = 13
elif actorNo == 4:
if actorRotate == 0:
lastRow = 14
else:
lastRow = 13
elif actorNo == 5:
if actorRotate == 0:
lastRow = 14
else:
lastRow = 13
else:
lastRow = 14
การเปลี่ยนให้ตารางมีค่าเป็น 1
เมื่อวัตถุตกลงมาถึงแถวสุดท้ายที่สามารถตกลงมาได้ เราจะเปลี่ยนให้ค่าของตาราง field ในตำแหน่งที่วัตถุนั้นแสดงอยู่มีค่าเป็น 1 โดยในบทความนี้ยังไม่พิจารณาเรื่องการซ้อนกันของวัตถุก่อนหน้ากับปัจจุบัน โดยโค้ดของการทำงานในส่วนนี้เป็นดังนี้
### อัพเดต field
for i in range(4):
for j in range(4):
if actors[actorNo][actorRotate][i][j] == 1:
field[posY+i-1][posX+j] = 1
table()
การควบคุมการแสดงผล
ในการทำงานเรื่องการควบคุมการแสดงผลนั้นใช้ตัวตั้งเวลาชื่อ Render โดยสั่งงานไว้ดังนี้
Render = Timer(1)
Render.init( period=100, mode=Timer.PERIODIC, callback=cbRender)
...
Render.deinit()
จากโค้ดด้านบนจะพบว่ามีการกำหนดให้ตัวตั้งเวลาเรียก cbRender ทุก 100 มิลลิวินาที หรือ 10 ครั้งต่อวินาที โดยฟังก์ชันเรียกกลับมีส่วนการทำงาน 2 ส่วนหลัก คือ การนำเข้าข้อมูล และการแสดงผล
การรับข้อมูล
การนำเข้าข้อมูลหรือกาารับข้อมูลมีหลักการเหมือนจากบทความในตอนแรก แต่ตัดการดัก swM2 สำหรับการสุ่มวัตถุออกไป และย้ายที่เหลือทั้งมาไว้ในฟังก์ชัน cbRebder ทำให้อัตราการรับข้อมูลจากสวิตช์เป็น 10 ครั้งต่อวินาที และเปลี่ยนการควบคุมการแสดงผลเองจากที่เรียกใช้โค้ดดังนี้
table() # ล้างหน้าจอแสดวาดตารางใหม่
...ประมวลผลต่าง ๆ
draw() # วาดวัตถุ
มาเป็นการใช้การตั้งการแสดงผลผ่านตัวแปร updated โดยกำหนดให้
- เป็น False จะไม่มีการวาด
- เป็น True จะมีการวาด
การวาด
เมื่อตัวแปร updated เป็นจริง จะดำเนินการวาดโดยมีหลักการทำงาน 2 ส่วน คือ
การหาแถวที่ต้องวาด
การคำนวณหาแถวที่ต้องวาดใช้หลักการเลือกวาดแถวก่อนตำแหน่งของวัตถุเพื่อลบภาพเดิมของวัตถุไปจนถึงแถวปัจจุบันบวกกับค่าความสูงของวัตถุ แต่ถ้าการวาดวาดเกินด้านล่างของขอบจอจะกำหนดให้แถวมากสุดคือแถวสุดท้ายของตาราง ดังนั้น โค้ดส่วนของการหาแถวที่ต้องวาดจึงเป็นการกำหนดตามประเภทของวัตถุและลักษณะการหมุนของวัตถุดังโค้ดโปรแกรมต่อไปนี้
if (updated):
startY = 0
endY = posY
if posY > 0:
startY = posY-1
if endY >= maxRow:
endY = maxRow
if actorNo == 0:
endY += 4
elif actorNo == 1:
endY += 3
elif actorNo == 2:
endY += 3
elif actorNo == 3:
endY += 3
elif actorNo == 4:
endY += 3
elif actorNo == 5:
endY += 3
else:
endY += 2
if endY >= maxRow:
endY = maxRow
table(startY,endY)
draw()
updated = False
คำสั่งวาด
คำสั่งวาดนั้นมี 2 ส่วน คือ table() และ draw() โดยคำสั่ง draw() ยังคงทำงานเหมือนกับบทความก่อนหน้านี้ แต่คำสั่ง table() มีการเปลี่ยนแปลง โดยให้ผู้เขียนโปรแกรมสามารถระบุแถวเริ่มต้นที่จะวาด และแถวสุดท้ายที่จะวาดเพื่อลดภาระของการวาดในส่วนที่ไม่จำเป็นออกไป ทำให้การทำงานในภาพรวมนั้นดีขึ้น และโค้ดของ table() เป็นดังนี้
def table(rowStart=0, rowEnd=maxRow):
for i in range(rowStart, rowEnd,1):
for j in range(maxCol):
x = j * 8 + 1
y = i * 8 + 1
w = 6
h = 6
if (field[i][j] == 0):
scr.fill_rect(x,y,w,h, blankColor)
else:
scr.fill_rect(x,y,w,h, filledColor)
ตัวอย่างโปรแกรม
จากโครงสร้างข้อมูลและการกำหนดเกี่ยวกับการรับค่าพร้อมทั้งเงื่อนไขของการรับค่า/แสดงผล เขียนโค้ดได้ดังนี้ โดยกำหนดให้
- swM1 สำหรับออกจากโปรแกรม
- swA สำหรับหมุนวัตถุ
- keL สำหรับเลื่อนตำแหน่งวัตถุไปทางซ้าย
- keR สำหรับเลื่อนตำแหน่งวัตถุไปทางขวา
#################################################################
# tetris Ep2
# JarutEx 2021-11-06
#################################################################
import gc
import os
import sys
import time
import machine as mc
from machine import Pin,SPI, Timer
import math
import st7735 as tft
import vga1_16x16 as font
import random
#################################################################
###### setting ##################################################
#################################################################
gc.enable()
gc.collect()
mc.freq(240000000)
spi = SPI(2, baudrate=26000000,
sck=Pin(14), mosi=Pin(12),
polarity=0, phase=0)
#### Key
keL = Pin(39, Pin.IN, Pin.PULL_UP)
keU = Pin(34, Pin.IN, Pin.PULL_UP)
keD = Pin(35, Pin.IN, Pin.PULL_UP)
keR = Pin(32, Pin.IN, Pin.PULL_UP)
swM1 = Pin(33, Pin.IN, Pin.PULL_UP)
swM2 = Pin(25, Pin.IN, Pin.PULL_UP)
swA = Pin(26, Pin.IN, Pin.PULL_UP)
swB = Pin(27, Pin.IN, Pin.PULL_UP)
spk = Pin(19,Pin.OUT)
spk.on()
time.sleep(0.1)
spk.off()
# dc, rst, cs
scr = tft.ST7735(spi, 128, 160, dc=Pin(15, Pin.OUT), reset=Pin(13,Pin.OUT), cs=Pin(2, Pin.OUT), rotation=3)
scr.initr()
Tmr = Timer(0)
field = []
actors = [
[ # แบบ1
[[1,1,1,1], # rotate=0
[0,0,0,0],
[0,0,0,0],
[0,0,0,0]],
[[1,0,0,0], # rotate=1
[1,0,0,0],
[1,0,0,0],
[1,0,0,0]]
],
[ # แบบ 2
[[1,1,1,0], # rotate=0
[1,0,0,0],
[0,0,0,0],
[0,0,0,0]],
[[1,1,0,0], # rotate=1
[0,1,0,0],
[0,1,0,0],
[0,0,0,0]],
[[0,0,1,0], # rotate=2
[1,1,1,0],
[0,0,0,0],
[0,0,0,0]],
[[1,1,0,0], # rotate=3
[1,0,0,0],
[1,0,0,0],
[0,0,0,0]]
],
[ # แบบ 3
[[1,0,0,0], # rotate=0
[1,1,1,0],
[0,0,0,0],
[0,0,0,0]],
[[1,1,0,0], # rotate=1
[1,0,0,0],
[1,0,0,0],
[0,0,0,0]],
[[1,1,1,0], # rotate=2
[0,0,1,0],
[0,0,0,0],
[0,0,0,0]],
[[0,1,0,0], # rotate=3
[0,1,0,0],
[1,1,0,0],
[0,0,0,0]]
],
[ # แบบ 4
[[0,1,0,0], # rotate=0
[1,1,1,0],
[0,0,0,0],
[0,0,0,0]],
[[1,0,0,0], # rotate=1
[1,1,0,0],
[1,0,0,0],
[0,0,0,0]],
[[1,1,1,0], # rotate=2
[0,1,0,0],
[0,0,0,0],
[0,0,0,0]],
[[0,1,0,0], # rotate=3
[1,1,0,0],
[0,1,0,0],
[0,0,0,0]],
],
[ # แบบ 5
[[0,1,1,0], # rotate=0
[1,1,0,0],
[0,0,0,0],
[0,0,0,0]],
[[1,0,0,0], # rotate=1
[1,1,0,0],
[0,1,0,0],
[0,0,0,0]]
],
[ # แบบ 6
[[1,1,0,0], # rotate=0
[0,1,1,0],
[0,0,0,0],
[0,0,0,0]],
[[0,1,0,0], # rotate=1
[1,1,0,0],
[1,0,0,0],
[0,0,0,0]]
],
[ # แบบ 7
[[1,1,0,0], # rotate=0
[1,1,0,0],
[0,0,0,0],
[0,0,0,0]]
]
]
actorColors = [
tft.color565(232,232,64),
tft.color565(232,64,64),
tft.color565(232,64,232),
tft.color565(64,64,232),
tft.color565(64,232,64),
tft.color565(64,232,232),
tft.color565(232,232,232)
]
actorRotate = 0
actorNo = random.randint(0,len(actors)-1)
maxCol = 10
maxRow = 16
posX = 0
posY = 0
blankColor = tft.color565(48, 48, 48)
filledColor = tft.color565(192,192,192)
updated = False
#################################################################
###### sub modules ##############################################
#################################################################
def splash():
scr.fill(tft.color565(0x00,0x00,0x00))
scr.text(font,"JarutEx", 20, 20, tft.YELLOW, tft.BLACK)
scr.text(font,"JarutEx", 21, 20, tft.YELLOW, tft.BLACK)
scr.text(font,"(C)2021", 40,48, tft.CYAN, tft.BLACK)
time.sleep_ms(2000)
scr.fill(tft.BLACK)
def genTable():
global field
for i in range(maxRow):
row = []
for j in range(maxCol):
row.append(0)
field.append(row)
def table(rowStart=0, rowEnd=maxRow):
for i in range(rowStart, rowEnd,1):
for j in range(maxCol):
x = j * 8 + 1
y = i * 8 + 1
w = 6
h = 6
if (field[i][j] == 0):
scr.fill_rect(x,y,w,h, blankColor)
else:
scr.fill_rect(x,y,w,h, filledColor)
def draw():
actor = actors[actorNo][actorRotate]
for i in range(4):
for j in range(4):
if actor[i][j]:
x = (posX+j) * 8 + 1
y = (posY+i) * 8 + 1
w = 6
h = 6
scr.fill_rect(x,y,w,h,actorColors[actorNo])
def cbFalling(x):
global posY,posX,actorRotate, actorNo, updated
posY += 1
##### กำหนดแถวสุดท้าย
lastRow = 14
if actorNo == 0:
if actorRotate == 0:
lastRow = 15
else:
lastRow = 12
elif actorNo == 1:
if actorRotate == 0:
lastRow = 14
elif actorRotate == 1:
lastRow = 13
elif actorRotate == 2:
lastRow = 14
else:
lastRow = 13
elif actorNo == 2:
if actorRotate == 0:
lastRow = 14
elif actorRotate == 1:
lastRow = 13
elif actorRotate == 2:
lastRow = 14
else:
lastRow = 13
elif actorNo == 3:
if actorRotate == 0:
lastRow = 14
elif actorRotate == 1:
lastRow = 13
elif actorRotate == 2:
lastRow = 14
else:
lastRow = 13
elif actorNo == 4:
if actorRotate == 0:
lastRow = 14
else:
lastRow = 13
elif actorNo == 5:
if actorRotate == 0:
lastRow = 14
else:
lastRow = 13
else:
lastRow = 14
#### หยุดเมื่อถึงแถวสุดท้าย
if (posY > lastRow):
### อัพเดต field
for i in range(4):
for j in range(4):
if actors[actorNo][actorRotate][i][j] == 1:
field[posY+i-1][posX+j] = 1
table()
### สุ่มวัตถุใหม่
actorNo = random.randint(0,len(actors)-1)
posX = 0
posY = 0
actorRotate = 0
updated = True
def cbRender(x):
global posY,posX,actorRotate, actorNo, updated
#######################################################
##### Input
#######################################################
if swA.value() == 0: # rotate
if (actorNo == 0):
actorRotate += 1
if actorRotate > 1:
if (posX > (maxCol-4)): # ถ้้าตำแหน่งเกินขอบให้หมุนกลับไปตำแหน่งที่เหมาะสม
actorRotate = 1
else:
actorRotate = 0
updated = True
else:
updated = True
elif (actorNo == 1):
if actorRotate == 0:
actorRotate = 1
updated = True
elif actorRotate == 1:
if (posX < (maxCol - 2)):
actorRotate = 2
updated = True
else:
actorRotate = 1
elif actorRotate == 2:
actorRotate = 3
updated = True
else:
if (posX < (maxCol - 2)):
actorRotate = 0
updated = True
else:
actorRotate = 3
elif (actorNo == 2):
if actorRotate == 0:
actorRotate = 1
updated = True
elif actorRotate == 1:
if (posX < (maxCol - 2)):
actorRotate = 2
updated = True
else:
actorRotate = 1
elif actorRotate == 2:
actorRotate = 3
updated = True
else:
if (posX < (maxCol - 2)):
actorRotate = 0
updated = True
else:
actorRotate = 3
elif (actorNo == 3):
if actorRotate == 0:
actorRotate = 1
updated = True
elif actorRotate == 1:
if (posX < (maxCol - 2)):
actorRotate = 2
updated = True
else:
actorRotate = 1
elif actorRotate == 2:
actorRotate = 3
updated = True
else:
if (posX < (maxCol - 2)):
actorRotate = 0
updated = True
else:
actorRotate = 3
elif (actorNo == 4):
if actorRotate == 0:
actorRotate = 1
updated = True
else:
if (posX < (maxCol - 2)):
actorRotate = 0
updated = True
else:
actorRotate = 1
elif (actorNo == 5):
if actorRotate == 0:
actorRotate = 1
updated = True
else:
if (posX < (maxCol - 2)):
actorRotate = 0
updated = True
else:
actorRotate = 1
if keL.value() == 0:
if posX > 0:
posX -= 1
updated = True
if keR.value() == 0:
if (actorNo == 0):
if (actorRotate == 0):
maxX = maxCol-4
else:
maxX = maxCol-1
if posX < maxX:
posX += 1
updated = True
elif (actorNo == 1):
if (actorRotate == 0):
maxX = maxCol-3
elif (actorRotate == 1):
maxX = maxCol-2
elif (actorRotate == 2):
maxX = maxCol-3
else:
maxX = maxCol-2
if posX < maxX:
posX += 1
updated = True
elif (actorNo == 2):
if (actorRotate == 0):
maxX = maxCol-3
elif (actorRotate == 1):
maxX = maxCol-2
elif (actorRotate == 2):
maxX = maxCol-3
else:
maxX = maxCol-2
if posX < maxX:
posX += 1
updated = True
elif (actorNo == 3):
if (actorRotate == 0):
maxX = maxCol-3
elif (actorRotate == 1):
maxX = maxCol-2
elif (actorRotate == 2):
maxX = maxCol-3
else:
maxX = maxCol-2
if posX < maxX:
posX += 1
updated = True
elif (actorNo == 4):
if (actorRotate == 0):
maxX = maxCol-3
else:
maxX = maxCol-2
if posX < maxX:
posX += 1
updated = True
elif (actorNo == 5):
if (actorRotate == 0):
maxX = maxCol-3
else:
maxX = maxCol-2
if posX < maxX:
posX += 1
updated = True
elif (actorNo == 6):
if posX < (maxCol-2):
posX += 1
updated = True
#######################################################
##### Input
#######################################################
if (updated):
startY = 0
endY = posY
if posY > 0:
startY = posY-1
if endY >= maxRow:
endY = maxRow
if actorNo == 0:
endY += 4
elif actorNo == 1:
endY += 3
elif actorNo == 2:
endY += 3
elif actorNo == 3:
endY += 3
elif actorNo == 4:
endY += 3
elif actorNo == 5:
endY += 3
else:
endY += 2
if endY >= maxRow:
endY = maxRow
table(startY,endY)
draw()
updated = False
#################################################################
###### main program #############################################
#################################################################
splash()
genTable()
table()
draw()
Render = Timer(1)
Tmr.init( period=1000, mode=Timer.PERIODIC, callback=cbFalling)
Render.init( period=100, mode=Timer.PERIODIC, callback=cbRender)
while swM1.value():
pass
Tmr.deinit()
Render.deinit()
scr.fill(0)
spi.deinit()
สรุป
จากบทความจะพบว่ามีการปรับเปลี่ยนโครงสร้างของโปรแกรมเพื่อรองรับการใช้ข้อมูลตารางฉากหลังเพื่อใช้บันทึกว่าวัตถุที่ตกถึงพื้นนั้นอยู่ในตำแหน่งของตารางนั้นบ้าง และปรับแต่งการทำงานจากการวนรอบและหน่วงเวลาซึ่งผู้เขียนโปรแกรมไม่สามารถควบคุมเวลาให้ใกล้เคียงกับที่ต้องการได้ เนื่องจากไม่ทราบว่าแต่ละบรรทัดนั้นทำงานโดยใช้เวลาในการทำงานเท่าไร ดังนั้น จึงควบคุมการตกลงของวัตถุและการแสดงผล/รับข้อมูลเป็นการใช้ตัวตั้งเวลา และส่วนของการตรวจสอบการชนซึ่งมีผลต่อการวางซ้อนกันของวัตถุที่ตกมาใหม่กับวัตถุก่อนหน้า การขยับวัตถุไปซ้าย/ขวา การหมุนได้หรือหมุนไม่ได้ของวัตถุ และการตัดแถวที่เต็มแถวออกจากตารางจะเป็นส่วนที่กล่าวถึงในบทความตอนหน้าซึ่งเป็นตอนสุดท้ายของการสร้างเกม Tetris แบบง่าย ๆ ของเรา
สุดท้ายนี้หวังว่าตัวอย่างของบทความนี้คงมีประโยชน์และสร้างแนวคิดให้กับผู้ที่สนใจพัฒนาเกมแบบทำเองง่าย ๆ และสามารถทำได้ด้วยตนเอง ขอให้สนุกกับการเขียนโปรแกรมครับ
(C) 2020-2022, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-11-08, 2022-01-17