Thể hiện đồ họa 2D trên android qua Sudoku game

Tác giả: Tăng Hải Ngọc Sơn – Hunter BMT

Mục đích của bài viết này là giúp các bạn nắm được cách thể hiện đồ họa 2D trên android , đồng thời xử lý IO từ bàn phím cũng như màn hình cảm ứng của người dùng .

Yêu cầu :

  • Đã cài SDK Android và plugin Android cho Netbean
  • Nắm rõ cách tạo GUI với Swing cũng như tạo giao diện web với HTML
  • Các tool sử dụng :
    • Netbean 6.9
    • Android 2.2
  • Đã thực  hiện khung ứng dụng trong phần 1 của bài hướng dẫn làm quen với lập trình android qua ứng dụng game Sudoku (phần 1)

Bài viết có tham khảo từ “Hello Android 3rd Edition 2010” của Pragmatic.

Chào  các bạn , ở bài hướng dẫn trước, chúng ta đã thực hiện sơ bộ một ứng dụng game Sudoku. Tuy nhiên chúng ta vẫn thiếu phần quan trọng nhất của ứng dụng. Đó là “play Game”. Một ứng dụng game chỉ đúng nghĩa khi nó có phần play game. Hôm nay chúng ta sẽ đi vào implement phần Game cho ứng dụng của mình .

Bước 1 : Hoàn thiện hàm startGame

Các bạn mở project cũ mà chúng ta đang thực hiện dở , và thêm vào hàm startGame như sau :

Tiếp theo chúng ta sẽ tạo class Game để xử lý phần “Game” cho ứng dụng .

Các bạn tạo class Game như sau :

Phần màu đỏ mình sẽ giải thích ở phần dưới .

Tiếp theo, để code dễ nhìn hơn, chúng ta sẽ tách biệt phần xử lý đồ họa 2D sang 1 class riêng biệt. Ở đây mình dùng class PuzzleView.

Các bạn tạo class PuzzleView như sau :

Chúng ta đã tạo xong View. Việc tiếp theo chúng ta cần làm là vẽ bàn cho game Sudoku .

Bước 2 : Vẽ bàn cho game

Từ bước này trở đi, chúng ta sẽ sử dụng khá nhiều hàm xử lý đồ họa, vì vậy các bạn không nên vội vã để tránh những sai sót không cần thiết

Việc đầu tiên chúng ta cần làm là khai báo những màu sắc chúng ta sẽ dùng trong file colors.xml .

Các bạn thay đổi colors.xml như sau :

Tiếp theo , chúng ta sẽ vẽ những nét đầu tiên của tấm bảng .

Các bạn thêm hàm  onDraw vào PuzzleView.java như sau :

Build và run Project , các bạn sẽ có nền game như sau :

Nếu xẩy ra lỗi , chúng ta lại quên một điểm rất quan trọng . Game là một activity , chúng ta phải khai báo nó trong AndroidManifest.xml .

Các bạn thay đổi AndroidManifest.xml như sau :

Thêm giá trị game_title vào strings.xml

Clean and build , Run project , bạn sẽ có nền game như ở trên.

Tiếp theo, chúng ta sẽ vẽ thêm các đường trên bàn game , để chia thay các ô .

Các bạn thêm vào onDraw như sau :

Clean and Build ,  run project, các bạn sẽ được màn hình như sau

Cũng khá ok . Mình khá thích màu xanh nên lấy màu nên như vậy, các bạn có thể tùy biến trong file colors.xml màu theo ý thích của mình .

Tiếp theo, chúng ta sẽ vẽ các con số , game Sudoku mà không có số thì đồng nghĩa với không thể chơi, đúng không nào .

Các bạn tiếp tục thêm vào hàm onDraw như sau :

Các bạn để NetBean phát sinh code tự động hàm getTitleString(i,j) vì chúng ta sẽ quay lại implement hàm này sau .

Clean and build project ,run

Màn hình trên sẽ là thành quả của các bạn sau một vài bước nữa . Còn hiện tại khi run ,  ứng dụng sẽ báo lỗi vì hàm getTitleString netbean tự động sinh ra sẽ ném exception khi chưa implement .

Như trước khi implement hàm đó, chúng ta sẽ implement dứt điểm hàm onDraw đã .

Các bạn thêm vào hàm onDraw như sau :

Để tô màu ô player đang trỏ tới .(ở đây mình dùng màu đỏ đỏ :D )

Tiếp theo, chúng ta sẽ cho người dùng di chuyển ô trỏ tới

Trong PuzzleView.java , chúng ta thêm hàm onKeyDown như sau

Hàm trên return về true .

Thêm hàm select  :

Clean and build , Run project chúng ta được màn hình như sau :

Nhưng đấy là với Dpad .Còn  nếu người dùng sử dụng trackball  thì sao ?

Chúng ta cũng có thể override onTrackballEvent() nhưng nếu bạn không override hàm này, Android sẽ tự động chuyển từ trackball sang Dpad . Do đó chúng ta không cần thiết phải override onTrackballEvent().

Vậy còn touchscreen ? Android cũng sẽ tự động chuyển đổi cho chúng ta chứ ? Rất tiếc câu trả lời là không . Do đó để sử dụng touchscreen , các bạn phải override onTouchEvent như sau :

Clean and build , run project , lúc này các bạn có thể sử dụng touch screen để di chuyển như bình thường.

Chúng ta sẽ bổ sung thêm các nút bấm vào onKeyDown như sau :

Chúng ta sẽ implement hàm setSelectedTitle như sau :

Vậy là phần View của chúng ta  đã xong , chúng ta tiếp tục bước thứ 3

Bước 3 : Implement các hàm còn thiếu :

ở bước hai chúng ta đã tạm nợ khá nhiều hàm :D Và bây giờ là lúc chúng ta trả nợ .

Đầu tiên sẽ làm hàm setTitleIfValid .

Các bạn thêm hàm setTitleIfValid vào bên trong Game.java như sau ( hoặc implement nếu

đã để netbean tự sinh hàm )

Hàm getUsedTitle :

ở đây chúng ta dùng 1  mảng used để chứa những số đã sử dụng , đảm bảo tính logic của trò

chơi . Tiếp theo , chúng ta cần 1 hàm calculatedUsedTiles() để kiểm tra tính đúng sai của con số

người chơi nhập vào.

Chúng ta thêm hàm calculatedUsedTiles vào trong Game.java như sau

Tiếp theo là hàm calculateUsedTiles(x,y)

Mình sẽ giải thích một chút về đoạn code trên .

Chúng ta sẽ bắt đầu với một mảng c[9] . Sau đó chúng ta sẽ kiểm tra lần lượt từ hàng dọc , ngang, từng cell(3x3) , nếu có title nào được sử dụng ,chúng ta sẽ cho vào mảng c.

Cuối cùng, chúng ta loại bỏ số 0 trong mảng c, chúng ta được mảng c1 chứa các title đã được sử dụng.

Chúng ta đã xong phần logic cho game . Bây giờ chúng ta sẽ xử lý phần độ khó .

Nhưng các bạn để ý ở đầu bài

Ở đây mình hard-code độ khó , các bạn có thể đưa nó vào xml để dễ dàng thay đổi cũng như cập nhật .

Các ban implement hàm getPuzzle như sau :

Hàm fromPuzzleString để chuyển từ chuỗi String sang mảng Title

Cuối cùng là ba hàm getTitle(x,y) setTitle(x,y) và getTitleString(x,y)

ở đây mình chỉ dùng mảng 1 chiều để lưu các title . Việc sử dụng mảng 2 chiều là không cần thiết .

Clean and build , Run project . Chúng ta đã có được game Sudoku

Game của chúng ta về mặt cơ bản đã hoàn thành . Nhưng chúng ta sẽ thêm một chút gia vị, để game hấp dẫn hơn .

Ý tưởng ở đây là , mỗi khi người chơi chọn sai số để điền vào , chúng ta sẽ cho màn hình rung . Chúng ta sẽ thực hiện chúng như sau :

Tại hàm setSelectedTile , chúng ta sẽ thêm Animation shake mỗi khi người chơi chọn sai con số

Chúng ta sẽ khai báo thêm anim.xml trong resource như sau

Tiếp theo là khai báo về mức độ lắc trong cycle_7.xml

Clean and build, run project ,chúng ta sẽ được hiệu ứng animation như sau :

Khá thú vị phải không các bạn.

Tới đây chúng ta đã xong 90% game . Vậy còn 10% còn lại là gì ? Chúng ta chỉ mới xử lý Input là phím cứng , vậy  nếu điện thoại của người chơi không có phím cứng, mà chỉ có phím vật lý thì sao ?? Chúng ta sẽ giải quyết bằng cách hiển thị một bàn phím ảo với các số từ 1->9 dưới dạng 1 dialog và đón nhận input của người chơi từ đó.

Bước 5 : Tạo bàn phím ảo và đón nhận sự kiện trên đó

Đầu tiên, chúng ta sẽ tạo 1 bàn phím ảo bằng xml như sau :

Trong resource/layout , tạo tập tin keypad.xml

Tiếp theo , tạo 1 class Keypad để xử lý sự kiện

Class này khá giống với phần đón nhận input của Game.java , nên mình sẽ không giải thích thêm

Tiếp theo, chúng ta cần modify Game.java một chút

Chúng ta implement hàm showKeypadOrError để hiển thị thông báo nếu không còn nước đi. Ngược lại chúng ta sẽ hiển thị Keypad cho người dùng.

Hàm onTouchEvent của class PuzzleView modify như sau :

Clean and build , run, chúng ta được như sau :

Để tăng thêm thử thách cho người dùng, mình sẽ cho hiển thị hết keypad ảo , thay vì ẩn đi những title đã dùng như sau :

Và đây là kết quả :

Tới đây các bạn đã hoàn thành game Sudoku rồi . Hy vọng hai phần của bài hướng dẫn này , giúp các bạn tiếp cận phần nào tới lập trình trên Android . Cảm ơn các bạn vì đã dành thời gian theo dõi . Trong loạt bài tiếp theo, chúng ta sẽ cùng nhau tinh chỉnh những thứ như setting cho bật tắt nhạc nền, hướng dẫn . Chạy video mở đầu ( có thể là 1 clip hài như game Đế chế I đã từng dùng chẳng hạn ) Và một vài tính năng nâng cao khác cho ứng dụng game Sudoku . Rất mong nhận được sự ủng hộ từ các bạn . Mọi ý kiến đóng góp , câu hỏi , các bạn có thể cmt ở bên dưới hoặc send email trực tiếp cho mình qua địa chỉ : hunterbmt@gmail.com.