YOMEDIA
ADSENSE
Phát triển game đơn giãn trên mobile
65
lượt xem 9
download
lượt xem 9
download
Download
Vui lòng tải xuống để xem tài liệu đầy đủ
Mời các bạn cùng tham khảo nội dung bài viết "Phát triển game đơn giãn trên mobile" dưới đây để được hướng dẫn, giới thiệu về cách thức để phát triển một trò chơi đơn giãn trong J2ME,các thành phần giao diện người dùng có sẵn trong J2,... Đây là tài liệu tham khảo hữu ích cho các bạn chuyên ngành Công nghệ thông tin.
AMBIENT/
Chủ đề:
Bình luận(0) Đăng nhập để gửi bình luận!
Nội dung Text: Phát triển game đơn giãn trên mobile
Phát triển game đơn giãn trên Mobile(P1) <br />
Giới thiệu:<br />
Bài này nhằm mục tiêu cung cấp hướng dẫn, giới thiệu về cách thức để phát triển một trò <br />
chơi đơn giãn trong J2ME. Trước khi đọc bài này, tôi khuyên bạn nên tìm hiểu sơ lược về <br />
platform J2ME. Đây là một trong 3 platform của Java được sử dụng để phát triển các ứng <br />
dụng trên các thiết bị nhúng, mobile…Bây giờ, hãy cùng tìm hiểu phần 1.<br />
Phần một: <br />
Ngõ vào ứng dụng(entry point):<br />
Trong tất cả các ứng dụng trên J2ME, để chạy ứng dụng chúng ta phải extends từ lớp <br />
ảo(abstract) MIDlet trong gói javax.microedition.midlet, tương tự như tạo một applet phải <br />
extend từ lớp java.applet.Applet. Và entry point của nó là phương thức startApp(), tương tự <br />
như phương thức main() trong J2SE.<br />
Ví dụ: Ta có một lớp MyMidlet. <br />
public class MyMidlet extends MIDlet{<br />
// invoked when the application starts and each time is resumed<br />
protected void startApp() throws MIDletStateChangeException {}<br />
// invoked when the MIDlet needs to be destroyed<br />
protected void destroyApp(boolean uncondicional) throws MIDletStateChangeException {}<br />
// invoked when the MIDlet needs to be paused. (Some phones ignore pauseApp().)<br />
protected void pauseApp() {}<br />
}<br />
Nếu bạn tạo project và chạy file này thì nó chỉ hiển thị màn hình blank.<br />
Tiếp theo, để trình bày nội dung trong ứng dụng, bạn cần sử dụng lớp Display. Lớp Display <br />
điều khiển tất cả những gì xảy trong màn hình của MIDlet, mỗi MIDlet có một đối tượng <br />
Display và truy cập nó thông qua việc sử dụng phương thức tĩnh getDisplay().<br />
Để biểu diễn một đối tượng nào đó trên màn hình, bạn cần phải sử dụng phương thức <br />
setCurrent() của lớp Displayable.<br />
Ví dụ: ta có một lớp Alert, lớp này kế thừa từ lớp Displayable, trong ví dụ này sử dụng Alert <br />
để hiển thị một câu thông báo đơn giãn lên màn hình.<br />
Alert alert;<br />
//entry point for the application<br />
protected void startApp() throws MIDletStateChangeException {<br />
// creates alert<br />
alert = new Alert("Hello World");<br />
// shows alert in the screen.<br />
Display.getDisplay(this).setCurrent(alert);<br />
}<br />
Nếu chạy chương trình, bạn sẽ thấy nó hiển thị ra một thông điệp như sau:<br />
Tiếp theo, để thêm các command cho ứng dụng, như commad exit, bạn xử lý như sau:<br />
Command comExit;<br />
[...]<br />
protected void startApp() throws MIDletStateChangeException {<br />
[...]<br />
// create command<br />
comExit = new Command("Exit", Command.EXIT, 1);<br />
// add a command to exit the Midlet<br />
<br />
<br />
1<br />
alert.addCommand(comExit);<br />
[...]<br />
}<br />
Đoạn code trên show cho bạn command exit lên màn hình, nhưng khi bạn click vào, nó không <br />
làm gì cả bởi bạn cần thêm CommandListener vào Alert để đăng ký một listener. <br />
public class MyMidlet extends MIDlet implements CommandListener{ public void startApp()<br />
{ [...] // adds a listener to the alert alert.setCommandListener(this); } public void <br />
commandAction(Command cmd, Displayable display) { // check what command was selected if <br />
(cmd == comExit) { notifyDestroyed(); } } }<br />
Với thay đổi này, chúng ta có một MIDlet hoàn chỉnh. Ở phần tiếp theo, chúng ta sẽ có một <br />
cái nhìn chi tiết hơn về các phần tử giao diện người dùng như: Alert, Display, và <br />
CommandListener.<br />
<br />
<br />
<br />
<br />
Phát triển game đơn giãn trên Mobile(P2) <br />
Ở phần này, ta tìm hiểu về các thành phần giao diện người dùng(UI) có sẵn trong J2ME <br />
nhằm tạo ra sự tương tác giữa người dùng với điện thoại, đây là vấn đề quan trọng bởi kích <br />
thước của màn hình điện thoại được giới hạn. J2ME cung cấp đặc tả MIDP chứa các thành <br />
<br />
<br />
<br />
2<br />
phần giao diện đồ họa, hiện nay đã có rất nhiều phiên bản MIDP 3.0, tuy nhiên trong bài này <br />
tôi chỉ xét MIDP 2.0. Ta cùng tìm hiểu sơ đồ phân lớp của nó:<br />
<br />
<br />
<br />
<br />
MIDP 2.0 cung cấp các lớp UI trong một gói javax.microedition.lcdui, trong đó lcdui là viết tắt <br />
của liquid crystal display user interface(LCD UI). Để show một phần tử UI lên màn hình, bạn <br />
phải sử dụng một lớp Displayable. Ví dụ, một lớp Displayable có thể có một title, một ticker, <br />
và các command liên kết với nó.<br />
Lớp Display quản lý những cái gì hiển thị lên màn hình. Phương thức static getDisplay(MIDlet <br />
midlet) cho phép bạn truy cập vào Display của MIDlet. Sau đó, bạn sử dụng phương thức <br />
setCurrent(Displayable element) để lựa chọn đối tượng nào extend từ Displayable được hiển <br />
thị lên màn hình. Tại một thời điểm, chỉ có một đối tượng Displayable được hiển thị trên màn <br />
hình. Hãy xem lại ví dụ từ bài trước:<br />
Display.getDisplay(this).setCurrent(alert);<br />
Trong MIDP, nó phân chia các lớp thành 2 thành phần: các thành phần giao diện cấp cao(high<br />
level interface component) và cấp thấp(lowlevel).<br />
Các thành phần highlevel được thực thi thông qua các lớp extends từ class Screen, còn các <br />
thành phần lowlevel được thực thi thông qua các lớp extends từ class Canvas. Và tất cả chúng <br />
đều extends từ class Displayable.<br />
Bất kỳ một ứng dụng nào cũng có thể kết hợp cả các thành phần giao diện highlevel và low<br />
level để phục vụ cho mục đích của ứng dụng. Ví dụ, trong một ứng dụng game, List và Form <br />
có thể được sử dụng để chọn lựa hay cấu hình game, trong khi Canvas (hay GameCanvas) <br />
<br />
<br />
<br />
3<br />
được sử dụng cho các thành phần tương tác của game như tạo nhân vật chuyển động, ảnh <br />
background.<br />
Lớp Command:<br />
Một MIDlet tương tác với người dùng thông qua các Command. Một Command tương đương <br />
với một menu item trong một ứng dụng thông thường, và nó chỉ có thể kết hợp với một phần <br />
tử UI Displayable. Lớp Displayable cho phép người dùng attacth một Command bằng cách sử <br />
dung phương thức addCommand(Command command). Một đối tượng Displayable có thể cón <br />
nhiều Command bên trong nó. Lớp Command được nắm giữ các thông tin về command. <br />
Thông tin này được đóng gói trong 4 thuộc tính: short label, optional long label, command type <br />
và priority. Ví dụ, sử dụng lớp Command để tạo ra đối tượng command thông qua cung cấp <br />
các giá trị trong Constructor:<br />
// adds a command to exit the MIDlet<br />
comExit = new Command("Exit", Command.EXIT, 1);<br />
<br />
Lưu ý rằng các command không thay đổi khi chúng được tạo ra.<br />
Nếu xác định command type, bạn có thể cho phép thiết bị chạy MIDlet ánh xạ(map) bất kỳ <br />
phím định sẵn nào trên thiết bị vào command đó. Ví dụ, một command với kiểu OK được ánh <br />
xạ vào phím OK của thiết bị. Trong MIDP 2.0, nó có sẵn các kiểu command sau: BACK, <br />
CANCEL, EXIT, HELP, ITEM, SCREEN và STOP. Kiểu SCREEN liên quan đến một <br />
command được map trong ứng dụng cho màn hiền hiện thời. Cả SCREEN và ITEM không có <br />
bất kỳ các phím được ánh xạ trên thiết bị. Để nhận được phản hồi từ người dùng, bạn cần <br />
phải listen từ các command, điều này được thực hiện thông qua thực thi giao diện <br />
CommandListener. Trong ví dụ Hello World, Interface được thực thi thông qua phương thức <br />
commandAction().<br />
Ví dụ:<br />
alert.addCommand(comExit);<br />
// adds a listener to the form<br />
alert.setCommandListener(this);<br />
[...]<br />
public void commandAction(Command cmd, Displayable display) {<br />
if (cmd == comExit) {<br />
exit();<br />
}<br />
}<br />
Như bạn thấy, phương thức commandAction() nhận 2 tham số: đó là Command được thực thi <br />
và Displayable đang được hiển thị hiện tại. <br />
Giao diện người dùng cấp cao(Highlevel User Interface):<br />
Các API của giao diện người dùng cấp cao được thiết kế cho các ứng dụng kinh doanh của <br />
các khách hàng mà các thành phần client của nó chạy trên các thiết bị di động. Đối với các <br />
loại ứng dụng này, tính linh động(portability) qua nhiều thiết bị là rất quan trọng. Để đạt <br />
được tính linh động như vậy, các API của nó ở mức cao được trừu tượng hóa(abstraction) và <br />
cung cấp ít các điều khiển hơn các look anh feel. Điều này cho phép thiết bị sử dụng look and <br />
feel giao diên người dùng tự nhiên(native) để hiển thị thay thế cho các thành phần giao diện <br />
MIDP highlevel. Điều này có nghĩa khi một môt ứng dụng được viết bằng API highlevel, nó <br />
look and feel một cách tự động sử dụng look and feel của thiết bị mà ứng dụng đang chạy, <br />
còn đối với người dùng cuối, điều này cung cấp sự tương tác với người dùng một cách liền <br />
<br />
<br />
<br />
4<br />
mạch, đó là ứng dụng MIDP làm việc như là các ứng dụng native trên thiết bị.<br />
Tóm lại, khi sử dụng API highlevel, bạn có thể:<br />
Vẽ để được hiển thị bởi hệ thống phần mềm của thiết bị. Ứng dụng không định <br />
nghĩa giao diện trực quan như hình dáng, màu sắc… của các thành phần highlevel.<br />
Điều hướng, cuộn, và các tương tác nguyên thủy khác với các thành phần giao diện <br />
người dùng được thực hiện bởi thiết bị. Tuy nhiên, ứng dụng không nhận biết được <br />
các tương tác này.<br />
Ứng dụng không thể truy cập vào các kỹ thuật input cụ thể, như các phím nhấn cụ thể <br />
nào đó.<br />
Alert:<br />
Ứng dụng Hello World sử dụng một alert. Phần tử này đại diện cho một màn hình(Screen) <br />
dùng để show dữ liệu đến người dùng và đợi một thời gian trước khi xử lý đối tượng <br />
Displayable tiếp theo. Một Alert thì có thể chứa môt chuỗi text và một image. Thông thường, <br />
Alert được sử dụng để thông báo lỗi hay các ngoại lệ khác.<br />
TextBox:<br />
Kế thừa từ lớp Screen, cho phép người dùng nhập và chỉnh sửa text. Lớp này có thể được cấu <br />
hình để thích nghi với các nhu cầu của bạn. Bạn có thể giới hạn maximum các ký tự hiển thị <br />
trong TextBox. Ngoài ra, bạn có thể ràng buộc các kiểu nhập cho TextBox bằng cách sử dụng <br />
các flag được định nghĩa trong lớp TextField. Có 6 ràng buộc(constrain) để giới hạn nội dung <br />
hiển thị, đó là: ANY, EMAILADRR, NUMBERIC, PHONENUMBER, URL và DECIMAL. Có <br />
6 ràng buộc ảnh hưởng tới kiểu nhập: PASSWORD, UNEDITABLE, SENSITIVE, <br />
NON_PREDICTIVE, INITIAL_CAPS_WORD, và INITIAL_CAPS_SENSITIVE. Ví dụ, chỉ <br />
cho phép địa chỉ email được phép nhập trong TextBox, bạn thiết lập flag <br />
TextField.EMAILADRR sử dụng phương thức setConstrains(), ngoài ra để kết hợp nhiều ràng <br />
buộc cùng lúc, bạn sử dụng toán tử OR giữa các flag. Ví dụ:<br />
setConstraints(TextField.EMAILADDR | TextField.UNEDITABLE);<br />
List:<br />
Một List chứa một danh sách các chọn lựa. Khi List được biểu diễn trên màn hình, người <br />
dùng có thể tương tác với nó bằng cách chon lựa các phần tử, di chuyển qua lại giữa các <br />
phần tử của List. Nó có các kiểu cấu hình sau:<br />
Choice.EXCLUSIVE: chỉ có 1 phần tử được chọn lựa.<br />
Choice.MULTIPLE: có thể có nhiều phần tử được chọn lựa.<br />
Choice.IMPLICIT: phần tử được hightlight được chọn lựa.<br />
Form:<br />
Nó chứa nhiều item, bất kỳ lớp nào extends từ lớp Item để có thể được chứa trong một Form. <br />
Việc thực thi xử lý các layout, traversal, và scrolling. Nội dung của Form có thể được cuộn lại <br />
với nhau.<br />
Các loại Item có thể được thêm vào trong Form:<br />
StringItem: là một label và không cho phép người dùng sửa lên nó. Item này có thể chứa một <br />
tiêu đề và một text, và cả hai đều có thể null.<br />
DateField: cho phép người dùng nhập ngày date/time một trong 3 dạng sau: DATE, TIME và <br />
DATE_TIME.<br />
TextField: Tương tự như TextBox.<br />
<br />
<br />
5<br />
ChoiceGroup: Tương tự như List.<br />
Gauge: sử dụng để mô phỏng process bar, tuy nhiên nó có thể sử dụng trong kiểu tương tác <br />
bởi người dùng, ví dụ nếu bạn muốn dùng nó để show một Volume Control.<br />
ImageItem: Nắm giữ một image.<br />
CustomItem: là một lớp ảo abstract cho phép các subclass tạo ra giao diện riêng, tương tác <br />
riêng và cơ chế thông báo riêng của nó. Nếu bạn muốn một phần tử UI khác so với các phần <br />
tử được cung cấp thì bạn có thể tạo ra subclass.<br />
Giao diện người dùng cấp thấp(Lowlevel User Interface): <br />
Các API của lowlevel user interface( như lớp Canvas) được thiết kế cho các ứng dụng cần sự <br />
sắp đặt và điều khiển các phần tử graphics một cách chính xác cũng như truy cập vào các <br />
lowlevel input event. Ví dụ điển hình là game board, một chart object hay một graph. Sử dụng <br />
lowlevel user interface, một ứng dụng có thể:<br />
Kiểm soát những gì được vẽ trên màn hình.<br />
Điều khiển được các sự kiện primitive như các sự kiện nhấn phím(key press) và nhả <br />
phím(key release)<br />
Truy cập vào cụ thể từng phím và các thiệt bị đầu vào khác.<br />
Ngoài ra, trong MIDP 2.0 còn cung cấp javax.microediton.lcdui.game. Gói này bao gồm 5 class <br />
dùng để thiết kế cho các game, đó là : GameCanvas, LayerManger, Layer, Sprite và TiledLayer. <br />
Bạn có thể tìm hiểu phần này sau.<br />
Ví dụ về giao diện người dùng:<br />
Đối với mỗi màn hình game, chúng ta tạo một phương thức init[ScreenName] để khởi tạo <br />
màn hình và trả về đối tượng Displayable được tạo ra:<br />
Đối với Main Menu, bạn sử dụng component List để biểu diễn main options. Ví dụ:<br />
public Displayable initMainForm() {<br />
if (mainForm == null) {<br />
// creates a implicit List where the current element is<br />
// the selected<br />
mainForm = new List("Menu", List.IMPLICIT);<br />
// append list options<br />
mainForm.append("New Game", null);<br />
mainForm.append("Options", null);<br />
mainForm.append("Scores", null);<br />
mainForm.append("Help", null);<br />
mainForm.append("Exit", null);<br />
// adds a select Command<br />
comSelect = new Command("Select", Command.ITEM, 1);<br />
mainForm.setSelectCommand(comSelect);<br />
// adds a listener to the form<br />
mainForm.setCommandListener(this);<br />
}<br />
return mainForm;<br />
}<br />
Đối với Menu Settings, bạn chọn phần tử Form, và thêm một đối tượng ChoiceGroup để tùy <br />
chỉnh âm thanh:<br />
public Displayable initSettingsForm() {<br />
// check if already created<br />
if (settingsForm == null) {<br />
settingsForm = new Form("Settings");<br />
<br />
<br />
<br />
6<br />
settingsForm.addCommand(initBackCommand());<br />
settingsForm.setCommandListener(this);<br />
// creates a choice Group for sound options<br />
soundChoice = new ChoiceGroup("Sound", List.EXCLUSIVE);<br />
soundChoice.append("On", null);<br />
soundChoice.append("Off", null);<br />
// appends the choice to the form<br />
settingsForm.append(soundChoice);<br />
}<br />
return settingsForm;<br />
}<br />
Đối với Help Screen, bạn chọn phần tử Form với static message:<br />
public Displayable initHelpForm() {<br />
if (helpForm == null) {<br />
helpForm = new Form("Help");<br />
helpForm<br />
.append("User cursors to move your pad, don't let "+<br />
"the ball go by you, hit all the bricks!");<br />
helpForm.setCommandListener(this);<br />
helpForm.addCommand(initBackCommand());<br />
}<br />
return helpForm;<br />
}<br />
Để giới thiệu High Score, bạn sử dụng một Form với các item của nó là TextField và <br />
DateField:<br />
public Displayable initNewHighScore(int score, int pos) {<br />
if (newHighScoreForm == null) {<br />
newHighScoreForm = new Form("New High Score");<br />
newHighScoreForm.setCommandListener(this);<br />
// create items<br />
highScoreName = new TextField("Name", "", 20, TextField.ANY);<br />
highScoreValue = new StringItem("Score", Integer.toString(score));<br />
highScorePosition = new StringItem("Position", Integer.toString(pos));<br />
// create save command<br />
highScoreSave = new Command("Save", Command.OK, 1);<br />
// append command and itens to screen<br />
newHighScoreForm.addCommand(highScoreSave);<br />
newHighScoreForm.append(highScoreName);<br />
newHighScoreForm.append(highScoreValue);<br />
newHighScoreForm.append(highScorePosition);<br />
}<br />
// update score<br />
highScoreValue.setText(Integer.toString(score));<br />
// update pos<br />
highScorePosition.setText(Integer.toString(pos)+1);<br />
return newHighScoreForm;<br />
}<br />
Màn hình game sẽ được đề cập trong bài tiếp theo. Bây giờ, hãy tạo một phương thức giả lập <br />
kết thúc game và sử dụng nó để thay thế:<br />
public void endGame(int lifes, int score, int time) {<br />
Displayable nextScreen = initMainForm();<br />
String message;<br />
if (lifes == 0) {<br />
message = "Game Over!!";<br />
<br />
<br />
<br />
7<br />
} else {<br />
message = "You Win!";<br />
}<br />
int pos = isHighScore(score);<br />
if (pos != -1) {<br />
nextScreen = initNewHighScore(score, pos);<br />
}<br />
display(new Alert(message, message, null, AlertType.INFO), nextScreen);<br />
}<br />
Bây giờ, tất cả các phương thức đã được tạo ra, bạn link chúng đến phương thức <br />
commandAction(). Rewrite code:<br />
public void commandAction(Command cmd, Displayable display) {<br />
// check what screen is being displayed<br />
if (display == mainForm) {<br />
// check what command was used<br />
if (cmd == comSelect) {<br />
switch (mainForm.getSelectedIndex()) {<br />
case (0):<br />
// At the moment just go directly to the end of the game<br />
endGame(1, 200, 50);<br />
break;<br />
case (1):<br />
display(initSettingsForm());<br />
break;<br />
case (2):<br />
display(initScoreForm());<br />
break;<br />
case (3):<br />
display(initHelpForm());<br />
break;<br />
case (4):<br />
exit();<br />
break;<br />
}<br />
}<br />
} else if (display == highScoreForm) {<br />
if (cmd == comBack) {<br />
display(initMainForm());<br />
}<br />
} else if (display == settingsForm) {<br />
if (cmd == comBack) {<br />
soundOn = soundChoice.getSelectedIndex() == 0;<br />
display(initMainForm());<br />
}<br />
} else if (display == helpForm) {<br />
if (cmd == comBack) {<br />
display(initMainForm());<br />
}<br />
} else if (display == newHighScoreForm) {<br />
if (cmd == highScoreSave) {<br />
int pos = Integer.parseInt(highScorePosition.getText())-1;<br />
// advance all the scores<br />
for ( int i = scores.length-1; i > pos ; i--){<br />
scores[i].name = scores[i-1].name;<br />
scores[i].value = scores[i-1].value;<br />
scores[i].when = scores[i-1].when;<br />
<br />
<br />
<br />
8<br />
}<br />
// insert new score<br />
scores[pos].name = highScoreName.getString();<br />
scores[pos].value = Integer.parseInt(highScoreValue.getText());<br />
scores[pos].when = new Date();<br />
display(initScoreForm());<br />
}<br />
}<br />
}Tất cả các logic menu cho các MIDlet được xác định bên trong phương thức <br />
commandAction() này. Quyết định làm gì tiếp theo phụ thuộc vào màn hình hiển thị và <br />
command được chọn lựa. Từ menu chính, tôi chỉ đơn giãn chuyển hướng người dùng đến mỗi <br />
màn hình cụ thể. Các màn hình hiện tại chỉ có một back command, chỉ duy nhất một form <br />
NewHighScores có command save dùng để lưu thông tin về điểm số(score).<br />
Bạn lưu ý cách thức sử dụng phương thức display(), vì đây là cách đơn giãn để kích hoạt đối <br />
tượng Displayable.<br />
public void display(Displayable display) {<br />
// shows display in the screen.<br />
Display.getDisplay(this).setCurrent(display);<br />
}<br />
Bài tiếp theo sẽ mô tả làm thế nào để cài đặt một Game Screen.<br />
Phát triển game đơn giãn trên Mobile(P3) <br />
Ở bài trước, chúng ta đã hoàn thành một giao diện menu cho game. Tuy nhiên, màn hình Game <br />
Screen vẫn chưa được tạo ra. Mục đích của bài này là sinh ra một Game Screen có giao diện <br />
như hình dưới:<br />
<br />
<br />
<br />
<br />
Các thành phần giao diện cấp cao không thể sử dụng cho game screen bởi vì chúng ta phải <br />
điều khiển tất cả các phần tử trong game khi chúng được vẽ và làm thế nào để game tương <br />
tác trở lại với keypad. Để làm được điều này, chúng ta phải sử dụng các class giao diện low<br />
level. Các class thuộc nhóm lowlevel cho phép bạn kiểm soát chi tiết các phần tử và sự kiện <br />
<br />
<br />
9<br />
trên màn hình game. Sử dụng các class này, bạn có thể xác định rõ vị trí, màu sắc và kích <br />
thước. Cũng chính vì vậy, bạn phải thiết kế màn hình game screen cho mỗi loại màn hình ứng <br />
với mỗi thiết bị.<br />
Diagram sau cho biết các lớp chính trong giao diện lowlevel :<br />
<br />
<br />
<br />
<br />
Với entry point là lớp Canvas, nó cho phép bạn truy cập các sự kiện của hệ thống:<br />
keyPressed(), keyReleased(), keyRepeated() thông báo tới Canvas khi keypad được sử <br />
dụng.<br />
pointerPressed(), pointerDragged(), pointerReleased() thông báo tới Canvas khi <br />
pointer được sử dụng, đây là các phương thức xử lý trong các màn hình cảm ứng.<br />
paint() thông báo đến Canvas khi nó cần vẽ lên màn hình, phương thức này dùng để <br />
truy cập vào đối tượng Graphics.<br />
getWidth(), getHeight() truy cập vào kích thước hiện tại của màn hình được vẽ.<br />
Lớp Graphics cung cấp các phương thức để vẽ trực tiếp lên màn hình:<br />
drawLine() vẽ đường thẳng.<br />
drawRect(), fillRect() vẽ hay đổ màu một rectangle lên màn hình.<br />
drawArc() vẽ một cung(arc) lên màn hình, có thể dùng nó để vẽ đường tròn.<br />
drawChar() vẽ một ký tự lên màn hình.<br />
drawString() vẽ một chuỗi lên màn hình.<br />
drawImage() vẽ một bitmap image lên màn hình<br />
setFont() thiết lập Font chữ.<br />
setColor() thiết lập màu.<br />
Ngoài ra trong MIDP 2.0 còn nhiều phương thức khác, bạn tự tìm hiểu thêm.<br />
Để tạo Game Screen, bạn phải tạo một lớp extends từ lớp Canvas và cài đặt phương thức <br />
paint().<br />
import javax.microedition.lcdui.Canvas;<br />
import javax.microedition.lcdui.Graphics;<br />
public class MyCanvas extends Canvas{<br />
<br />
<br />
10<br />
int width;<br />
int height;<br />
public MyCanvas() {<br />
}<br />
protected void paint(Graphics g) {<br />
// stores width and height<br />
width = getWidth();<br />
height = getHeight();<br />
// set background color<br />
g.setColor(0,0,0);<br />
// clear screen<br />
g.fillRect(0, 0, width, height);<br />
// draw a red circle that represents a ball<br />
g.setColor(255,0,0);<br />
g.drawArc(100, 100, 5, 5, 0, 360);<br />
// draws a blue rectangle for the pad<br />
g.setColor(0,0,255);<br />
g.fillRect(100, 200, 15, 15);<br />
}<br />
}<br />
Để kích hoạt Canvas, tạo nó trong lớp MIDlet và hiển thị nó trong phương thức <br />
commandAction()<br />
public Displayable initGameCanvas() {<br />
if (gameCanvas == null){<br />
gameCanvas = new MyCanvas();<br />
// add a back Command to return to the menu screen<br />
gameCanvas.addCommand(initBackCommand());<br />
// set the listener to our actions<br />
gameCanvas.setCommandListener(this);<br />
}<br />
return gameCanvas;<br />
}<br />
Vòng lặp game:<br />
Trước khi bắt đầu, bạn phải hiểu những cách thức thông thường mà một ứng dụng game <br />
hoạt động. Một game hay animation được xây dựng dựa trên một mẫu(piece) code thực thi <br />
được lặp đi lặp lại. Mẫu code này theo dõi giá trị của các biến và các trạng thái game. Dựa <br />
trên trạng thái game, đoạn code sẽ vẽ hay vẽ lại các phần tử trên game. Các giá trị của biến <br />
có thể được thay đổi bởi các tương tác của người dùng hay các hành vi của game bên trong.<br />
Điều này được tạo ra bằng cách đặt đoạn code đó lặp đi lặp lại trong một vòng lặp liên tục. <br />
Trước khi vào vòng lặp, một biến có thể sẽ được kiểm tra xem game còn chạy hay không. <br />
Nếu không, vòng lặp có thể được thoát. Các đoạn code trong vòng lặp nên cho phép thread <br />
thực thi hiện hành sleep vài giây để điều khiển tốc độ(rate) mỗi khi trạng thái game được cập <br />
nhật(đó là sau bao lâu thì màn hình game được refresh). Code có dạng sau:<br />
public class MyCanvas extends GameCanvas implements Runnable{<br />
…<br />
<br />
<br />
11<br />
public void start() {<br />
run = true;<br />
Thread t = new Thread(this);<br />
t.start();<br />
}<br />
public void stop() {<br />
run = false;<br />
}<br />
public void run(){<br />
init();<br />
while (run){<br />
// update game elements, positions, collisions, etc..<br />
updateGameState();<br />
// check user input<br />
checkUserInput();<br />
// render screen<br />
updateGameScreen();<br />
// redraws screen<br />
flushGraphics();<br />
// controls at which rate the updates are done<br />
Thread.sleep(10);<br />
}<br />
}<br />
…<br />
}<br />
Ở đoạn code trên, lớp GameCanvas được sử dụng. Lớp GameCanvas là một trường hợp đặc <br />
biệt của lớp Canvas, được optimize cho các game. Nó sử dụng với các kỹ thuật sau:<br />
Bộ đệm đôi(double buffer): GameCanvas sử dụng một image offscreen mà image này <br />
được sử dụng cho tất cả các thao tác vẽ. Khi thao tác vẽ hoàn tất, nó được vẽ lên màn <br />
hình nhờ sử dụng phương thức flushGraphics(). Điều này giúp tránh tình trạng màn <br />
hình bị flick và các chuyển động mượt mà hơn.<br />
Lưu trữ trạng thái phím trong một mảng: Thông qua phương thức getKeyStates(), bạn <br />
có thể truy cập vào một mảng bit tương ứng với trạng thái của mỗi phím sử dụng các <br />
giá trị hằng(constant) được định nghĩa trong Canvas.<br />
Bên cạnh sử dụng GameCanvas, bạn cần sử dụng một Thread để giữ các chuyển động trong <br />
game độc lập với các event của MIDlet. Bằng cách này, các animation sẽ không cần chờ các <br />
event của hệ thống vẽ lại chính nó. Trong ví dụ này, game có 3 thực thể:<br />
Pad: là một hình chữ nhật nhỏ di chuyển từ trái sang phải nằm phái dưới màn hình.<br />
Ball: nằm trước Pad, khi bạn nhấn phím Fire, nó sẽ di chuyển với tốc độ theo chiều <br />
dọc ngang của Pad.<br />
Brick: các khối tĩnh nằm phía trên màn hình, khi chúng trúng Ball thì chúng sẽ biến <br />
mất.<br />
<br />
<br />
<br />
12<br />
Mục đích của game là làm cho các Brick biến mất càng nhanh càng tốt, và không để cho Ball <br />
chạy ra khỏi phía dưới của màn hình. Game cần theo dõi 3 biến:<br />
Điểm số của người chơi. Người chơi sẽ nhận được 10 điểm mỗi lần làm một Brick <br />
biến mất.<br />
Số lượt chơi: mỗi lần Ball chạy ra khỏi phía dưới màn hình, người chơi mất một <br />
lượt.<br />
Thời gian chơi game: người chơi phải hoàn tất game trong khoảng thời gian cho phép <br />
hay giới hạn.<br />
Lớp Entity là supeclass của 3 thực thể trên, gồm có các thuộc tính và chức năng sau:<br />
x, y: Xác định vị trí hiện tại của thực thể.<br />
speedX, speedY: Xác định tốc độ của thực thể.<br />
width, height: Xác định kích thước của thực thể.<br />
update(): Xác định hành vi của các thực thể.<br />
paint(): phương thức được lớp Graphics sử dụng để vẽ các thực thể.<br />
collided(): Kiểm tra va chạm giữa các thực thể.<br />
Tiếp theo, mở rộng lớp Entity cho mỗi phần tử game và cài đặt phương thức update() và <br />
paint(). Đối với Ball:<br />
public class Ball extends Entity {<br />
public int radium = 2;<br />
public Ball(int radium){<br />
this.radium = radium;<br />
width = radium * 2;<br />
height = radium * 2;<br />
// red color<br />
this.color = 0×00FF0000;<br />
}<br />
/**<br />
* Paints the ball using a circle<br />
*/<br />
public void paint(Graphics g) {<br />
g.setColor(color);<br />
g.fillArc(x, y, radium*2, radium*2, 0, 360);<br />
}<br />
/***<br />
* Updates the ball position.<br />
*/<br />
public void update() {<br />
// update position<br />
oldX=x;<br />
oldY=y;<br />
x += speedX;<br />
y += speedY;<br />
<br />
<br />
13<br />
}<br />
}<br />
Đối với Pad:<br />
public class Pad extends Entity{<br />
int minLimit = 0;<br />
int maxLimit = 1;<br />
public Pad(int width, int height) {<br />
this.width = width;<br />
this.height = height;<br />
}<br />
public void paint(Graphics g) {<br />
g.setColor(0,0,255);<br />
g.fillRect(x, y, width, height);<br />
}<br />
public void update() {<br />
// change x position according the speed<br />
x += speedX;<br />
// check if world bounds are reached<br />
if (x maxLimit){<br />
x = maxLimit – width;<br />
}<br />
}<br />
}<br />
Đối với Brick:<br />
public class Brick extends Entity {<br />
boolean active = true;<br />
public Brick(int color){<br />
this.color = color;<br />
}<br />
public void paint(Graphics g) {<br />
// only paints if still active<br />
if (active){<br />
g.setColor(color);<br />
g.fillRect(x, y, width, height);<br />
}<br />
}<br />
public void update() {<br />
// the bricks don’t move<br />
<br />
<br />
<br />
14<br />
}<br />
}<br />
Bây giờ, tạo và cấu hình các lớp này trên lớp Canvas. Tạo phương thức init() trên lớp Canvas:<br />
public void init(){<br />
// resets lifes<br />
lifes = 3;<br />
// resets score<br />
score = 0;<br />
// resets time<br />
time = 0;<br />
// bricks hit<br />
bricksHit = 0;<br />
// create a pad<br />
pad = new Pad(getWidth()/10,getWidth()/10/4);<br />
pad.x = (this.getWidth()pad.width) / 2;<br />
pad.y = this.getHeight() – (2*pad.height);<br />
pad.maxLimit = getWidth();<br />
pad.minLimit = 0;<br />
// create ball <br />
ball = new Ball(4);<br />
ball.x = getWidth() / 2;<br />
ball.y = getHeight() / 2;<br />
ball.speedX = 1;<br />
ball.speedY = 1;<br />
// set collision limits<br />
wallMinX = 0;<br />
wallMaxX = getWidth();<br />
wallMinY = 0;<br />
// to allow to get out of screen<br />
wallMaxY = getHeight() + 4 * ball.radium;<br />
// create bricks<br />
Brick brick;<br />
bricks = new Vector();<br />
for (int i=0; (i*(BRICK_WIDTH+2)) 0) {<br />
<br />
<br />
<br />
16<br />
// move right<br />
pad.speedX=1;<br />
} else {<br />
// don't move<br />
pad.speedX=0;<br />
}<br />
}<br />
Bây giờ nếu bạn chạy trò chơi trên mô phỏng, các bạn sẽ có một màn hình trò chơi thực sự, <br />
nơi bạn có thể chơi trò chơi riêng của bạn: Di chuyển pad, trúng ball và xóa tất cả các brick.<br />
Trong phần tiếp theo giải thích cách sử dụng hình ảnh để có được một trò chơi tìm kiếm tốt <br />
hơn.<br />
<br />
<br />
<br />
<br />
Phát triển game đơn giãn trên Mobile(P4) <br />
Trong bài trước, lớp GameCanvas được xây dựng với tất cả các tương tác giữa các element <br />
chính. Bây giờ, tất cả các element gameplay được xây dựng, đây là lúc cải thiện giao diện <br />
trực quan của trò chơi bằng cách sử dụng các tập tin hình ảnh thay vì sử dụng phương thức <br />
draw/fill trên đối tượng graphics để biểu diễn các thực thể trò chơi. Tôi đã tạo ra một số hình <br />
ảnh dựa trên các tài nguyên SpriteLib sẽ được sử dụng trong trò chơi.<br />
<br />
<br />
<br />
<br />
Image<br />
Để truy cập và hiển thị các hình ảnh trong MIDlet, ta phải sử dụng một phần tử hay lớp <br />
Image trong giao diện người dùng cấp thấp. Lớp này lưu trữ dữ liệu hình ảnh đồ họa độc lập <br />
với thiết bị hiển thị trong một bộ nhớ đệm offscreen. Các hình ảnh hoặc ở dạng Mutable hay <br />
Immutable phụ thuộc vào cách mà chúng được tạo ra. Thông thường, các hình ảnh dạng <br />
Immutable được ta ra thông qua việc load hình ảnh từ tài nguyên như file Jar hay mạng. Các <br />
hình ảnh dạng Immutable được tạo ra thông qua việc sử dụng phương thức tĩnh createImage() <br />
<br />
<br />
17<br />
của lớp Image. Tuy nhiên, một khi ảnh Immutable đã được tạo ra thì nó không thể được sửa <br />
đổi. Các hình ảnh dạng Mutable được tạo ra thông qua phương thức khởi dựng(Constructor) <br />
của lớp Image và lúc đó nó chỉ chứa các pixel trắng. Ứng dụng có thể biểu diễn image <br />
Mutable bằng cách gọi phương thức getGraphics() trên Image để có được đối tượng Graphics <br />
cho mục đích này.<br />
Trong game này, các hình ảnh Immutable đại diện cho các thực thể game. Bây giờ, ta hãy thay <br />
đổi lớp Pad và tạo ra đối tượng Image trong constructor của nó.<br />
Image image;<br />
public Pad() {<br />
try {<br />
image = Image.createImage(“/pad.png”);<br />
width = image.getWidth();<br />
height = image.getHeight();<br />
} catch (IOException e) {<br />
e.printStackTrace();<br />
}<br />
}<br />
Sau đó, cài đặt lại phương thức paint() của lớp Pad để vẽ hình ảnh:<br />
public void paint(Graphics g) {<br />
g.drawImage(image, x,y, Graphics.TOP | Graphics.LEFT);<br />
}<br />
Lưu ý: khi tạo hình ảnh, bạn nên sử dụng ảnh với định dạng PNG, bởi đây là định dạng thông <br />
dụng của các loại thiết bị. Những thiết bị đời mới có thể hỗ trợ nhiều định dạng khác như: <br />
JPEG, BMP…, tuy nhiên để an toàn bạn nên tạo ra file ảnh dạng PNG.<br />
Tiếp theo, ta sửa đổi lớp Ball như sau:<br />
Image image;<br />
public Ball() {<br />
try {<br />
image = Image.createImage(“/ball.png”);<br />
width = image.getWidth();<br />
height = image.getHeight();<br />
} catch (IOException e) {<br />
e.printStackTrace();<br />
}<br />
}<br />
public void paint(Graphics g) {<br />
g.drawImage(image, x,y, Graphics.TOP | Graphics.LEFT);<br />
}<br />
Bây giờ, nếu bạn chạy ứng dụng, bạn sẽ cố một Ball đẹp, tuy nhiên trong game sử dụng <br />
nhiều Brick với màu sắc khác nhau, và mỗi Brick được cắt từ một Image chính. Bạn có thể <br />
làm điều này thông qua sử dụng phương thức setClip() trong lớp Graphics, tuy nhiên từ MIDP <br />
2.0 trở đi, nó còn một hỗ trợ một lớp Sprite để ta làm điều này.<br />
Sprite<br />
<br />
<br />
18<br />
Một Sprite là một thuật ngữ chung trong game. Nó tham chiếu đến một phần tử trực quan <br />
được tạo ra bởi các Image, thường chuyển động và di chuyển xung quanh các phần tử khác <br />
một cách độc lập trong game. Lớp Sprite trong MIDP 2.0 đại diện cho khái niệm này. Nó cho <br />
phép tạo ra các Sprite dựa trên các hình ảnh với nhiều frame. Nó có thể thay đổi frame, điều <br />
khiển chuyển động và kiểm tra va chạm với các phần tử khác.<br />
Tất cả các khả năng này được sử dụng trong các thực thể của game, và ta hãy xem cách xây <br />
dựng Brick:<br />
public static int BRICK_FRAMES = 20;<br />
Image image = null;<br />
Sprite sprite = null;<br />
public Brick(){<br />
// load image<br />
image = Image.createImage(“/bricks.png”);<br />
// create the sprite with 20 frames, one for each brick<br />
sprite = new Sprite(image,image.getWidth()/BRICK_FRAMES, image.getHeight());<br />
width = sprite.getWidth();<br />
height = sprite.getHeight();<br />
}<br />
Mã này tạo ra một Sprite với 20 khung hình(frame), một cho mỗi Brick có sẵn trên hình ảnh. <br />
Trước khi vẽ Brick bạn cần thay đổi frame sẽ được sử dụng.<br />
public void paint(Graphics g) {<br />
if (active){<br />
sprite.nextFrame();<br />
sprite.setPosition(x, y);<br />
sprite.paint(g);<br />
}<br />
}<br />
Bây giờ, nếu bạn chạy Midlet, bạn sẽ được các Brick thay đổi màu sắc vào tất cả thời gian. <br />
Tuy nhiên, vấn đề đặt ra ở đây là hình ảnh bricks.png được load cho mỗi Brick được tạo ra, <br />
điều này thật sự không tối ưu. Sửa đổi mã để nó tối ưu như sau:<br />
public Brick(Image image, int numFrames, int frameSelected){<br />
sprite = new Sprite(image,image.getWidth()/numFrames,image.getHeight());<br />
// set frame<br />
sprite.setFrame(frameSelected);<br />
// get size for collision detection<br />
width = sprite.getWidth();<br />
height = sprite.getHeight();<br />
}<br />
public void paint(Graphics g) {<br />
if (active){<br />
sprite.setPosition(x, y);<br />
sprite.paint(g);<br />
<br />
<br />
<br />
19<br />
}<br />
}<br />
Bây giờ chỉ cần load một lần trong phương thức init () và chọn một frame sẽ được sử dụng <br />
trong vòng đời của một Brick.<br />
// create bricks<br />
Image bricksFrames = null;<br />
try {<br />
bricksFrames = Image.createImage(“/bricks.png”);<br />
} catch (IOException e) {<br />
e.printStackTrace();<br />
}<br />
Brick brick = new Brick(bricksFrames, 20, 0);<br />
bricks = new Vector();<br />
for (int i=0; (i*(brick.width+2))
ADSENSE
CÓ THỂ BẠN MUỐN DOWNLOAD
Thêm tài liệu vào bộ sưu tập có sẵn:
Báo xấu
LAVA
AANETWORK
TRỢ GIÚP
HỖ TRỢ KHÁCH HÀNG
Chịu trách nhiệm nội dung:
Nguyễn Công Hà - Giám đốc Công ty TNHH TÀI LIỆU TRỰC TUYẾN VI NA
LIÊN HỆ
Địa chỉ: P402, 54A Nơ Trang Long, Phường 14, Q.Bình Thạnh, TP.HCM
Hotline: 093 303 0098
Email: support@tailieu.vn