Cùng với việc tiếp tục áp dụng những nguyên tắc thông thườngkhi thiết kế game trên máy tính, ta cần quan tâm đến một số đặc điểm củagame được thiết kế trên các thiết bị di động: + Không
Trang 1BỘ LAO ĐỘNG THƯƠNG BINH VÀ XÃ HỘI
TRƯỜNG CAO ĐẲNG NGHỀ ISPACE
KHOA: CÔNG NGHỆ THÔNG TIN
Trang 2BỘ LAO ĐỘNG THƯƠNG BINH VÀ XÃ HỘI
TRƯỜNG CAO ĐẲNG NGHỀ ISPACE
KHOA: CÔNG NGHỆ THÔNG TIN NGÀNH: LẬP TRÌNH MOBILE
Trang 3LỜI CẢM ƠN
Đầu tiên, em xin được gửi lời cảm ơn đến tất cả quý thầy cô đã tham giagiảng dạy tại Trường Cao đẳng nghề CNTT iSpace vì đã truyền đạt những kiến thứchữu ích làm cơ sở cho em thực hiện đồ án này
Em cũng xin chân thành cảm ơn đến thầy Lê Hồng Kỳ, người đã tận tìnhhướng dẫn, tạo mọi điều kiện thuận lợi để chúng em hoàn thành tốt đồ án
Sau cùng em xin gửi lời biết ơn sâu sắc đến gia đình vì đã luôn tạo điều kiệncho chúng em trong suốt quá trình học tập cũng như thực hiện đồ án
Hồ Chí Minh, tháng 06 năm 2015
Trân trọng
Trang 4TÓM TẮT ĐỒ ÁN
Traffic Warden là một game với giao diện và cách chơi khá đơn giản, từ trẻ
em đến người trung niên, người lớn tuổi đều thích hợp Traffic Warden mang lạicho người chơi những kiến thức bổ ích và rất cần thiết liên quan đến luật giaothông Biết đâu qua trò chơi mà người chơi sẽ không còn bị cảnh sát giao thông bắtphạt nữa
Đồ án này gồm 05 chương:
Chương 1: Tổng quan về tình hình game và Lập trình Game trên thiết bị di
động, giới thiệu về Game Traffic Warden
Chương 2: Lập trình game với thư viện LibGDX
Chương 3: Thiết kế và xây dựng Game Traffic Warden
Chương 4: Phát triển Game Đa nền tảng
Chương 5: Tối ưu Game cho nền tảng di động
Trang 5
MỤC LỤC
DANH MỤC HÌNH i
DANH MỤC BẢNG iii
MỞ ĐẦU 1
CHƯƠNG 1: GIỚI THIỆU VỀ GAME TRAFFIC WARDEN 2
1.1 GIỚI THIỆU VỀ GAME TRAFFIC WARDEN 2
1.2 CỐT TRUYỆN GAME TRAFFIC WARDEN 2
CHƯƠNG 2: LẬP TRÌNH GAME VỚI LIBGDX 3
2.1 GIỚI THIỆU VỀ LIBGDX 3
2.2 CÀI ĐẶT MÔI TRƯỜNG LÀM VIỆC 4
2.3 TẠO PROJECT LIBGDX 4
2.4 IMPORT PROJECT VÀO ECLIPSE 7
2.4.1 CHẠY THỬ TRÊN ANDROID 8
2.4.2 CHẠY THỬ TRÊN WEB 9
2.4.3 CHẠY THỬ TRÊN DESKTOP 9
CHƯƠNG 3: THIẾT KẾ VÀ XÂY DỰNG GAME TRAFFIC WARDEN 11
3.1 THIẾT KẾ 11
3.1.1 SƠ ĐỒ THIẾT KẾ GAME TRAFFIC WARDEN 11
3.1.2 GIẢI THÍCH VIỆC CHIA THEO PACKAGE 11
3.2 XÂY DỰNG GAME TRAFFIC WARDEN 13
3.2.1 Vẽ Map 13
3.2.2 Class SplashGame 17
3.2.3 Class MenuScreen 19
3.2.4 Class Main Game 23
3.2.5 Class ProjectA 23
3.2.6 Class Xe 24
3.2.7 Class CacXe 24
3.2.8 Class NonPlayCharacter 31
3.2.9 Class NPC 32
3.2.10 Class ImageButton 33
3.2.11 Class Asset 33
Trang 63.2.13 Class MainCamera 37
3.2.14 Class World 37
3.2.15 Class WorldRender 37
3.2.16 Class WorldController 42
3.3 TEST GAME HOÀN CHỈNH 47
3.3.1 TRÊN THIẾT BỊ ANDROID 47
3.3.2 TRÊN DESKTOP 49
CHƯƠNG 4: PHÁT TRIỂN GAME ĐA NỀN TẢNG 51
4.1 TRÊN DESKTOP (Windows / Linux / MacOS) 51
4.1.1 Chạy thử với Eclipse 51
4.1.2 Build từ Project thành tập tin JAR để chạy độc lập 52
4.2 TRÊN ANDROID: 54
4.2.1 Chạy trên máy ảo 54
4.2.2 Build thành tập tin APK chạy độc lập 55
4.3 TRÊN WEB (HTML5) 58
4.3.1 Chạy ở chế độ Debug với Eclipse 58
4.3.2 Chạy trực tiếp trên Server 61
4.4 TRÊN iOS 63
CHƯƠNG 5: TỐI ƯU GAME CHO NỀN TẢNG DI ĐỘNG 64
5.1 TỐI ƯU BỘ NHỚ SỬ DỤNG 64
5.2 TỐI ƯU TỐC ĐỘ XỬ LÝ 65
5.3 TỐI ƯU CHO THIẾT BỊ MÀN HÌNH KHÁC NHAU 66
5.3.1 Tạo nhiều hình có các kích thước khác nhau 66
5.3.2 Kéo giãn hình ảnh cho vừa với độ phân giải 66
KẾT QUẢ ĐẠT ĐƯỢC VÀ HƯỚNG PHÁT TRIỂN 68
KẾT QUẢ ĐẠT ĐƯỢC 68
HƯỚNG PHÁT TRIỂN 68
CÁC QUI ƯỚC TRONG GAME 70
TÀI LIỆU THAM KHẢO 71
BẢNG PHÂN CHIA CÔNG VIỆC TRONG NHÓM 72
Trang 7DANH MỤC HÌNH
Hình 2.1: Tạo project LibGDX 5
Hình 2.2: Chọn Eclipse 6
Hình 2.3: Tạo project thành công 6
Hình 2.4: Cấu trúc thư mục sau khi đã tạo thành công project 7
Hình 2.5: Import project vào eclipse 8
Hình 2.6: Chạy thử ứng dụng LibGDX trên Android 9
Hình 2.7: Chạy thử ứng dụng LibGDX trên Web 9
Hình 2.8: Chạy thử ứng dụng LibGDX trên Desktop 10
Hình 3.1: Sơ đồ thiết kế game Traffic Warden 11
Hình 3.2: Cấu trúc Package và Class 12
Hình 3.3: Tạo Map mới 13
Hình 3.4: Gắn Tileset vào Map 14
Hình 3.5: Map hoàn chỉnh 16
Hình 3.6: So sánh hình bị lỗi và hình bình thường 17
Hình 3.7: Các file cần để chạy TileMapPacker 17
Hình 3.8: Demo splash screen trong game Traffic Warden 19
Hình 3.9: Trước và sau khi touch hoặc click vào button 22
Hình 3.10: Demo menu screen game Traffic Warden 23
Hình: 3.11: So sánh góc quay của 2 hình Chữ nhật và Đa giác 24
Hình 3.12: Sơ đồ tính toán vị trí đặt xe theo tâm 26
Hình 3.13: Tính góc quay cho xe 27
Hình 3.14: Minh họa tạo lỗi xe chạy lấn tuyến 28
Hình 3.15: Khung va chạm chuyển từ Polygon góc 280 sang Rectangle 30
Hình 3.16: Tính khoảng cách xe trong cùng 1 đường 30
Hình 3.17: Tính khoảng cách xe giữa 2 đường khác nhau 31
Hình 3.18: Cách tính hướng quay mặt cho đối tượng NonPlayCharacter 32
Hình 3.19: Ảnh tập hợp hình các xe 33
Hình 3.20: Bố trí hình nhân vật Animation 34
Hình 3.21: Cách tính các vị trí trong đường thẳng bất kì ở góc phần tư thứ nhất .36
Hình 3.22: Tính kích cỡ hình theo độ phân giải 39
Trang 8Hình 3.24: Mô tả tính toán vị trí khung 41
Hình 3.25: Kết quả vẽ khung đã được kiểm tra trên máy ảo 42
Hình 3.26: Cách tính vị trí mới dựa vào deltaX và deltaY 44
Hình 3.27: Sự di chuyển của vị trí minPosition và maxPosition 45
Hình3.28: Test trên Android: Menu 48
Hình 3.29: Test trên Android, độ phân giải màn hình 1280*800pixel 48
Hình 3.30: Test Game Android, bộ nhớ sử dụng 81MB 49
Hình 3.31: Test Game trên Desktop: Màn hình Menu 49
Hình 3.32: Test Game tên Desktop: Màn hình Game 50
Hình 3.33: Test Game trên Desktop, tiêu thụ RAM và CPU 50
Hình 4.1: Project Desktop 51
Hình 4.2: Chạy thử trên môi trường Desktop 52
Hình 4.3: Chọn loại tập tin JAR sẽ tạo 53
Hình 4.4: Cấu hình để chạy tập tin JAR 54
Hình 4.5: Chạy Project Android trên máy ảo Droid4X 55
Hình 4.6: Chọn tạo tâp tin APK 56
Hình 4.7: Tạo mới hoặc chọn keystore đã có 57
Hình 4.8: Chọn đường dẫn lưu tập tin APK 57
Hình 4.9: Plugin hỗ trợ FireFox 58
Hình 4.10: Chọn GWT Complie 58
Hình 4.11: GWT Complie 59
Hình 4.12: Chuẩn bị chạy trên trình duyệt 59
Hình 4.13: Loading của LibGDX vào trình duyệt 60
Hình 4.14: Vào màn hình Menu của Game 61
Hình 4.15: Chép các tập tin cần thiết vào chuẩn bị chạy với Gradlew 62
Hình 4.16: Bắt đầu Build 62
Hình 4.17: Quá trình Build html hoàn tất 63
Trang 9DANH MỤC BẢ Bảng 3.1: Lỗi tham khảo trong Game 25 Bảng: 3.2: Phân chia, lên cấp bậc trong Game 47
Bảng phân chia công việc trong thiết kết và xây dựng Game 73
Y
Trang 10MỞ ĐẦU
Kể từ khi máy tính xuất hiện, game đã trở thành một trong những ứngdụng phổ biến nhất trên thị trường Cùng với sự đa dạng và phong phú về thểloại game có thể lựa chọn, chúng ta cần tập trung vào những yếu tố phù hợpnhất với nền tảng di động (điện thoại di động, máy tính bảng, ), đặc biệt quantâm đến những game có thể phát triển bằng một nhóm ít người Với tiềm năng
to lớn của thị trường di động, không ngạc nhiên khi có rất nhiều nghiên cứu và
ý tưởng được đưa ra nhằm mục đích tạo nên một game di động hấp dẫn nhiềungười chơi Cùng với việc tiếp tục áp dụng những nguyên tắc thông thườngkhi thiết kế game trên máy tính, ta cần quan tâm đến một số đặc điểm củagame được thiết kế trên các thiết bị di động:
+ Không lãng phí thời gian của người chơi
+ Cung cấp sự trợ giúp cần thiết cho người chơi
+ Làm cho mục tiêu của trò chơi dễ hiểu hơn
+ Hiển thị các trạng thái trong game một cách rõ ràng
+ Người dùng di động thường chỉ chơi game trong một thời gian ngắn.+ Người chơi có thể dễ dàng tạm dừng hay tiếp tục game khi cần thiết.+ Người chơi có thể đạt được tiến bộ trong một thời gian ngắn
+ Những hạn chế của thiết bị di động ảnh hưởng đến việc xây dựnggame:
• Kích thước màn hình nhỏ, và đa dạng về kích thước màn hình, độphân giải
• Có rất nhiều cách thức nhập dữ liệu từ người dùng: bàn phím, cảmứng, cảm biến gia tốc, cảm biến độ nghiên,…
• Hạn chế về sức mạnh tính toán và sức mạnh đồ họa
• Hạn chế về pin
Nhằm đáp ứng những đặc điểm trên, nhóm em đã xây dựnggameTraffic Warden là một game quản lý thời gian (Time Management) đơngiản, không buộc người chơi phải mất quá nhiều công sức tìm hiểu cách chơi.Không chỉ thế, người chơi còn có thể góp thêm cho mình những kiến thức cănbản về luật khi tham gia giao thông Việt Nam
Trang 12CHƯƠNG 1: GIỚI THIỆU VỀ GAME TRAFFIC WARDEN
1.1 GIỚI THIỆU VỀ GAME TRAFFIC WARDEN
Theo tìm hiểu của nhóm trên App Store (iPhone & iPad) và CH Play(Android) những game liên quan đến giao thông, phần lớn chỉ là game điềukhiển xe chạy cho đúng làn đường (Traffic Warden – trên App Store vàTraffic Control – trên CH Play), hay game bật đèn tín hiệu cho xe dừng đúnglàn đường để không gây tai nạn (Traffic Control Emergency) chứ chưa cógame nào đi sâu vào khai thác công việc thật sự của Cảnh sát giao thông – bắtnhững xe quy phạm luật giao thông Hầu như các thể loại game về giao thôngrất ít
Chính vì thế, nhóm quyết định phát triển game Traffic Warden – Quản
lý giao thông, với ý tưởng không thể nói là hoàn toàn mới mẻ nhưng lại hoàntoàn mới trong kho ứng dụng game Với cách chơi đơn giản và đồ họa nhẹnhàng, Traffic Warden sẽ mang đến cho người chơi những giây phút giải tríthoải mái
1.2 CỐT TRUYỆN GAME TRAFFIC WARDEN
Lấy ý tưởng từ một cậu bé có người cha bị qua đời vì tai nạn giaothông, từ đó cậu bé quyết tâm học thật giỏi để trở thành Cảnh sát giao thôngvới mong ước giúp tất cả mọi người xung quanh mình tuân thủ đúng luật giaothông, để không còn xảy ra những tai nạn không mong muốn, sẽ không còn aichịu cảnh mất người thân như cậu
Trong Traffic Warden người chơi vào vai người cảnh sát giao thông bắtnhững xe vi phạm luật giao thông
Trang 13CHƯƠNG 2: LẬP TRÌNH GAME VỚI LIBGDX
Do nhu cầu không ngừng của người chơi game cũng như của nhữngnhà phát triển, ngày nay trên thị trường có rất nhiều framework hỗ trợ việc xâydựng và phát triển game trên các thiết bị di động, đặc biệt là trên hệ điều hànhAndroid Tùy theo yêu cầu và đặc điểm của từng game, mà lập trình viên lựachọn cho mình framework thích hợp để lập trình Một số framework cơ bản cóthể kể đến như: LibGDX, AndEngine, Unity,…
Qua đồ án này, chúng em cũng muốn giới thiệu một Framework hỗ trợlập trình đa nền tảng là LibGDX
1
2.1 GIỚI THIỆU VỀ LIBGDX
LibGDX là một framework phát triển ứng dụng game, được viết bằngngôn ngữ lập trình Java, một số các thành phần được viết bằng C và C++ để
có hiệu năng tốt hơn Nó cho phép phát triển các ứng dụng desktop và mobiletrên cùng một codebase LibGDX chạy được đa nền tảng, hỗ trợ Windows,Linux, Mac OS X, Android, iOS, và trên web browsers với WebGL
Một số ưu điểm của LibGDX:
-Đa nền tảng: chúng ta chỉ cần viết code 1 lần nhưng có thể chạytrên được nhiều nền tảng khác nhau Một ứng dụng của tính năng này đó làphát triển các ứng dụng cho Android Để phát triển một ứng dụng choAndroid, khi chạy thử ứng dụng, chúng ta cần chạy ứng dụng trên Emulatorhoặc trên thiết bị thật Việc này rất mất thời gian do Emulator chạy rất chậm
và quá trình cài đặt cũng như chạy ứng dụng trên thiết bị thật tốn kém VớiLibGDX, chúng ta có thể chạy ứng dụng trên PC, sau đó chỉ cần với vài dòngcode, chúng ta có thể chạy ứng dụng này trên Android với hiệu năng tươngđương Điều này giúp chúng ta kiểm thử và tìm lỗi ứng dụng nhanh hơn vàhiệu quả hơn
-Hiệu năng: Hiệu năng của LibGDX thực sự rất ấn tượng doLibGDX có các thành phần được viết bằng C và C++
Trang 14-Cộng đồng: cộng đồng sử dụng LibGDX rất tuyệt vời với số lượngngười dùng lớn Các lập trình viên luôn đóng góp và giúp đỡ cho cộng đồng.Việc sửa lỗi và nâng cấp thư viện cũng được cập nhật khá thường xuyên.-Tài liệu và ví dụ: khá nhiều với Javadoc LibGDX cũng cung cấp rấtnhiều các minh họa với đầy đủ các chức năng từ đơn giản đến phức tạp Đặt biệtđược sự hỗ trợ từ Google, nhà phát triển Android.
-Mã nguồn: mã nguồn mở với thiết kế rất rõ ràng và phù hợp vớiviệc phát triển ứng dụng cho di động LibGDX cho phép người lập trình khảnăng sử dụng các API từ các lớp thấp đến cao, tùy theo yêu cầu của người sửdụng
-Tính năng: LibGDX có rất nhiều tính năng như dựng hình, xử lý
đồ họa 2D, 3D, xử lý âm thanh, quản lý các thiết bị vào ra Cùng với đó là cácplugin đi kèm như Box2D, Bullet,
2.2 CÀI ĐẶT MÔI TRƯỜNG LÀM VIỆC
LibGDX sử dụng Grade để quản lý các dependencies, quá trình build,
và hỗ trợ tích hợp các IDE khác nhau Nó cho phép bạn phát triển các ứngdụng với bất cứ môi trường phát triển nào bạn thích Điều tuyệt vời nhất:thành viên trong nhóm của bạn có thể sử dụng những môi trường phát triểnkhác nhau trong khi làm việc trên cùng một project Chỉ cần bạn không đưacác tập tin cấu hình được tạo ra bởi riêng IDE vào source code Tậptin gitignore chứa trong những projects LibGDX sẽ làm nhiệm vụ đó nếu bạn
Android SDK, chỉ cần SDK, không cần ADT bundle, trong đó bao gồm
cả Eclipse Cài đặt những platforms ổn định nhất thông qua SDKManager: http://developer.android.com/sdk/installing/index.html
Trang 15 Android Development Tools for Eclipse - ADT Plugin :http://developer.android.com/sdk/installing/index.html
2.3 TẠO PROJECT LIBGDX
Download phiên bản mới nhất của LibGDX tại trang chủ:
Hình 2.1: Tạo project LibGDX
Trang 16Vào Advanced chọn Eclipse Save (Hình 2.2)
Hình 2.2: Chọn Eclipse
Nhấn Generate để tạo project Màn hình hiển thị sau khi tạo project thành công(Hình 2.3):
Hình 2.3: Tạo project thành công
Sau khi tạo xong project, bạn sẽ có được source code với cấu trúc thư mục như sau:
Trang 17Hình 2.4: Cấu trúc thư mục sau khi đã tạo thành công project
2.4 IMPORT PROJECT VÀO ECLIPSE
Mở Eclipse File Import General > Existing Projects intoWorkspace Chọn tới thư mục lưu project đã tạo Chọn Copy projects intoworkspace Nhấp Finish (Hình 2.5)
Trang 18Hình 2.5: Import project vào eclipse
2.4.1 CHẠY THỬ TRÊN ANDROID
Trang 19Hình 2.6: Chạy thử ứng dụng LibGDX trên Android
2.4.2 CHẠY THỬ TRÊN WEB
Hình 2.7: Chạy thử ứng dụng LibGDX trên Web
2.4.3 CHẠY THỬ TRÊN DESKTOP
Trang 20Hình 2.8: Chạy thử ứng dụng LibGDX trên Desktop
Trang 21CHƯƠNG 3: THIẾT KẾ VÀ XÂY DỰNG GAME TRAFFIC WARDEN 3.1 THIẾT KẾ
3.1.1 SƠ ĐỒ THIẾT KẾ GAME TRAFFIC WARDEN
Hình 3.1: Sơ đồ thiết kế game Traffic Warden
3.1.2 GIẢI THÍCH VIỆC CHIA THEO PACKAGE
-Do 1 nhóm có 3 thành viên nên việc phân chia Package sẽ ưu tiêntheo tên thành viên, sau đó sẽ chia theo công việc của Class đó
-Package gốc trong Project là “com.doan.projecta”
-Cách phân chia Package và đặt các Class như hình (Hình 3.2)
Trang 22-Do trong Project, công việc của Huỳnh làm riêng nên được chia 1package riêng “com.doan.projecta.huynh” Các package khác do Dũng vàNam làm có liên quan với nhau trong cùng 1 Class nên không tách ra được.
-Trong Package “com.doan.projecta.game” chứa các Class liên qua tớiClass Screen và Game
-Trong Package “com.doan.projecta.huynh” chứa Splash Screen vàMenu Screen, phần này Huỳnh làm
-Package “com.doan.projecta.object” chứa các Class liên quan đốitượng Xe, các ImageButton, các nhân vật NPC
Trang 23-Package “com.doan.projecta.util” có các Class để chứa Hằng số, cácphương thức dùng chung, Class Asset load hình, âm thanh.
-Trong Package “com.doan.projecta.world” là những Class liên quantới điều khiển và vẽ trong Game
3.2 XÂY DỰNG GAME TRAFFIC WARDEN
3.2.1 Vẽ Map
3.2.1.1 Nguyên liệu cần thiết:
-Chương trình vẽ, ở đây dùng Tiled phiên bản 0.12
-Hình Tileset gồm các ảnh nhỏ được gom lại thành 1 tập tin ảnh, cácảnh nhỏ này sẽ được đặt theo một trình tự do người thiết kế tạo ra để có mộtMap hoàn chỉnh
3.2.1.2 Các bước để vẽ
-Bước 1: Tạo 1 Map mới trống trong chương trình Tiled Chọn cácthông số như trong hình (Hình 3.3)
Hình 3.3: Tạo Map mới
+Mục “Orientation” chọn “Orthogonal”, đây là góc nhìn từ trên xuống
1 góc vuông với nền Map sẽ tạo
+Mục “Tile layer format” chọn “Base64 (zlib compressed)” để tập tinTMX xuất ra có dung lượng nhỏ Sẽ có một số chương trình tạo Map khác
có thể đọc không hiểu dạng này
Trang 24+Mục “Map size” chọn số “cột” và “dòng” Mỗi một ô (1 cột x 1dòng) sẽ chỉ đặt được 1 Tileset.
+Mục “Tile size” chọn kích thước bằng pixel cho 1 ô Mục này liênquan tới mục “Map size” ở trên
Như vậy Map của chúng ta có độ lớn là 200 x 137 Tileset, mỗi ô cókích cỡ 16x16pixel Toàn bộ Map sẽ có kích thước:
Ngang: 200 x 16 (pixel) = 3200 (pixel)
Dọc: 137 x 16 (pixel) = 2192 (pixel)
-Bước 2: Gắn Tileset vào Map:
+Trên menu Map của Tiled, chọn “New Tileset ”
+Hộp thoại “New Tileset” hiện ra như hình (Hình 3.4)
Hình 3.4: Gắn Tileset vào Map
+Trong ô “Source” nhập đường dẫn và tên tập tin Tileset
+Mục “Tile width” và “Tile height” để nhập kích cỡ 1 Tileset tínhbằng pixel Do lúc tạo Map ta chọn Tile sizelà 16px x 16px nên bây giờcũng chọn giống như vậy Nếu chọn lớn hơn hoặc nhỏ hơn sẽ làm thiếu ảnhhoặc tràn ảnh trong toàn bộ ô (Tile) trong Map
-Bước 3: Tạo Layer để đặt các Tileset
Trang 25+Mặc định Tiled sẽ đặt tên Tile Layer đầu tiên là “Tile Layer 1”,chúng ta sẽ sửa lại thành “GrassRoad” Tại Layer này sẽ vẽ đồng cỏ, đường,khung cảnh hồ nước,
+Tạo thêm Tile Layer khác đặt tên “TreesHouse” Trong Layer này sẽđặt hình ảnh cây cối, nhà cửa Phải tạo Layer riêng mà không gắn chung vớiLayer “GrassRoad” vì khi nhân vật di chuyển sẽ không bị “đè” lên cây, sẽ cóhiệu ứng đi dưới cây, trông thực tế hơn
+Tạo 1 Object Layer mới đặt tên “RoadPath” Trong Layer này sẽ vẽcác đường xe được phép chạy bằng các đường Polyline Nguyên tắc vẽđường sẽ được trình bày chi tiết trong phần “Tìm đường cho xe” (Mục3.2.6.3)
+Tạo 1 Object Layer mới đặt tên “NPCPath” Layer này sẽ được vẽcác đường Polyline - đường dành cho các nhân vật không cần điều khiển(NonPlayCharacter) di chuyển Các nhân vật này thêm vào để sinh độngthêm cho Game
-Bước 4: Vẽ trên từng Layer tương ứng trong Map cho hoàn chỉnh nhưhình (Hình 3.5)
Trang 27Hình 3.6: So sánh hình bị lỗi và hình bình thường
-Bước 7: Xử lý lỗi sọc đen dùng TileMapPacker:
+Tìm và chép các tập tin vào 1 thư mục như trong hình (Hình 3.7)
Hình 3.7: Các file cần để chạy TileMapPacker
+Chạy dòng lệnh tại thư mục đã chép các tập tin:
java -cp ".;*" com.badlogic.gdx.tiledmappacker.TiledMapPacker "input"
"output" " strip-unused"
Trong đó input là thư mục lưu tập tin TMX bị lỗi, output là thư mục
để lưu tập tin TMX sẽ tạo Các thông số “—strip-unused” sẽ bỏ những tilesetkhông sử dụng trong map Sau khi có tập tin TMX mới, đưa vào Game đểkiểm tra không còn bị lỗi sọc đen nữa
3.2.2 Class SplashGame
-Để phân chia tốt thời gian hoạt động, trước khi game sẵn sàng, sẽ cómột quá trình nạp dữ liệu, quá trình này có thể kéo dài đến vài giây Trongthời gian đó, tránh để người dùng phải nhìn vào một màn hình trống Vì thế,hãy hiển thị một màn hình chờ (splash screen) Nó cho người dùng biết gamevẫn đang hoạt động bình thường
Trang 28-Chúng ta tạo Class “SplashGame” trong package
“com.doan.projecta.huynh” , class này sẽ implements Screen của LibgGDX
- Class này hiển thị màn hình chờ và sẽ xuất hiện đầu tiên khi vàogame
-Class dùng phương thức show() tải hình, render() để vẽ hình và hiểnthị hình lên màn hình, dispose() để giải phóng bộ nhớ Ngoài ra còn có cácphương thức resize(), pause(), resume(), hide() tuy nhiên trong class nàykhông cần thiết sử dụng nên không đề cập đến
+Đầu tiên ta cần phải khởi tạo các hình ảnh cần sử dụng trong classAsset Các hình ảnh này được lưu ở mục “asset/huynh”
logoTrai = new
TextureRegion(newTexture(Gdx.files.internal( "huynh/trai.png" )));
logoPhai = new TextureRegion(new
Texture(Gdx.files.internal( "huynh/phai.png" )));
logoTren = new TextureRegion(new
Texture(Gdx.files.internal( "huynh/tren.png" )));
logoGiua = new TextureRegion(new
Texture(Gdx.files.internal( "huynh/giua.png" )));
+Phương thức show() ta khởi tạo đối tượng sprite để tham chiếuđến các hình ảnh đã được khởi tạo ở Class Asset Để hình được đặt ở giữa mànhình, ta lấy tọa độ x, y:
x = (chiều_rộng_màn_hình – chiều_rộng_logo_giữa)/2
y = chiều_cao_màn_hình – chiều_cao_logo_trên – 50 – chiều_cao_logo_giữa.
+Phương thức render() ta bắt đầu vẽ các hình ảnh để hiển thị lênmàn hình
batch begin(); //Bắt đầu vẽ
//Vẽ logo ở giữa
batch draw(Assets.instance logoGiua , x , yGiua - 30);
//Vẽ các sprite
spriteTrai draw( batch ); //Hình logo bên trái
spritePhai draw( batch ); //Hình logo bên phải
//Vẽ hình logo trên
batch draw(Assets.instance logoTren , ( w –
Assets.instance logoTren getRegionWidth()) / 2, yTren - 20);
batch end(); //Kết thúc vẽ
Trang 29+Phương thức dispose() để giải phóng bộ nhớ.
Dưới đây là Demo màn hình Splash Screen của game Traffic Warden (Hình 3.8)
Hình 3.8: Demo splash screen trong game Traffic Warden
3.2.3 Class MenuScreen
Khi game đã sẵn sàng, chúng ta cần cung cấp cho người chơi màn hình
để lựa chọn, ví dụ như: Bắt đầu vào trò chơi hay Chơi tiếp, Hướng dẫn cáchchơi,
“com.doan.projecta.huynh”, class này sẽ extends Class InputAdapter,implements Screen và GestureListener của LibGDX
-Tại Class Constant, tiến hành khởi tạo đường dẫn, tên cácImageButton và các hình ảnh dưới dạng String Dưới đây là code mẫu khởi tạotên Button và hình nền dùng cho Màn hình Menu chính Dùng cho các mànhình khác ta có thể làm tương tự
Trang 30//Hình nền menu screen
public static final String MENU_SCREEN = "huynh/menu.png" ;
//Button menu Screen lấy từ atlas
public static final String MENU_BUTTON = "huynh/buttonMenu.atlas" ;
public static final String BTN_START = "batdau" ;
public static final String BTN_START_PRESS = "batdauPress" ;
public static final String BTN_RESUME = "choitiep" ;
public static final String BTN_RESUME_PRESS = "choitiepPress" ;
public static final String BTN_EXIT_MENU = "thoat" ;
public static final String BTN_EXIT_MENU_PRESS = "thoatPress" ;
public static final String BTN_HELP = "huongdan" ;
public static final String BTN_HELP_PRESS = "huongdanPress" ;
-Ở Class Asset, ta khởi tạo các TextureRegion cho mỗi button và thamchiếu tới tên các button đã tạo ở Class Constant Dưới đây cũng là code mẫudùng cho Menu Screen Các màn hình menu khác ta có thể làm tương tự
btnBatdau = buttonPack.findRegion(Constant.BTN_START);
btnBatdauPress = buttonPack.findRegion(Constant.BTN_START_PRESS); btnChoitiep = buttonPack.findRegion(Constant.BTN_RESUME);
btnChoitiepPress = buttonPack.findRegion(Constant.BTN_RESUME_PRESS); btnHuongdan = buttonPack.findRegion(Constant.BTN_HELP);
btnHuongdanPress = buttonPack.findRegion(Constant.BTN_HELP_PRESS); btnThoatMenu = buttonPack.findRegion(Constant.BTN_EXIT_MENU);
btnThoatMenuPress =
buttonPack.findRegion(Constant.BTN_EXIT_MENU_PRESS);
-Sau đó khởi tạo các giá trị cần thiết ngay trong Constructor nhưSpriteBatch để vẽ, các ImageButton cần dùng Các ImageButton này có 2 giátrị truyền vào là buttonUp là trạng thái khi thả chuột hoặc cảm ứng,buttonDown là trạng thái nhấn chuột hoặc cảm ứng
-Vì Class này có 1 màn hình chính và 3 màn hình phụ nên ta sẽ tạo 4phương thức vẽ riêng cho 4 màn hình này với giá trị truyền vào là SpriteBatch
để vẽ
+Màn hình MenuScreen (màn hình chính) – phương thứcdrawScreen()
+Màn hình Cốt truyện – Khi nhấp vào button bắt đầu trên mànhình chính, sẽ chuyển sang màn hình cốt truyện, màn hình này hiển thị nộidung cốt truyện, sau khi xem hết cốt truyện sẽ có ImageButton “Bắt đầu” đểvào game – phương thức drawCotTruyen()
Trang 31+Màn hình Hướng dẫn – Khi nhấp vào ImageButton Hướng dẫn,
sẽ chuyển sang màn hình Hướng dẫn, nếu người chơi gặp vấn đề khi chơigame có thể vào đây để tham khảo cách chơi – phương thức drawHuongDan()
-Đây là code mẫu trong phương thức drawScreen(), các phương thứccòn lại có thể làm tương tự
// Nếu màn hình lớn hơn mặc định ta tiến hành vẽ nền
if ( x > 0 || y > 0) //x,y: vị trí canh khung nằm giữa màn hình
batch.draw( menuscreen , 0, 0);
//Vẽ khung
batch.draw( hdregion [ currentImg ], x , y ); //hdregion: Mảng các ảnh
// vẽ các button
//Xử lý 2 nút này và currentImg trong phần tap() hoặc pan()
btnPre setPosition( x + 40, y + 40); //Đặt vị trí ImageButton
btnPre render(batch); //Vẽ ImageButton
-Trong Class có phương thức show(), render(), resize(), pause(),resume(), hide(), dispose() do implement Screen của LibGDX
+Phương thức show() được gọi khi Class Screen vừa được gọi(trước khi gọi render() ) Tương đương phương thức create() (trong ClassGame) Là phương thức dùng để khởi tạo các giá trị cho game Đối tượngchính trong phương thức này là Pixmap, ta dùng Pixmap để vẽ màu nền chocác màn hình phụ, khi khung màn hình phụ nhỏ hơn màn hình chính
Trang 32//Khởi tạo ảnh pixmap cốt truyện
Pixmap pcottruyen = new Pixmap((int) w , (int) h , Format.RGBA8888);
//Màu nền trong suốt 50% cho cốt truyện
pcottruyen.setColor(0.5f, 0.2f, 0, 0.5f);
//Tô màu hình chữ nhật
pcottruyen.fillRectangle(0, 300, pcottruyen.getWidth(),
pcottruyen.getHeight() +100);
//Lưu ảnh vào texture
cottruyen_tt = new Texture(pcottruyen);
+Phương thức dispose() giải phóng bộ nhớ
+Phương thức resize(), pause(), resume(), hide() không cần thiết sửdụng trong class này nên không đề cập đến
-Để có thể thao tác được trên desktop ta kế thừa class “InputAdapter”
và implements “GestureListener” để thao tác trên màn hình cảm ứng Để cóthể kết hợp được 2 Class này, ta sử dụng 1 class trung gian của Libgdx đó làInputMultiplexer (Xem kỹ hơn phần này ở mục 3.2.13 Class WorlController)
Để có thể chạm (touch – trên màn hình cảm ứng) hoặc bấm chuột (click trêndesktop) ta có thể xử lý chung trong phương thức tap()
-Các sự kiện dùng trong class InputAdapter:
+Phương thức mouseMoved(), để khi touch (trên màn hình cảmứng) hoặc click (trên desktop) vào ImageButton sẽ tạo ra hiệu ứng Hover(Hình 3.9) (trong bài này sử dụng ImageButton có màu sáng hơn so với màubutton hiện tại) để cho người chơi biết mình có thể tương tác với đối tượngnào
Trang 33Hình 3.9: Trước và sau khi touch hoặc click vào button
+Sự kiện nhấn và giữ trên thiết bị cảm ứng sử dụng phương thứctouchDown() Khi người chơi chạm vào một vị trí trên màn hình cảm ứnghoặc nhấn giữ chuột trên desktop sẽ gọi sự kiện này, sự kiện này tạo ra cáchiệu ứng cho ImageButton (Ví dụ là hiệu ứng Hover vừa được nêu ở trên)
+Sự kiện touchUp() là khi ta thả chuột trên desktop hoặc thả chạmvào một vị trí trên thiết bị cảm ứng sẽ tạo ra hiệu ứng ngược lại với sự kiệntouchDown()
Hình 3.10: Demo menu screen game Traffic Warden
3.2.4 Class Main Game
-Tạo Class đặt tên “MainGame” trong package “com.doan.projecta.game”,
kế thừa Class Game của LibGDX
-Class này sẽ được gọi thứ 2 (sau các Class DekstopLauncher hoặc
Trang 34-Trong Class có phương thức “create()” dùng để gọi Class tải hình và âmthanh vào Game, khởi tạo Class “SplashGame”.
-Class còn có phương thức “displose()” được gọi để giải phóng bộ nhớ khiđóng Game
3.2.5 Class ProjectA
-Tạo Class đặt tên “ProjectA” implements Screen trong package
“com.doan.projecta.game”
-Class này sẽ khởi tạo các Class chính cho Game hoạt động bao gồm
“WorldController”, “WorldRender”, “World”, “MainCamera”
-Do implement Class Screen nên có đủ các phương thức của Class Screennhư show(), render(), resize(), pause(), resume(), hide(), dispose()
-Trong phương thức show() có cho phép load dữ liệu nếu người chơi muốnchơi tiếp từ lần trước
-Phương thức render() sẽ gọi phương thức vẽ và phương thức điều khiểnnhập từ người chơi Đây cũng là phần Game Loop trong Lập trình Game
-Phương thức resize() được gọi khi có thay đổi về kích thước màn hìnhGame
-Phương thức pause() và resume(), hide() sẽ gọi phần load và save Game.-Phương thức dispose() giải phóng bộ nhớ những phần liên quan do Class
“ProjectA” này đã tạo Phương thức sẽ không được tự gọi mà ta phải gọi phươngthức này từ Class “MainGame”
3.2.6 Class Xe
-Tạo Class đặt tên “Xe” trong package “com.doan.projecta.object”
-Class Xe này chứa các thông tin về hình ảnh, gốc quay trung tâm, đường đi,biển số xe, vị trí đường đi, vị trí xe trên Map, vận tốc, góc quay, vùng bao quanh,loại vi phạm, tên đường xe đang đi của 1 xe
-Vùng bao quanh của xe là một hình đa giác (Polygon)(com.badlogic.gdx.math.Polygon) có 4 cạnh tương ứng với 4 cạnh của hình xe
-Vùng bao quanh không sử dụng hình chữ nhật(com.badlogic.gdx.math.Rectangle) cũng có 4 cạnh vì hình chữ nhật không thểxoay 1 góc tự do như đa giác được Do sử dụng hình đa giác nên tính phức tạp khi
Trang 35đặt vùng bao quanh cũng phức tạp lên hơn nhiều lần so với hình chữ nhật Hình(Hình 3.11) minh họa cho giải thích này.
Hình: 3.11: So sánh góc quay của 2 hình Chữ nhật và Đa giác
3.2.7 Class CacXe
-Class “CacXe” chứa tập hợp một mảng (com.badlogic.gdx.utils.Array)các đối tượng Xe (Class Xe)
-Các xe sẽ được tạo theo một số lượng cho trước, hình ảnh và biển số
xe là ngẫu nhiên, biển số có 4 chữ số và không trùng với biển số xe khác Vịtrí ban đầu trên đường của cũng là ngẫu nhiên nhưng không trùng lên nhau,cách nhau 1 đoạn lớn hơn chiều dài xe
-Loại vi phạm của xe được tạo ngẫu nhiên bằng phương thứcMathUtil.ramdom(số) Kết quả là số nguyên (Integer) đại diện cho lỗi Bảngqui định mã số và tên lỗi như sau:
1 Lái lấn qua 2 bên đường xe chạy Lỗi lấn tuyến
2 Xe chạy nhanh, chậm, lệch qua 2 bên Lỗi say rượu
3 Xe chạy nhanh Lái xe quá tốc độ qui định
4 trở lên Các lỗi khác Để dành sau này nâng cấp sẽ dùng
Bảng 3.1: Lỗi tham khảo trong Game.
-Trong Class này có thủ tục vẽ các xe lên màn hình Một vòng lặp sẽquét từng thông tin xe trong Mảng các xe để tìm vị trí xe hiện tại, góc quay xe,
Trang 36vị trí tiếp theo của xe, kiểm tra có va chạm với xe khác hay không rồi vẽ ramàn hình.
3.2.7.1 Tìm vị trí hiện tại của xe
Hình 3.12: Sơ đồ tính toán vị trí đặt xe theo tâm
-Giả sử điểm O (x1,y1) là tâm của xe cũng là vị trí sẽ đặt trùng vớiĐiểm đặt tâm xe, điểm P(x2,y2) là vị trí đặt xe thực trong Game ta cần tìm.Chiều ngang xe là w, chiều cao là h Ta tìm được vị trí đặt điểm P là:
P.x=O.x-w/2 và P.y=O.y-h/2-Thay vì phải tính toán, trong LibGDX đã hỗ trợ chúng ta làm dễ hơnbằng cách dùng Vector2 và phương thức sub() Code sau là tính vị trí:
//Lấy vị trí tâm xe, cũng là tâm hình ảnh
Vector2 pos_origin = new Vector2(w / 2, h / 2);
//Lấy vị trí của waypoint (cũng là vị trí đặt pos_origin theo tính toán)
Vector2 vitri = xe waypoint get(xe waypointXY ).cpy();
//Tính vị trí xe
xe position = vitri.sub(pos_origin);
3.2.7.2 Tính góc quay tương đối cho xe
Trang 37Lấy 1 điểm hiện tại và 1 điểm kế tiếp xe sẽ đi qua, ta được 1 hình tam giác vuông như hình (Hình 3.13) Từ đó sẽ tìm được góc α từ phép tính tang góc.
Hình 3.13: Tính góc quay cho xe
Vector2 v1 = xe waypoint get(xe waypointXY ); //Điểm thứ nhất
Vector2 v2 = xe waypoint get(xe waypointXY + 1); //Điểm thứ hai
float tang = (v2 y - v1 y ) / (v2 x - v1 x ); //Tìm tang của góc
float goc = (float) Math.toDegrees(Math.atan(tang));//Chuyển về độ //Liên quan tới góc 90 độ vì ảnh xe thẳng đứng lên trên nên
//phải quay góc 90 độ về góc 0 độ chuẩn
if (v2 x >= v1 x
xe rotation = (360 - 90 + goc);
else
xe rotation = (90 + goc);
3.2.7.3 Tìm đường cho xe khi kết thúc đường hiện tại
-Do đường xe chạy không chỉ có đường thẳng mà sẽ có một số ngã ba,ngã tư nên sẽ có thêm vài lựa chọn đường xe có thể chạy
-Trong Class “CacXe” này sẽ có một Mảng để lưu các đường xe có thểchạy Khi xe hết đường chạy sẽ tìm trong mảng xem có đường nào nối tiếp vớiđiểm xe kết thúc hay không Nếu có nhiều hơn 1 đường thì sẽ chọn ngẫu nhiên
1 đường Code phương thức sau giải thích cho cách tìm đường này:
Trang 38public static Way findPath(Array<Way> startPath, Vector2
endPoint) {
Way retPath = null; Array<Way> numPath = new Array<Way>();
int startPathSize = startPath size ;
for (int i = 0; i < startPathSize; i++) {
//Lấy điểm đầu so sánh với điểm cuối
if (startPath.get(i) path get(0).equals(endPoint))
3.2.7.4 Tạo ra biểu hiện lỗi cho xe nếu xe đó có giá trị lỗi
-Với lỗi có mã số là 1, là lỗi lấn tuyến Khi xe chạy bình thường không
vi phạm, ta cho biên độ bằng 0, khi xe vi phạm cho biên độ là 1 con số đủ lớn
để xe chạy lên vạch chia đường Vị trí mới sẽ cách vị trí xe nếu không vi phạm
1 đoạn, trong Game là 20 đơn vị pixel
Hình 3.14: Minh họa tạo lỗi xe chạy lấn tuyến
-Với lỗi có mã số là 2, xe sẽ chạy nhanh, chậm, lệch qua 2 bên Cũngtương tự như mã lỗi số 1 Nhưng ở đây có thêm phần tăng giảm tốc độ đượctính theo hình sin
-Với lỗi mã số 3 là lỗi vượt quá tốc độ Ta cho tốc độ tăng lên bằngcách tạo ra một con số ngẫu nhiên lớn hơn số vận tốc bình thường
Trang 39-Đoạn code xử lý việc tạo ra 3 lỗi đã giải thích ở trên:
runTime = ( runTime + 0.1f) % 360; //Biến lưu góc xe sẽ chạy
if (xe loai_vipham == 1) { //Lỗi mã số 1: Xe chạy lệch qua 2 bên
//Tạo ra biên độ tăng dần và giảm dần theo hình Sin
float randomBienDo = Constant.BIEN_DO *
MathUtils.cosDeg(runTime );
//Giới hạn các góc quay được phép có lỗi
if (xe rotation == 0 || xe rotation == 180)
xe position add(randomBienDo, 0);
if (xe rotation == 90 || xe rotation == 270)
xe position add(0, randomBienDo);
} else if (xe loai_vipham == 2) { //Lỗi mã số 2: Lái nhanh chậm + lệch qua lại
float randomBienDo = Constant.BIEN_DO *
MathUtils.cosDeg(runTime );
xe position add(randomBienDo, randomBienDo);
} else if (xe loai_vipham == 3) { //Lỗi mã số 3: Vượt tốc độ
if (xe velocity <= 1)
xe velocity = MathUtils.random(1.0f, 2.5f) + 0.5f;
3.2.7.5 Kỹ thuật lấy vùng bao quanh xe dùng cho Collision
-Do trong Game vị trí xe nằm trên Map, mà Map lại cho phép phóng to,thu nhỏ nên vị trí xe để xác định vùng bao quanh thay đổi tùy vào giá trị phóng
to, thu nhỏ đó
-Kích thước vùng bao quanh này cũng phụ thuộc vào giá trị phóng to, thunhỏ và được tính bằng: Kích_thước = (1/giá_trị_zoom) * kích_cỡ_ban_đầu
-Góc quay vùng bao quanh sẽ tương ứng với góc quay của xe đó
-Vị trí đặt vùng bao quanh sẽ được tính như code sau:
//Lấy tọa độ gốc O từ Screen chuyển vào MainCamera trong Game
scrZero = newWorld mainCamera project(new Vector3(0, 0, 0));
//Thêm sai số do chuyển đổi zoom
addVector = new Vector2(-12 * newWorld mainCamera getZoom()
* ( newWorld mainCamera getZoom() - 1), -15 *
newWorld mainCamera getZoom()
* ( newWorld mainCamera getZoom() - 1));
//Scale vị trí xe theo Zoom và copy vào Vector tạm
Vector2 vpos = xe position cpy().add( addVector ).scl(1 /
( newWorld mainCamera getZoom()));
Trang 40-Cách tính va chạm đã được hỗ trợ khá nhiều trong thư viện Intersector(com.badlogic.gdx.math.Intersector) của LibGDX Như đã trình bày trongphần (Mục 3.2.5), khung xác định va chạm là hình Polygon nên trong thư viện
Intersector có phương thức overlapConvexPolygons (Polygon p1, Polygon
p2) làm nhiệm vụ kiểm tra giữa xe với xe có chạm nhau không Phương thức
này có nhược điểm là chạy khá chậm do phải tính toán, so sánh rất nhiều.Trong thư viện Polygon của LibGDX có hỗ trợ việc chuyển từ hình Polygon
sang hình Rectangle ( phương thức getBoundingRectangle ()) Nhưng cách
này cũng có vấn đề là khung va chạm Rectangle sẽ lớn hơn kích cỡ thật sự của
xe ở đa số các góc quay (Rectangle là đường gạch liền, khung hình xe làđường nét đứt) (Hình 3.15)
Hình 3.15: Khung va chạm chuyển từ Polygon góc 28 0 sang Rectangle
-Với cách này khi xe quay góc và chạy gần nhau sẽ va chạm mà thực tếthì hình 2 chiếc xe vẫn cách xa nhau, không chạm nhau Và cách này cũngkhông được nhanh Cuối cùng phải chọn cách xác định va chạm khác nhanhhơn Đó là cách xác định khoảng cách 2 tâm xe với nhau trên cùng 1 đường(Hình 3.16) và 2 đường khác nhau (Hình 3.17)
Hình 3.16: Tính khoảng cách xe trong cùng 1 đường