Từ đầu đến đây thì chúng ta đã học rất nhiều những cơ bản của quá trình lập trình. Tuy nhiên các bạn hãy nhớ ra thử xem nào. Đây là quyển sách Lập trình game. Đã để các bạn phải chờ. Tại phần này chúng ta sẽ bắt đầu với việc lập trình game.

Tạo bộ phận chiến đấu RPG

Chúng ta hãy tạo nên trạng thái chiến đấu RPG dựa trên những kiến thức cơ bản đã học. Về hình ảnh và âm thành sẽ được giải thích chi tiết trong các chương sau này. Còn trong chương này chúng ta chỉ dùng chữ hiển thị để tạo nên một game đơn giản.

Mã lệnh chính

Quân địch chính là con rồng hung ác. Tên của nhân vật chính mà con rổng hướng vào là Dango. Đầu tiên chúng ta sẽ xây dựng mã lệnh chính lặp đi lặp lại trong game.

puts "Con rổng đã xuất hiện"
while true
  print "command >"
  cmd = gets.chomp
end```

Về lệnh [puts][print] thì chắc không cần phải giải thích lại, nó chỉ đơn giản là lệnh yêu cầu hiển thị thôi.
```ruby

while true
  ...
end

Đây là lệnh để tạo ra chuỗi kết quả vô hạn đúng không? Bộ phận nằm giữa [while true~end] sẽ được lặp đi lặp lại vĩnh viễn. Trong chuỗi, đầu tiên hiện ra tin nhắn [command >] và dòng tiếp theo thì nó sẽ nhận command từ người chơi.


cmd = gets.chomp

Command mà người chơi nhập vào được kí hiệu là cmd. Phần code cuối dòng nhập vào chúng ta sẽ dùng lệnh [chomp] để cắt và vứt nó đi. Thông qua biến số [cmd] và lệnh sẽ được thực hành. Chương trình dưới đây sẽ xử lý 1 lệnh. #####vsdragon00.rb


puts "con rồng đã xuất hiện!"
while true
  prints "command >"
  cmd = gets.chromp
  if smd == "run"
    puts "Dango đã chạy đi mất"
  exit
  end
end

Trong chương trình này thì lệnh mà chương trình nhận từ người chơi chỉ là "run". Lệnh "run" là lệnh để nhân vật chính chạy trốn khỏi địch. Trong trường hợp cmd là "run" thì tin nhắn điền sau [puts] sẽ hiện ra, gọi ra lệnh [exit] và kết thúc chương trình.

Trong chương trình này thì chúng ta mới chỉ có thể chạy trốn khỏi kẻ thù. Ngoài ra chúng ta sẽ thêm vào những lệnh dưới đây.

##Thêm hành động tấn công

Chúng ta thêm vào lệnh "attack" để tấn công. #####vsdragon001.rb


puts "Con rồng đã xuất hiện"
while true
  prints "command >"
  cmd = gets.chropm
  if cmd == "run"
    puts "Dango đã chạy thoát"
    xit
  elsif
    cmd = "attack"
    puts " Tấn công của Dango"
    puts "Con rồng đã hứng 1 đòn tấn công"
  end
end.

Biến số [cmd] nếu đại diện cho "attack" thì tin nhắn tấn công sẽ hiện ra.

Cho đến nay thì chúng ta mới chỉ cho toàn những tin nhắn dễ thôi nhưng không hiểu tại sao tôi lại có cảm giác chuẩn bị chiến đấu rồi ấy.

##Thay biến số cho tên nhân vật Tên của nhân vật chính là [Dango], ta sẽ đặt biến số cho tên này là "name". Làm như vậy chúng ta có thể viết tên nhân vật một cách dễ dàng và ngắn hơn.

#####vsdragon002.rb


name = "Dango"
puts "Con rồng đã xuất hiện"
while true
  print "command >"
  cmd = gets.chromp
  if cmd == "run" or cmd == "r"
    puts "#{name} đã chạy trốn"
  exit
  elsif cmd == "attack" or cmd == "a"
    puts " #{name} đang phản công"
    puts " Con rồng đã hứng 1 đòn tấn công"
  end
end

Tại version này thì đã có thêm 1 điểm được làm mới. Ta vừa cho thêm chức năng làm ngắn lại lệnh. Đó chính là bộ phận này.


if cmd == "run" or cmd == "r"

Như vậy chúng ta có thể phát lệnh [run] bằng [r]. Bây giờ chỉ cần một chữ [r] chúng ta cũng có thể phát động lệnh [run]. Tượng tự như vậy với lệnh [attack] chúng ta đã chuyển nó thành [a].

##Tính toán chấn thương và định dạng thắng thua Sau khi tấn công, chúng ta sẽ cho thêm chức năng tính toán chấn thương và định dạng thắng thua. Chúng ta chuẩn bị biến số đại diện cho máu của kẻ địch là [hp]. Trước khi xử lý chuỗi thì sẽ chuẩn bị giá trị cho [hp].


hp = 20

Để cho nhân vật chính của chúng ta thắng thì chúng ta nên làm thế nào? Khi [HP] của quân địch xuống 0 thì chính là chiến thắng của nhân vật chính. Chúng ta xử dụng lệnh [if] để thêm chức năng chiến thắng này.


if hp<=0
  puts "Con rồng đã bị đánh bại"
  exit
end

Khi [hp] trở về 0 thì tin nhắn [Con rồng đã bị đánh bại] hiện ra, và lệnh [exit] để thoát khỏi chương trình.

Sau khi nhận định về chiến thắng thì chúng ta phải xử lý tính toán về tấn công. Lệnh "attack" được xử lý như dưới đây.


puts "Con rồng đã hứng một đòn tấn công"

Lệnh trên sẽ được chỉnh sửa như dưới đây.


d = rand (3) + 4
puts "Con rồng đã hứng #{d} đòn tấn công"

Bây giờ đòn tấn công không chỉ là một chữ số nhất định mà nó được tính toán thành những con số ngẫu nhiên. [d] nhận giá trị ngẫu nhiên từ bốn đến sáu và đại diện cho số đòn tấn công mà con rổng phải nhận.

Thêm vào nữa, chúng ta cũng phải trừ số đòn tấn công phải nhận [d] vào số máu [hp] của quân địch nữa. Như vậy chúng ta phải thêm một dòng


hp -= d

Để xác nhận xem biến số [hp] đã bị trừ cẩn thận chưa thì chúng ta phải thêm một dòng nữa. Hoàn thành xong chương trình code chuyên dùng để debug sẽ như dưới đây.


puts "DEBUG: hp #{hp}"  # hiển thị debug

Vậy toàn bộ chương trình cho đến bây giờ sẽ là ####vsdrangon003


name = "Dango"
puts "Con rồng đã xuất hiện!"
hp = 20
while true
  print "command >"
  cmd = gets.chromp
  if cmd == "run" or cmd == "r"
    puts "#{name} đã chạy trốn"
    exit
  elsif cmd == "attack" or cmd == "a"
    puts "#{name} đã tấn công"
    d = rand (3) + 4
    puts "Con rồng đã nhận #{d} tấn công"
    hp -= d
   end
 puts "DEBUG: hp # {hp}"
 if hp <= 0
   puts "Con rồng đã chết!"
   exit
 end
end

Tại đây chúng ta đã xong lệnh tấn công. Khi chúng ta phát lệnh [attack] hay [a] thì máu [HP] của đối phương sẽ bị giảm trực tiếp. Khi máu của kẻ địch bằng 0 thì game sẽ kết thúc. ##Thêm thuật tấn công lửa Tiếp theo chúng ta sẽ thêm [tấn công ma thuật]. Chúng ta hãy cùng thêm thuật tấn công nữa. Chúng ta có phần code dưới đây.


elsif cmd == "fire" or cmd == "f"
  puts "#{name} đã dùng lửa tấn công"
  d = rand(10) + 1
  puts "Con rồng đã nhận #{d} tấn công"
  hp -= d
end

Khác với đòn tấn công [attack] thì nội dung tin nhắn tại đây chỉ có là lượng tấn công mà con rồng nhận phải. Độ trấn thương mà con rổng phải nhận khi hứng đòn này là 1~10. Nếu may mắn thì con rồng có thể bị nhận một lượng tấn công rất lớn.

vsdragon004

Đây là version của game sau khi thêm lệnh này vào


name = "Dango"
puts "con rồng đã xuất hiện"
hp = 20
while true
  print "command >"
  cmd = gets.chomp
  if cmd == "run" or cmd == "r"
    puts "#{name} đã chạy trốn"
    exit
  elsif cmd == "attack" or cmd == "a"
    puts "#{name} đã tấn công"
    d = rand(3) + 4
    puts "con rồng đã hứng #{d} tấn công"
    hp -= d
  elsif cmd == "fire" or cmd == "f"
    puts "#{name} đã dùng ma thuật lửa tấn công"
    d = rand(10) + 1
    puts "con rồng đã nhận #{d} tấn công"
    hp -= d
  end
  puts "DEBUG: hp #{hp}"
  if hp <= 0
    puts "con rồng đã chết"
  exit
  end
end

##Thêm ma thuật Sleep

Chúng ta cùng thêm 1 ma thuật nữa. Nói vậy nhưng ma thuật lần này không đơn giản như lần trước. Lần này chúng ta dùng thuật [Sleep] để bắt quái thú ngủ.

Hiệu quả của thuật [Sleep] là như dưới đây. Khi nhân vật chính hô [Sleep] thì xác suất con rổng ngủ chỉ có 50%. Và khi con rồng ngủ thì sẽ bị ăn nhiều tấn công hơn nên số tấn công nó phải hứng là gấp đôi.

Để thêm ma thuật [Sleep] thì chúng ta dùng mã code dưới đây.


elsif cmd == "sleep" or cmd == "s"
  puts "#{name} đã dùng thuật đánh mê"
  if rand(2) == 0
    puts "làm cho con rồng ngủ được rồi"
    sleeping = true
  else
    puts "chả có việc gì xảy ra cả"
  end
end

Sau khi nhập lệnh [Sleep] thì đầu tiên hiển thị tin nhắn. Sau đó sẽ xảy ra nhận định đòn tấn công của Dango có hiệu quả không. Lệnh [rand(2)] sẽ trả lại 2 kết quả là [0] và [1]. Nếu kết quả trả lại là [0] thì đòn tấn công của Dango là có hiệu quả, ngược lại nếu kết quả trả lại là [1] thì đòn tấn công của Dango là thất bại.

Nếu đòn [Sleep] là hiệu quả thì quân địch sẽ về trạng thái ngủ. Để ghi lại chế độ bật/tắt trạng thái ngủ thì chúng ta tạo nên biến số [sleeping] Nếu rồng rơi vào trạng thái ngủ thì biến số [sleeping] sẽ là [true]. Trạng thái con rồng vẫn còn thức thì biến sẽ [Sleeping] sẽ là [false]. Trước khi xảy ra trận đấu thì con rồng vẫn thức, trước khi vào chuỗi thì chúng ta để trạng thái của [sleeping] sẽ ở định dạng như sau


sleeping = false

Tiếp theo chúng ta cho thêm tính năng nếu con rồng ngủ thì tấn công sẽ được nhân 2. Chúng ta sẽ xử lý vào tính toán [attack] như mã code dưới đây


if sleeping
  d *= 2
  puts "Một đòn tấn công hiệu quả"
  sleeping = false
end

Nếu đòn tấn công là true thì con rồng sẽ bị mất gấp đôi máu. Tin nhắn "Đòn tấn công hiệu quả" hiện ra, rồi chúng ta chuyển [sleeping] thành [false] để giải thoát con rồng ra khỏi trạng thái ngủ. Có nghĩa là con rồng lúc ngủ, khi bị tấn công chắc chắn sẽ thức dậy.

Đây là chương trình đã cho thêm tính năng dùng ma thuật ngủ và xử lý tăng gấp đôi tấn công khi con rồng ngủ. ####vsdragon005


name = "Dango"
Puts "Con rồng đã xuất hiện"
hp = 20
sleeping = false
while true
  print "command >"
  cmd = gets.chomp
  if cmd == "run" or cmd == "r"
    puts "#{name} đã chạy trốn"
    exit
  elsif cmd == "attack" or cmd == "a"
    puts "#{name} đã tấn công"
    d = rand(3) + 4
    if sleeping
      d *= 2
      puts "Một đòn tấn công hiệu quả"
      sleeping = false
    end
    puts "con rồng đã hứng #{d} tấn công"
    hp -= d
  elsif cmd == "fire" or cmd == "f"
    puts "#{name} đã dùng ma thuật lửa tấn công"
    d = rand(10) + 1
    puts "con rồng đã nhận #{d} tấn công"
    hp -= d
  elsif cmd == "sleep" or cmd == "s"
    puts "#{name} đã dùng ma thuật ngủ"
    if rand(2) == 0
      puts "Con rồng đã ngủ"
      sleeping = true
    else
      puts "...Chẳng có gì xảy ra cả"
    end
  end
  puts "DEBUG: hp #{hp}"
  if hp <= 0
    puts "con rồng đã chết"
  exit
  end
end

Sử dụng case

Nếu cứ thêm các lệnh khác thì chúng ta càng có thể viết thêm vào nội dung của game nhưng tôi trước hết chúng ta kết thúc game tại đây.

Chúng ta thử check những bộ phận xử lý lệnh hầu hết đến giờ đều sử dụng [if] hoặc [elsif] để check nội dung [cmd].


cmd = gets.chomp
if cmd == "run" or cmd == "r"
(lược)
elsif cmd == "attack" or cmd == "a"
(lược)
elsif cmd == "fire" or cmd == "f"
(lược)
elsif cmd == "sleep" or cmd == "s"
(lược)
end

Trong chương trình này, chúng ta đều dùng [if] hoặc [elsif] để điều tra biến số [cmd]. Trong những trường hợp có cũng 1 biến số mà xảy ra những trường hợp khác nhau thì sử dụng [câu case] sẽ tiện lợi hơn.

Chúng ta hãy sử dụng câu [case] để viết lại chương trình ở trên.


cmd = gets.chomp
case cmd
when "run", "r"
(lược)
when "attack", "a"
(lược)
when "fire", "f"
(lược)
when "sleep", "s"
(lược)
end

Sau [case] sẽ là đối tượng ta muốn so sánh (object), sau [when] sẽ là những điều kiện của từng trường hợp. Sau [when] chúng ta viết 1 điều kiện cũng được. Từ 2 điều kiện trở lên thì chúng ta cần phân biệt giữa các điều kiện bằng dấu [,].

####vsdragon.rb


name = "Dango"
Puts "Con rồng đã xuất hiện"
hp = 20
sleeping = false
while true
  print "command >"
  cmd = gets.chomp
  case = cmd
  when "run", "r"
    puts "#{name} đã chạy trốn"
    exit
  when "attack","a"
    puts "#{name} đã tấn công"
    d = rand(3) + 4
    if sleeping
      d *= 2
      puts "Một đòn tấn công hiệu quả"
      sleeping = false
    end
    puts "con rồng đã hứng #{d} tấn công"
    hp -= d
  when "fire", "f"
    puts "#{name} đã dùng ma thuật lửa tấn công"
    d = rand(10) + 1
    puts "con rồng đã nhận #{d} tấn công"
    hp -= d
  when "sleep", "s"
    puts "#{name} đã dùng ma thuật ngủ"
    if rand(2) == 0
      puts "Con rồng đã ngủ"
      sleeping = true
    else
      puts "...Chẳng có gì xảy ra cả"
    end
  end
  puts "DEBUG: hp #{hp}"
  if hp <= 0
    puts "con rồng đã chết"
  exit
  end
end

##Thử thách Với những chức năng tôi giới thiệu cho đến bây giờ cũng có thể làm được game đơn giản. Có thể các bạn sẽ nghĩ làm game mà chỉ viết vào text thế này thì có vẻ giản dị nhưng hãy đưa ra ý tưởng để tự mình công phu lên được 1 game nhé.

Game lần này chúng ta làm chỉ là một game thông thường. Có rất nhiều chỗ cần phải sửa chữa. Các bạn hãy đưa ra những ý tưởng và hãy thử thách để sửa những chỗ trong chương trình.

Tôi sẽ đưa ra một vài ý tưởng


  • Biểu thị số lượng lượt cho đến khi đánh bại được con rồng
  • Con rồng đang ngủ, xác suất để nó thức dậy là 50%
  • Con rồng tấn công lại mình