Swift_Tetris game - Part 2
Bài đăng này đã không được cập nhật trong 9 năm
Swift_Tetris - Part 2
Tạo chuyển động cho đối tượng
-
Như những version game tetris bạn đã từng chơi trước đây, bạn sẽ chờ đợi từng khối rơi xuống theo mỗi khoảng thời gian nhất định và sẽ rơi với tốc độ từ chậm đến nhanh dần tuỳ theo từng level, tất nhiên khi chúng rơi, bạn không thể dừng chúng lại được. Và game Tetris cũng sẽ tạo chuyển động giống như vậy.
-
Chúng ta sẽ cần 1 class mở rộng của lớp
SKScene
kế thừa functionupdate(currentTime:CFTimeInterbval)
. Functionupdate
được các frame gọi liên tục. Mỗi frame có thể hiểu như 1 khối hình xuất hiện trên màn hình. Để game có chuyển động trơn tru thì ta cần thiết lập giá trị frame rate càng cao càng tốt, cỡ khoảng 60 hình trên giây hoặc cao hơn. -
Khi mắt chúng ta cảm nhận được từng frame đang chuyển động thì ta sẽ thấy game chuyển động một cách chậm chạp. Đây gọi là khái niệm chuyển động gián đoạn. Dưới đây là ví dụ về chuyển động của game theo các giá trị frame rate khác nhau
-
Bây giờ chúng ta cùng viết tiếp code cho
GameScene.swift
:
-
Trước tiên trong
#1
chúng ta định nghĩa 1 constantTickLengthLevelOne
. Constant này sẽ biểu thị tốc độ chậm nhất mà các khối hình rơi xuống. Như trong code, ta đã set cho nó giá trị là600
mili giây - nghĩa là cứ 0.6 giây thì khối hình sẽ rơi xuống 1 hàng. -
Ở
#2
chúng ta khai báo biến.tickLengthMillis
vàlastTick
được khai báo giống như chúng ta đã khai báo biến trong bài trước.tickLengthMillis
biểu thị tốc độ rơi hiện tại củaGameScene
được gán bằng giá trị mặc định là constantTickLengthLevelOne
chúng ta khai báo ở trên còn biếnlastTick
sẽ là tốc độ rơi nhanh nhất của khối hình, được gán bằng 1 đối tượngNSDate
.Tuy nhiên, dòng khai báo
var tick:(() -> ())?
nhìn có vẻ khá phức tạp. Ở đâytick
được coi nhưclosure
trong swift, kiểu của nó là(() -> ())?
, điều đó có nghĩa là nó là closure và không cần truyền parameter và cũng không cần trả ra dữ liệu. Dấu?
có nghĩa là nó có giá trị optional và có thểnil
. -
Trong
#3
, ta thêm một số logic cho những biến đã khai báo ở trên. NếulastTick
bị missing thì game ở trạng thái pause, không có bất cứ chuyển động nào của khối hình vì vậy đơn giản chỉ cần return ở đây. Tuy nhiên nếulastTick
có giá trị, chúng ta thiết lập giá trị thời gian được thực thi của hàmupdate
bằng cách gọi functiontimeIntervalSinceNow
trong đối tượnglastTick
. Trong Swift, để gọi hàm trong đối tượng thì ta dùng dấu chấm.
Ở đây, khi gọi function ta thấy có xuất hiện dấu chấm than
!
. Ký hiệu này được dùng khi đối tượng gọi hàm là có kiểuoptional
. Sau khi gọi functiontimeIntervalSinceNow
, ta nhân kết quả với-1000
để nhận giá trị dương tính theo mili giây.Tiếp theo, ta kiểm tra nếu time được truyền mà vượt quá giá trị của
tickLengthMillis
, cứ mỗi khoảng thời gian trôi qua bằng giá trị củatimePassed
thì ta phải cập nhật lại vị trí của khối hình. Để làm được điều này, trước tiên ta cập nhật giá trịlastTick
bằng thời gian hiện tại, sau đó ta gọi đến closuretick?()
. Ở đây ta dùng dấu?
sau tên biến để check xem nó có giá trị hay không trước tiên, nếu có thì mới gọi nó và không có parameter. Thực ra đây chỉ là cách viết tắt, nếu viết tường minh ra thì nó sẽ tương dương với đoạn code:
if tick != nil {
tick!()
}
- Đoạn
#4
, ta khai báo các phương thức để cho phép các class bên ngoài có thể stop và start chuyển động của khối hình.
Block party
-
Trong lập trình nói chung, chúng ta sử dụng class để mô tả các đối tượng. Trong game của chúng ta, những đối tượng chính là những khối hình và mỗi khối hình bao gồm 4 phần độc lập nhau và hãy mô tả chúng như những đối tượng. Ta sẽ thực hiện bằng cách tạo một class mới
Block
. Tạo fileBlock.swift
như ta đã làm vớiArray2D.swift
. Bây giờ ta khai báo class và thêm một số đoạn mã choBlock.swift
-
Nhìn đoạn code trên ta có thể dễ dàng nhận thấy đó không phải tất cả những gì cần khai báo để mô tả khối hình. Trong phần đầu tiên, ta định nghĩa 1
enumeration
làBlockColor
và game của chúng ta sẽ hộ trợ tổng cộng 6 màu.
-
Trong đoạn code đầu tiên
#1
, ta khai báo rằng game của chúng ta sẽ có tổng cộng 6 màu. -
Trong đoạn
#2
, ta khai báoenumeration
với kiểuInt
nó implement protocolPrintable
Class, Struc và enum implement
Printable
có khả năng tạo ra 1 chuỗi mà ta có thể đọc được trong quá trình debug hoặc in giá trị lên màn hình console. -
Đoạn
#3
ta khai báo đầy đủ danh sách những option của enumerable, biểu thị cho các màu ứng với giá trị 0 cho màu xanh biển cho đến giá trị 5 cho màu vàng. -
Đoạn
#4
ta định nghĩa 1 bộ các thuộc tínhspriteName
gọi làcomputed property
. 1computed property
hoạt động giống như 1 biến thông thường nhưng khi truy cập nó, cả 1 block code được gọi để đưa ra kết quả của nó.spriteName
sẽ trả về đúng tên file ứng với màu sắc, ở đây ta dùngswitch...case
để thực hiện. -
Đoạn
#5
, ta định nghĩa 1computer property
khác :description
. Thuộc tính này là bắt buộc nếu như ta tuân theo giao thứcPrintable
.Không có thuộc tính này thì code của chúng ta sẽ bị fail. Ở đây nó đơn giản chỉ trả về
spriteName
của màu sắc và như vậy là đủ để mô tả đối tượng. -
Cuối cùng, đoạn số 6, ta định nghĩa 1 static function có tên là
random()
. Function naỳ sẽ trả về 1 giá trị ngẫu nhiên được tìm thấy trongBlockColor
. Ta tạo 1BlockColor
sử dụng khởi tạo :răValue:Int
để thiết lập 1 enumeration được gán các giá trị số. Ở đây các giá trị đó là từ 0 -> 5.
-
Trên đây chúng ta đã khai báo những biến và những thuộc tính cần thiết cho màu sắc trong game, trong bài viết sau, ta sẽ thêm một số đoạn mã để xử lý màu sắc trong game.
Tham khảo mã nguồn: https://github.com/ngocthang/swift_tetris
All rights reserved