5-1 Cuộc chạy đua của những Punya(1)
Từ đầu đến giờ cũng ta đã được giải thích những thứ chỉ liên quan đến lý thuyết, bây giờ chúng ta sẽ chính thức bắt đầu thử sức với game thực sự. Nói như vậy những chúng ta sẽ bắt đầu bằng thứ thông thường nhất. Tại phần này chúng ta sẽ tạo một game để các con punya chạy tên màn hình đua với nhau, ...
Từ đầu đến giờ cũng ta đã được giải thích những thứ chỉ liên quan đến lý thuyết, bây giờ chúng ta sẽ chính thức bắt đầu thử sức với game thực sự. Nói như vậy những chúng ta sẽ bắt đầu bằng thứ thông thường nhất. Tại phần này chúng ta sẽ tạo một game để các con punya chạy tên màn hình đua với nhau, được gọi là "Punya Race".
Nội dung game
Chúng ta hãy nghĩ xem những gì sẽ hiển thị trên màn hình.
- Punya x 6
- Phong cảnh màn sau
Những gì xuất hiện trên màn hình đó chính là 6 con punya và khung cảnh hình ảnh màn sau. Để thao tác trở nên đơn giản hơn thì 6 con punya mọi người không cần phải thao tác mà bọn chúng sẽ tự động di chuyển. Các con punya đồng loạt chạy về phía bên phải màn hình và tất cả các con punya chạy qua được vạch đích thì trò chơi sẽ kết thúc.
Hình 5-1 Punya RaceHình ảnh punya
Chúng ta sẽ sử dụng những hình ảnh dưới đây.
Hình 5-2 Hình ảnh punya và phong cảnh nềnChúng ta tạo một folder để ảnh rồi để tất cả hình ảnh Punya và hình ảnh phông nền vào đó.
Hình 5-3 Tạo một folder để ảnhPunya class
Nhân vật chính của game này là Punya. Các con punya được chế tạo từ Punya class và xuất hiện trong thế giới Game. Punya class có 2 chức năng chính.
- Vẽ ảnh Punya
- Chạy (Di chuyển về phía bên phải)
Đầu tiên chúng ta thử tạo Punya class trước đã nhé.
punya00.rb
require 'mygame/boot' class Punya def initialize @image = TransparentImage.new("images/punya0.png") @image.x = 20 @image.y = 20 @image.w = 64 @image.h = 64 end def update @image.update @image.x += 2 end def render @image.render end end punya = Punya.new main_loop do punya.update punya.render end
Chương trình này chúng ta có thể chia ra thành 2 bộ phận lớn khác nhau. Bộ phận đầu đó chính là định nghĩa Punya class còn bộ phận sau chính là bộ phận main_loop.
Đầu tiên chúng ta cùng xem xét kĩ Punya class. Trong Punya class tồn tại 3 lệnh chính mà chức năng của từng lệnh sẽ được giới thiệu dưới đây.
- initialize .... Những xử lý ban đầu. Xác lập hình ảnh, tọa độ và kích thước hình ảnh
- update ... Xử lý làm mới. Cộng thêm vào tọa độ và làm mới tọa độ hình ảnh
- render ... Xử lý vẽ ảnh. Gọi hình ảnh ra màn hình
Lệnh [initialize] tạo nên object hình ảnh và thêm nó vào biến số @image. Ngay sau đó sẽ xác lập tọa độ ban đầu và kích thước của [@image]. Lệnh [update] là lệnh thực thi [@image.update] và sẽ thêm sử lý hoạt hình sau này. Nếu chúng ta không thực hiện [@image.update] thì biến đổi hoạt hình sẽ không xảy ra. Cùng mới đó,lệnh [update] trong Punya class sẽ xử lý di chuyển cho Punya. Cứ mỗi khi lệnh [update] được gọi thì tọa độ x của hình ảnh lại được thêm 2 đơn vị vào và vị trí của Punya được thay đổi.
Tạo nên object và main_loop
Để cho punya có thể chạy thực sự được thì nó phụ thuộc vào phần đằng sau này.
punya = Punya.new main_loop do punya.update punya.render end
Đến đây cũng không có gì đặc biệt khó cả. Dòng đầu tiên là dòng tạo ra object punya và thay nó vào biến số [punya].
punya = Punya.new
Trong main loop thì có 2 lệnh được gọi ra chính là [update] và lệnh [render] đối với punya.
punya.update punya.renderHình 5-4 Punya đang chạy về hướng bên phải màn hình
Nếu chạy chương trình này thì trên màn hình phía trên bên trái sẽ xuất hiệu một con Punya đang chuyển động về phía bên phải màn hình. Cho đến khi chương trình chưa được dừng lại thì sẽ coi như chúng ta đang gọi ra một chuỗi [update] vô hạn nên con punya này sẽ chạy luôn ra khỏi màn hình.
6 con Punya
Như vậy cho đến đây chúng ta đã tạo nên Punya class cho con Punya của mình và cũng dùng Punya class để làm hiện 1 con punya lên màn hình, cho chạy. Tiếp theo, chúng ta sẽ đồng thời làm 6 con punya chạy trên màn hình.
Vì chúng ta đã tạo lên Punya class nên việc tạo 6 con punya là một việc rất đơn giản. Trước khi tạo ra 6 con punya thì chúng ta cần sửa lại Punya class như sau.
class Punya def initialize(idx) @image = TransparentImage.new("images/punya#{idx}.png") @image.x = 20 @image.y = 20 + idx*70 (lược) end
Argument [idx] của [initialize] chính là số của punya.[idx] nhận giá trị 0~5. Những bức tranh màu khác nhau gồm 6 loại từ 0~5 nên qua [idx] thì file png sẽ được gọi ra và hình ảnh cũng sẽ thay đổi.
- idx = 0 ....images/punya0.png sẽ được đọc
- idx = 1 ....images/punya1.png sẽ được đọc
- idx = 2 ....images/punya2.png sẽ được đọc
Hơn nữa, tọa độ y cũng được thay đổi dựa trên sự thay đổi của [idx]
- idx = 0 .... 20 + 0*70 -> 20
- idx = 1 .... 20 + 1*70 -> 90
- idx = 2 .... 20 + 2*20 -> 160
Tiếp theo, Object được sinh ra từ Punya class và phần loop sẽ được chỉnh sửa lại như sau.
punyas = Array.new(6) {|i| Punya.new{i}} main_loop do punyas.each do |punya| punya.update punya.render end end
Dòng đầu tiên là dòng để sinh ra những Object.
punyas = Array.new(6) {|i|} Punya.new(i)}
Bằng lệnh [Array.new(6)] thì chúng ta đã sinh ra một hàng có kích thước 6, và những object được sinh ra bởi [Punya.new|i|] sẽ là những thành tố trong dãy.|i| nhận giá trị 0~5 nên chúng ta sẽ có dãy với những thành tố [Punya0]~[Punya5].
Hình 5-5 6 con punya xếp thành hàngNếu chạy chương trình này, chúng ta sẽ có 6 con punya cùng một lúc chạy về phía bên phải màn hình.Tuy nhiên những con punya trên sẽ di chuyển với một tốc độ như nhau, như vậy không thú vị nên chúng ta sẽ sửa lệnh update lên Punya class.
def update @image.update @image.x += rand(4) end
Như vậy tọa độ x sẽ được tính toán vào và chúng ta sử dụng số ngẫu nhiên. [rand(4)] sẽ trả lại câu trả lời là giá trị 0~3. Cứ mỗi lần lệnh [update] đối với tọa độ x của Punya thì tọa độ của chũng sẽ bị công 0~3.
Hình 5-6 Quảng đường di chuyển sẽ ngày càng có khoảng cáchChương trình được chạy cho đến nay là punya01.rb
Phán quyết Goal
Tại đây chúng ta sẽ làm cho nếu Punya chạy đến đầu phải cuối cùng thì đó sẽ là goal và punya sẽ dừng lại. Làm thế nào để chúng ta phán định xem Punya đã về đến đích chưa? Chúng ta có thể làm như nếu [@image.x] có giá trị lớn hơn một giá trị nhất định nào đó thì sẽ coi như đã về đến đích. Tại đó, chúng ta thêm lệnh [goal?] vào Punya class.
def goal? @image.x > 560 end
Lệnh [goal?] trả lại kết quả nếu [@image.x] có giá trị lớn hơn [560] sẽ trả lại kết quả [True] còn nếu có giá trị nhỏ hơn [560] thì kết quả trả lại sẽ là [False]. Chúng ta sẽ thêm lệnh [goal?] để sửa lệnh [update].
def update @image.update unless goals? @image.x += rand(4) end end
Nếu [goal?] không đúng, có nghĩa rằng chỉ khi punya vẫn chưa về đích thì [@image.x] vẫn sẽ được cộng thêm giá trị. Nếu [goal?] trả lại giá trị đúng thì giá trị [@image.x] sẽ không được cộng thêm giá trị, và nếu [@image.x] vượt quá giá trị 560 thì sẽ không tiến thêm được nữa.
Chương trình cho đến hiện tại sẽ được viết trong script [punyarace02.rb].
Hiệu ứng hoạt hình
Bây giờ chúng ta sẽ thêm vào hiệu ứng hoạt hình. Bức hình dưới đây sẽ miêu tả hành động của punya.
Hình 5-7Chúng ta xác lập cho hành động [:run] là (0)(1)(2) và xác lập hành động cho hành động khi đến vạch đích [:win] là (3)(4)(5).
Chúng ta chỉnh sửa lệnh [initialize] trong Punya class như sau.
def initialize (lược) @image.h = 64 animations = { :run => [8, [0,1,2,0]], :win => [8, [3,4,5,3]], } @image.add_animation(animation) @image.start_animation(:run) end
Chúng ta thiết lập hành động hoạt hình cho [@image] bằng [add_animation]. Hơn nữa, để hành động đầu tiên của punya là chạy thì chúng ta thực hiện lệnh [@image.start_animation(:run)].
Chúng ta cũng cần thực hiện thêm hành động khi punya chạy về đích thì hiệu ứng hoạt hình :win được khởi động. Vì vậy, chúng ta lại sửa lệnh update một chút
def update @image.update if goal? @image.start_animation(:win) else @image.x += rand(4) end end
Sau khi Punya đến đích thì [@image.start_animation(:win)] sẽ được thực hiện và cho đến khi về đến đích thì tọa độ x sẽ được cộng giá trị liên tục. Script về chương trình cho đến nay là [punyarace03.rb]
Hiển thị hình nền
Tiếp theo chúng ta chỉ cần thêm hình ảnh nền đằng sau thì chương trình sẽ được hoàn thành.
Hình 5-8 Hình nền [bg_race.png]bg = Image.new("image/bg_race.png") punyas = Array.new(6) {|i|Punya.new(i)} main_loop do bg.render punyas.each do |punya| (lược)
Chúng ta tạo Object hình ảnh phông nền rồi thay thế vào biến số [bg]. Tiếp theo trong loop, thực hiện lệnh [bg.render] để hiên thị phông nền đằng sau. Chúng ta cần để lệnh [bg.render] trước khi gọi hình ảnh của punya. Lý do cần làm thế là vì những thao tác trong loop sẽ được thực hành theo một thứ tự nhất định. Nếu chúng ta để hình ảnh punya trước rồi mới đè lên đó là phông nền thì hình ảnh punya sẽ bị đè lên và hình ảnh punya sẽ không nhìn thấy hình ảnh nữa.
Hình 5-9 Hình ảnh đã thêm cả phông nền đằng sauHoàn thành
Cho đến giờ thì chương trình đã hoàn thành. File nguồn chỉ gồm đúng 40 dòng, là một chương trình rất ngắn. Tuy nhiên, kĩ năng biến punya được sinh ra từ punya class và biến chúng trở thành một dãy là một kĩ năng rất quan trọng nên chúng ta nên chuyên nghiệp về kĩ năng này.
Cuối cùng, đây là chương trình hoàn thành.
punyarace.rb
require 'mygame/boot' class Punya def initialize @image = TransparentImage.new("images/punya#{idx}.png") @image.x = 20 @image.y = 20 + idx*70 @image.w = 64 @image.h = 64 animations = { :run => [8, [0,1,2,0]], :win => [8, [3,4,5,3]], } @image.add_animation(animations) @image.start_animation(:run) end def update @image.update if goals? @image.start_animation(:win) else @image.x += rand(4) end end def goal? @image.x > 560 end def render @image.render end end bg = Image.new("images/bg_race.png") punyas = Array.new(6) {|i|Punya.new(i)} main_loop do bg.render punyas.each do |punya| punya.update punya.render end end