[Lập Trình C] Hướng Dẫn Code Game Khủng Long – f-code

1

Tác giả: Nguyễn Phú Hưng

Mở đầu:

Chắc hẳn ai sử dụng trình duyệt Google Chrome cũng từng chơi trò khủng long né chướng ngại vật mỗi khi bị mất mạng rồi nhỉ. Vì mình khá thích game này nên trong kì Train C của câu lạc bộ, mình đã code game này trong project của mình. Nhận được khá nhiều hưởng ứng từ các bạn và anh chị trong câu lạc bộ, hôm nay mình sẽ giới thiệu với các bạn về cách mình code game này trên màn hình console bằng C. Hi vọng qua bài viết này các bạn sẽ có thêm nhiều kiến thức bổ ích cho mình trong trong việc lập trình cũng như thấy được rằng lập trình có thể rất là thú vị nhé.

Yêu cầu kiến thức:

Để đọc hiểu bài viết này, các bạn chỉ cần nắm vững kiến thức về lập trình C cơ bản, bao gồm:

  • Nhập xuất cơ bản qua bàn phím và màn hình
  • Nhập xuất file
  • Lệnh rẽ nhánh, lệnh lặp
  • Con trỏ, xử lí chuỗi và những kiến thức cơ bản khác

Qua bài viết, các bạn sẽ được giới thiệu thêm về các hàm dùng để:

  • Điều khiển vị trí của con trỏ trên màn hình
  • Đổi màu chữ
  • Tạm dừng chương trình trong một khoảng thời gian
  • Bắt phím ngay khi người chơi vừa ấn

Cuối cùng, các bạn chỉ cần một chút nhiệt huyết cùng với tinh thần ham học hỏi là có thể dễ dàng chinh phục trò chơi này rồi.

Nhận xét:

Game này khá đơn giản, mình chia ra những công việc chính phải làm như sau:

  • Hiển thị màn hình khởi đầu khi mở game lên
  • Thực hiện các chức năng chơi game, bao gồm:
    • Hiển thị và điều kiển khủng long
    • Hiển thị mặt đất
    • Hiển thị chướng ngại vật
    • Kiểm tra va chạm giữa khủng long và chướng ngại vật
    • Hiển thị điểm số và điểm cao nhất
    • Hiển thị Game Over khi người chơi thua
  • Đọc và lưu điểm cao nhất
  • Thực hiện chức năng thoát game

Yêu cầu chi tiết:

Từ nhận xét đã có, mình cụ thể hóa các chức năng thành các yêu cầu chi tiết cho việc code:

Hiển thị màn hình khởi đầu:

  • Hiển thị tên game
  • Hiển thị khủng long và mặt đất
  • Hiển thị hướng dẫn thoát game
  • Kiểm tra khi người chơi ấn phím Enter thì bắt đầu game

Thực hiện các chức năng chơi game:

Khủng long:

  • Hiển thị khủng long, thể hiện được chân khủng long đang chạy bằng cách hiển thị xen kẽ từng chân trong mỗi khung hình (frame)
  • Điều khiển cho khủng long nhảy bằng phím Spacebar
  • Khủng long nhảy lên và rớt xuống theo phương thẳng đứng, vị trí của khủng long trên mỗi frame được tính theo công thức ném vật thẳng đứng trong vật lý: h=-1/2*g*t^2+v0*t. Các thông số cụ thể và thời gian khủng long nhảy sẽ được hiệu chỉnh khi code.

Mặt đất:

Mặt đất có 2 phần:

  • Phần mặt bằng trên cùng cho khủng long chạy: Hiển thị một hàng kí tự ————-
  • Phần đất phía dưới được sinh random từ các kí tự . – ` ° , sau mỗi frame:
    • Di chuyển về bên trái để tạo hiệu ứng khủng long chạy
    • Sinh random thêm các kí tự . – ` ° để thể hiện phần đất mới

Chướng ngại vật:

  • Tạo nhiều loại chướng ngại vật khác nhau, ở đây mình tạo chướng ngại vật là 7 loại xương rồng
  • Sinh ngẫu nhiên một chướng ngại vật mới khi vừa bắt đầu game hoặc sau khi chướng ngại vật trước đó ra khỏi màn hình chơi.
  • Sau mỗi frame, chướng ngại vật di chuyển về bên trái cùng với mặt đất để tạo hiệu ứng khủng long chạy

Kiểm tra va chạm:

  • Kiểm tra va chạm khi chướng ngại vật đến chỗ khủng long, nếu có va chạm thì dừng trò chơi và hiển thị Game Over

Hiển thị điểm số:

  • Hiển thị điểm số hiện tại và điểm cao nhất ở góc trên bên phải màn hình

Hiển thị Game Over:

  • Hiển thị dòng chữ Game Over giữa màn hình
  • Kiểm tra khi người chơi ấn phím Enter thì bắt đầu lại game. Sở dĩ lúc này mình chọn phím Enter mà không chọn phím Spacebar là vì người chơi có thể tiếp tục ấn phím Spacebar theo quán tính sau khi Game Over

Đọc và lưu điểm cao nhất:

Đọc và lưu điểm cao nhất khi bắt đầu game và khi kết thúc game ở file friend.com.vn

Thoát game:

Kiểm tra bất cứ khi nào người dùng ấn phím Esc thì thoát game

Code:

Trình tự:

Để cho thuận tiện và hợp logic, mình code game theo trình tự sau:

  • Code một số function hỗ trợ cho việc hiển thị và chơi game
  • Code phần chơi game:
    • Vẽ mặt đất: Mình vẽ mặt đất đầu tiên vì mặt đất đơn giản nhất và mình sẽ dựa vào vị trí của mặt đất xác định vị trí của khủng long và chướng ngại vật
    • Vẽ chướng ngại vật
    • Vẽ khủng long
    • Xử lí hiệu ứng chuyển động của mặt đất
    • Xử lí việc sinh và chuyển động của chướng ngại vật
    • Xử lí việc điều khiển khủng long và cho khủng long nhảy
    • Kiểm tra va chạm
    • Xử lí Game Over
    • Hiển thị điểm số
  • Vẽ màn hình khởi đầu:
    • Vẽ tên game
    • Khởi tạo khủng long, mặt đất
  • Xử lí việc thoát game bằng cách nhấn phím Esc bất cứ lúc nào

Các function hỗ trợ cho việc hiển thị và chơi game:

1

  • void goToXY(int x, int y); đưa con trỏ đến vị trí cột x, dòng y
  • void textColor(int color); thay đổi màu chữ
  • void wait(clock_t time); tạm dừng time/1000 giây để chờ chuyển frame
  • int random(int max); trả về số ngẫu nhiên trong đoạn [1, max]

Code phần chơi game:

Vẽ mặt đất:

Mình lưu mặt đất trong một mảng ký tự hai chiều ground[3][121] vì mặt đất có 3 tầng, mỗi tầng chứa 120 kí tự, cũng là độ rộng của màn hình chơi.

Khởi tạo random seed và mảng ground bằng cách chọn random các kí tự . – ` ° và khoảng trắng:

2

In mặt đất bằng function printGround, ở đây mình cho tham số y0 là vị trí của mặt đất để nếu cần hiệu chỉnh thì chỉ cần sửa lại một dòng khởi tạo y0 ở chương trình chính:

3

Vẽ chướng ngại vật:

Để thể hiện cây xương rồng trên màn hình console, mình sử dụng các ký tự đặc biệt có mã ASCII từ 179 đến 218, các bạn có thể tham khảo các ký tự ASCII khi in ra màn hình theo bảng dưới đây:

ASCII

Sau nhiều lần thử code và chạy, mình đã vẽ được chướng ngại vật đầu tiên bằng đoạn code sau, với x là vị trí hiện tại của chướng ngại vật:

4

2

Cuối cùng là tăng số loại chướng ngại vật lên và viết function printObstacle để in ra một trong các loại chướng ngại vật đó:

5

Vẽ khủng long:

Việc vẽ khủng long thì cũng không có gì nhiều, nhưng vì mình không viết vẽ con này nên sau khi search trên mạng thì mình lấy được hình mẫu và cho vào function printDinosaur, với tham số leg cho biết chân nào của khủng long sẽ được in ra để thể hiện khủng long chạy, và tham số height cho biết độ cao hiện tại của khủng long so với mặt đất (cho trường hợp khi khủng long nhảy

6

Xử lí hiệu ứng chuyển động của mặt đất:

Mình dùng function makeGround để thay đổi mảng ground với mục đích là sinh phần đất mới bên phải và dịch chuyển mặt đất sang trái sau mỗi frame, sau đó chỉ cần gọi lại function printGround mình đã giới thiệu để in đè lên mặt đất ở frame cũ.

Bên dưới là phần code của function makeGround :

7

Xử lí hiệu ứng chuyển động của chướng ngại vật:

Sau khi vẽ được các chướng ngại vật, mình cần xử lí việc dịch chuyển chướng ngại vật sang trái sau mỗi frame. Khác với mặt đất chỉ cần in đè lên phần đất cũ, nếu ta cũng chỉ in đè chướng ngại vật mới lên thì sẽ xuất hiện hiện tượng sau:

3

Để khắc phục hiện tượng này, mình phải viết function deleteObstacle để xóa chướng ngại vật ở frame cũ, sau đó mới gọi function printObstacle để in lại chướng ngại vật ở frame mới.

Cách này khá ổn, tuy nhiên sau khi test thử thì mình thấy có hiện tượng lag, tức là sau khi chướng ngại vật ở frame cũ bị xóa thì chướng ngại vật ở frame mới chưa được in kịp, kết quả là người chơi sẽ thấy chướng ngại vật bị giật hoặc không đầy đủ.

Để khắc phục, mình cải tiến bằng cách không xóa hết chướng ngại vật ở frame cũ mà chỉ xóa những kí tự ở ngoài cùng bên phải của chướng ngại vật, phần còn lại sẽ được in đè lên. Khi đó mình có function deleteObstacle như sau:

8

Xử lí việc sinh chướng ngại vật:

Sau khi đã có các function cần thiết, mình xử lí việc sinh chướng ngại vật trong chương trình chính như sau:

9

Trong đó có các biến:

int obstacle: Có giá trị 0 khi trên màn hình không có chướng ngại vật, ngược lại có giá trị 1.

int tmp: Trong mỗi frame, nếu trên màn hình không có chướng ngại vật thì tmp sẽ được gán giá trị random từ 1 đến 100. Nếu tmp nằm trong khoảng từ 1 đến 7 thì chướng ngại vật có số hiệu tmp sẽ xuất hiện trong frame đó, ngược lại thì chướng ngại vật chưa xuất hiện.

int obstaclePos: Lưu vị trí hiện tại của chướng ngại vật. Ban đầu obstaclePos được gán giá trị bằng 108 khi chướng ngại vật vừa xuất hiện, sau đó giảm 1 đơn vị sau mỗi frame. Khi obstaclePos bằng 1, tức khi chướng ngại vật tiến đến sát bên trái màn hình, chướng ngại vật bị xóa và obstacle được gán bằng 0.

Xử lí việc điều khiển khủng long:

Mình dùng đoạn code sau trong chương trình chính để bắt việc người dùng ấn phím Spacebar cho khủng long nhảy:

10

Trong đó:

Hàm kbhit() dùng để kiểm tra xem người chơi có ấn phím hay không.

Hàm getch() dùng để lấy ký tự người chơi vừa ấn.

Phần xử lí việc ấn phím esc mình sẽ trình bày sau.

Biến int jump có giá trị là 0 khi khủng long đang chạy, lớn hơn 0 khi khủng long đang nhảy.

Sau khi bắt được phím do người chơi ấn, mình xác định độ cao của khủng long ở mỗi frame trong lúc đang nhảy bằng đoạn code sau:

11

Biến jump lúc này được sử dụng để đếm số frame mà khủng long đã nhảy. Ở đây mình thiết kế cho khủng long nhảy trong 50 frames.

Biến height lưu độ cao của khủng long được tính theo công thức vật lý như mình đã trình bày, với các thông số được mình hiệu chỉnh khi code.

Xử lí hiệu ứng nhảy của khủng long:

Cũng giống như hiệu ứng chuyển động của chướng ngại vật, mình xây dựng function deleteDinosaur để xóa một phần của khủng long sau mỗi frame:12

Kiểm tra va chạm:

Để kiểm tra va chạm, mình sử dụng hàm checkCollision, hàm này có trả về 1 nếu có va chạm, ngược lại trả về 0.

13

Mình dùng mảng 2 chiều int xy[100][21]: Nếu xy[x0, y0] bằng 1 tức là tại cột x0, dòng y0 có đồ họa của khủng long, ngược lại xy[x0, y0] bằng 0. Chú ý rằng mình chỉ quan tâm đến các vị trí mà khủng long có thể va chạm với chướng ngại vật, còn những chỗ như lưng, thân hay trên đầu khủng long thì xy[x0, y0] vẫn có giá trị mặc định là 0. Khi đó, tùy vào chướng ngại vật cụ thể mà mình có thể xác định có va chạm xảy ra hay không.

Xử lí Game Over:

Trong chương trình chính:

14

4

Khi có va chạm xảy ra, mình xử lí 5 việc:

  • Thay đổi mắt của khủng long thành dấu x
  • In ra màn hình dòng chữ Game Over
  • Kiểm tra xem điểm hiện tại có lớn hơn điểm cao nhất không, nếu lớn hơn thì gán điểm cao nhất bằng điểm hiện tại
  • Lưu điểm cao nhất vào file friend.com.vn
  • Khởi tạo lại game và chờ người chơi ấn phím Enter để chơi tiếp

Hiển thị điểm số:

Mình hiển thị điểm số hiện tại và điểm cao nhất ở góc trên bên phải màn hình. Vì điểm cao nhất là không đổi, chỉ có điểm hiện tại thay đổi nên mình chỉ in ra màn hình điểm cao nhất một lần mỗi khi bắt đầu game, còn điểm hiện tại sẽ được in đè lên điểm cũ với giá trị tăng một đơn vị sau mỗi frame.

Vẽ màn hình khởi đầu:

Mình dùng hàm createInterfaceDinosaur để in ra màn hình tên game và câu thông báo “Press Esc to exit”, sau đó dùng hàm printGround và printDinosaur để in ra mặt đất và khủng long.

15

Trong đó hàm createInterfaceDinosaur được viết như sau:

16

Xử lí việc thoát game bằng cách ấn phím Esc:

Để có thể thoát game bất kì lúc nào, mình bắt phím Esc ở ba chỗ:

  • Bắt đầu game
  • Trong game
  • Kết thúc game

Tuy nhiên, do cấu trúc mình lập trình là khi kết thúc game thì chương trình sẽ quay trở lại lúc bắt đầu game và chờ người chơi ấn phím (goto gameStart;) nên mình chỉ cần bắt phím Esc ở hai chỗ là khi bắt đầu và trong game:

17

18

Chương trình chính:

Khi code, mình viết chương trình chính song song với các hàm để có thể vừa viết vừa test. Vì lí do đó mà trong bài viết này có lẽ các bạn có thể khó theo dõi phần chương trình chính. Để làm rõ phần này, các bạn có thể theo dõi phần mã giả bên dưới của chương trình chính:

19

Cuối cùng là chương trình chính:

20

Sản phẩm hoàn thiện:

My-Movie

Đến đây mình xin được kết thúc bài viết. Hy vọng bài viết này sẽ đem lại cho bạn những kiến thức cơ bản nhất về lập trình game nói chung và lập trình game với C nói riêng, cũng như thấy được sự thú vị của việc lập trình thông qua các trò chơi đơn giản.

Nếu có khó khăn hay thắc mắc gì các bạn vui lòng để lại comment bên dưới nhé.

Chúc các bạn thành công!

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *