Game của bạn sẽ hay hơn nếu như nó cho phép người chơi nhập tên nhân vật. Việc này làm họ thấy họ là một phần trong trò chơi. Sau đó, tên của người chơi lại xuất hiện trong các đoạn hội thoại khiến họ cảm thấy như đang được nói chuyện. Tiếp nữa, họ có thể lưu điểm số với tên của mình làm bằng chứng chiến thắng...
Những thứ trên thế nào? Bạn có thể làm với textbox dễ dàng? No, my young padawan =))) . Đúng, bạn có thể làm vậy nhưng game bạn sẽ bị ẩn đi khi textbox xuất hiện. Khi đó nó sẽ dùng theme của điện thoại và trông không giống như textbox đó là một phần của game. Lại một lần nữa, đây là vấn đề về thẩm mỹ.
Chúng ta dùng code của bài này để hiển thị chữ bitmap. Nếu bạn chưa học thì hãy bắt đầu bài đó trước. Hay nếu không thì bạn cũng có thể dùng phương thức drawChar( ) của Graphics thay vì của clsFont chúng ta tự định nghĩa.
Thiếu phím bấm
Trong bài này chúng ta viết chữ như những game arcade console ngày xưa. Chúng chỉ có phím điều khiển 4 chiều, 6 phím chức năng, 1 nút start và 1 nút reset nhưng chúng vẫn có thể cho người chơi nhập tên. Còn chiếc điện thoại chúng ta đang có dù nhiều phím bấm hơn nhưng không phải máy nào cũng có mã phím giống nhau.
Rất may là lớp GameCanvas cho chúng ta các phím cơ bản giống nhau ở hầu hết các loại máy điện thoại:
Chúng ta sẽ làm như sau:
Các giá trị
Bắt đầu sửa lớp clsCanvas. Thêm các biến toàn cục sau vào lớp này:
Biến String prText chứa đoạn text người chơi sẽ nhập vào và đồng thời phản hồi các kí tự người chơi chọn. Độ dài chuỗi gán cho prText cũng là độ dài tối đa lí tự có thể nhập và là các kí tự xuất hiện mặc định.
Biến nguyên prSelected giữ chỉ số của kí tự đang được chọn từ chuỗi prText.
Các biến prWidth và prHeight giữ chiều dài và chiều cao của kí tự. Chúng cũng được dùng trong clsCanvas.drawChar( ) để resize khung cắt.
Biến nguyên prSpacing xác định khoảng cách tính theo pixel giữa các kí tự. Giá trị được gán cho psSpacing là tổng độ rộng các kí tự và khoảng cách giữa chúng.
Thay thế một kí tự đơn
Tiếp theo, chúng ta viết một phương thức là replaceCharAt( ) cho phép chúng ta thay đổi một kí tự đơn lẻ trong chuỗi cho trước.
Phương thức này có 3 tham số:
Người chơi điều khiển
Chúng ta cũng cần phương thức nhận và phản hồi phím người dùng nhấn và chuyển đổi hiển thị con trỏ khi cần thiết. Thêm phương thức updatePompt( ) như sau
Phương thức này sử dụng giá trị Long currTick là thời gian hiện tại tính bằng milli giây. Giá trị này dùng kiểm tra xem thời gian qua giữa prStart và currTick có lớn hơn khoảng thời gian delay prDelay hay không. Nếu có, biến boolean prShow sẽ điều khiển con trỏ ẩn hay hiện qua giá trị true/false.
Chú ý rằng phương thức updatePrompt( ) chấp nhận chữ hoa, chữ nhỏ, và dấu cách. Không khó để thêm số vào hay thậm chí là toàn bộ các kí tự. Nhưng trong trường hợp này chỉ có chữ hoa.
Bạn cũng sẽ muốn giữ kí tự "dấu cách", vì người chơi có thể dùng nó khi tên họ ngắn mà kí tự bạn cho quá dài. Bằng cách này bạn có thể làm gọn các khoảng trống từ giá trị của prText khi người chơi đã nhập tên xong.
Hiển thị kí tự
Cuối cùng chúng ta viết phương thức drawPrompt( ) hiện chữ và con trỏ.
Phương thức này vẽ từng chữ trong prText và gạch dưới mỗi chữ. Con trỏ nhấp nháy dưới chữ đang được chọn.
Phương thức này có 3 tham số:
Thực hiện
Hãy chỉnh sửa code phương thức run( ) để dùng các phương thức vừa viết. Đầu tiên, lời gọi phương thức updatePrompt( ) bên dưới lời gọi checkKey( )
Sau đó sử dụng:
...và chạy thử để xem kết quả:
Cuối cùng là lớp clsCanvas hoàn chỉnh
Đến đây là hết các bài học của Devlin.
Những thứ trên thế nào? Bạn có thể làm với textbox dễ dàng? No, my young padawan =))) . Đúng, bạn có thể làm vậy nhưng game bạn sẽ bị ẩn đi khi textbox xuất hiện. Khi đó nó sẽ dùng theme của điện thoại và trông không giống như textbox đó là một phần của game. Lại một lần nữa, đây là vấn đề về thẩm mỹ.
Chúng ta dùng code của bài này để hiển thị chữ bitmap. Nếu bạn chưa học thì hãy bắt đầu bài đó trước. Hay nếu không thì bạn cũng có thể dùng phương thức drawChar( ) của Graphics thay vì của clsFont chúng ta tự định nghĩa.
Thiếu phím bấm
Trong bài này chúng ta viết chữ như những game arcade console ngày xưa. Chúng chỉ có phím điều khiển 4 chiều, 6 phím chức năng, 1 nút start và 1 nút reset nhưng chúng vẫn có thể cho người chơi nhập tên. Còn chiếc điện thoại chúng ta đang có dù nhiều phím bấm hơn nhưng không phải máy nào cũng có mã phím giống nhau.
Rất may là lớp GameCanvas cho chúng ta các phím cơ bản giống nhau ở hầu hết các loại máy điện thoại:
- Phím 2 - Nút UP
- Phím 8 - Nút DOWN
- Phím 4 - Nút LEFT
- Phím 6 - Nút RIGHT
Chúng ta sẽ làm như sau:
- LEFT - Chọn kí tự bên trái
- RIGHT - Chọn kí tự bên phải
- UP - Cuộn kí tự lên trên
- DOWN - Cuộn kí tự xuống dưới
- FIRE - Chấp nhận thay đổi
Các giá trị
Bắt đầu sửa lớp clsCanvas. Thêm các biến toàn cục sau vào lớp này:
private clsFont myFont; // this will hold the resulting text private String prText = "AAAAAA"; // the currently selected character private int prSelected = 0; // width and height of the characters private int prWidth = 9; private int prHeight = 10; // character spacing - includes width of character private int prSpacing = 12; // vars for timing the blinking cursor private long prStart = 0; private long prDelay = 100; private boolean prShow = true; /** Creates a new instance of clsCanvas */ public clsCanvas(midMain m) {
Biến String prText chứa đoạn text người chơi sẽ nhập vào và đồng thời phản hồi các kí tự người chơi chọn. Độ dài chuỗi gán cho prText cũng là độ dài tối đa lí tự có thể nhập và là các kí tự xuất hiện mặc định.
Biến nguyên prSelected giữ chỉ số của kí tự đang được chọn từ chuỗi prText.
Các biến prWidth và prHeight giữ chiều dài và chiều cao của kí tự. Chúng cũng được dùng trong clsCanvas.drawChar( ) để resize khung cắt.
Biến nguyên prSpacing xác định khoảng cách tính theo pixel giữa các kí tự. Giá trị được gán cho psSpacing là tổng độ rộng các kí tự và khoảng cách giữa chúng.
Thay thế một kí tự đơn
Tiếp theo, chúng ta viết một phương thức là replaceCharAt( ) cho phép chúng ta thay đổi một kí tự đơn lẻ trong chuỗi cho trước.
public String replaceCharAt(String s, int pos, char c) {
if (pos < (s.length() - 1)){
return s.substring(0, pos) + c + s.substring(pos + 1);
} else {
return s.substring(0, pos) + c;
}
}
public void run() {
Phương thức này có 3 tham số:
- String s - chuỗi đã cho
- int pos - vị trí kí tự được thay đổi
- Char c - kí tự thay thế
Người chơi điều khiển
Chúng ta cũng cần phương thức nhận và phản hồi phím người dùng nhấn và chuyển đổi hiển thị con trỏ khi cần thiết. Thêm phương thức updatePompt( ) như sau
public void updatePrompt(long currTick){ // get text length int len = prText.length(); // get current selected characters ASCII value int prOrd = (int)prText.charAt(prSelected); // is it time to change the cursors visibility if ((currTick - prStart) >= prDelay){ // update starting time prStart = currTick; // toggle cursor visibility prShow = !prShow; } if (isDown[leftKey]){ // if not first character if (prSelected > 0){ // select previous character in string prSelected--; } } else if (isDown[rightKey]){ // if not last character if (prSelected < (len - 1)){ // select next character in string prSelected++; } } else if (isDown[upKey]){ if (prOrd == 97){ // small leter a prOrd = 90; // jump to capital letter Z } else if (prOrd == 65){ // capital letter A prOrd = 32; // jump to space character } else if (prOrd == 32){ // space character prOrd = 122; // jump to small leter z } else if ((prOrd > 97) || (prOrd > 65)){ prOrd--; // previous character } // replace the selected character with the new character prText = replaceCharAt(prText, prSelected, (char)prOrd); } else if (isDown[downKey]){ if (prOrd == 32){ // space character prOrd = 65; // jump to capilat letter A } else if (prOrd == 90){ // capital letter Z prOrd = 97; // jump to small letter a } else if (prOrd == 122){ // small letter z prOrd = 32; // jump to space character } else if ((prOrd < 90) || (prOrd < 122)){ prOrd++; // next character } // replace the selected character with the new character prText = replaceCharAt(prText, prSelected, (char)prOrd); } } public void run() {
Phương thức này sử dụng giá trị Long currTick là thời gian hiện tại tính bằng milli giây. Giá trị này dùng kiểm tra xem thời gian qua giữa prStart và currTick có lớn hơn khoảng thời gian delay prDelay hay không. Nếu có, biến boolean prShow sẽ điều khiển con trỏ ẩn hay hiện qua giá trị true/false.
Chú ý rằng phương thức updatePrompt( ) chấp nhận chữ hoa, chữ nhỏ, và dấu cách. Không khó để thêm số vào hay thậm chí là toàn bộ các kí tự. Nhưng trong trường hợp này chỉ có chữ hoa.
Bạn cũng sẽ muốn giữ kí tự "dấu cách", vì người chơi có thể dùng nó khi tên họ ngắn mà kí tự bạn cho quá dài. Bằng cách này bạn có thể làm gọn các khoảng trống từ giá trị của prText khi người chơi đã nhập tên xong.
Hiển thị kí tự
Cuối cùng chúng ta viết phương thức drawPrompt( ) hiện chữ và con trỏ.
public void drawPrompt(Graphics g, int x, int y){
// get length of the string
int len = prText.length();
// loop through the characters
for (int i = 0; i < len; i++){
// get ASCII value
int cIndex = (int)prText.charAt(i);
// compute next drawing position - X
int cx = x + (i * prSpacing);
// draw the character
myFont.drawChar(g, cIndex, cx, y, prWidth, prHeight);
// compute cusor position - Y
int cy = y + 6;
// if current char is selected
if (i == prSelected){
// if cursor shoud be visible
if (prShow) {
//draw the cursor using char 95 or underscore character
myFont.drawChar(g, 95, cx, cy, prWidth, prHeight);
}
} else {
//draw the cursor using char 95 or underscore character
myFont.drawChar(g, 95, cx, cy, prWidth, prHeight);
}
}
}
public void run() {
Phương thức này vẽ từng chữ trong prText và gạch dưới mỗi chữ. Con trỏ nhấp nháy dưới chữ đang được chọn.
Phương thức này có 3 tham số:
- Graphics g - Đối tượng Graphics dùng vẽ chữ.
- int x - Tọa độ x bắt đầu vẽ
- int y - Tọa độ y bắt đầu vẽ
Thực hiện
Hãy chỉnh sửa code phương thức run( ) để dùng các phương thức vừa viết. Đầu tiên, lời gọi phương thức updatePrompt( ) bên dưới lời gọi checkKey( )
checkKeys(iKey, lCurrTick);
updatePrompt(lCurrTick);
if (isDown[fireKey]){
Sau đó sử dụng:
g.fillRect(0, 0, screenW, screenH); myFont.drawString(g, "Enter your name:", 10, 20); drawPrompt(g, 10, 40); myFont.drawString(g, "LEFT/RIGHT - move cursor", 10, 80); myFont.drawString(g, "UP/DOWN - change letter", 10, 100); myFont.drawString(g, "Hello " + prText.trim() + "!", 10, 140); flushGraphics();
...và chạy thử để xem kết quả:
Cuối cùng là lớp clsCanvas hoàn chỉnh
package MyGame;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;
public class clsCanvas extends GameCanvas implements Runnable {
// key repeat rate in milliseconds
public static final int keyDelay = 250;
//key constants
public static final int upKey = 0;
public static final int leftKey = 1;
public static final int downKey = 2;
public static final int rightKey = 3;
public static final int fireKey = 4;
//key states for up, left, down, right, and fire key
private boolean[] isDown = {
false, false, false, false, false
};
//last time the key changed state
private long[] keyTick = {
0, 0, 0, 0, 0
};
//lookup table for key constants :P
private int[] keyValue = {
GameCanvas.UP_PRESSED, GameCanvas.LEFT_PRESSED,
GameCanvas.DOWN_PRESSED, GameCanvas.RIGHT_PRESSED,
GameCanvas.FIRE_PRESSED
};
private boolean isRunning = true;
private Graphics g;
private midMain fParent;
private clsFont myFont;
// this will hold the resulting text
private String prText = "AAAAAA";
// the currently selected character
private int prSelected = 0;
// width and height of the characters
private int prWidth = 9;
private int prHeight = 10;
// character spacing - includes width of character
private int prSpacing = 12;
// vars for timing the blinking cursor
private long prStart = 0;
private long prDelay = 100;
private boolean prShow = true;
/** Creates a new instance of clsCanvas */
public clsCanvas(midMain m) {
super(true);
fParent = m;
setFullScreenMode(true);
}
public void start(){
Thread runner = new Thread(this);
runner.start();
}
public void load(){
try{
// load the images here
}catch(Exception ex){
// exit the app if it fails to load the image
isRunning = false;
return;
}
myFont = new clsFont();
myFont.load("/images/fonts.png");
}
public void unload(){
// make sure the object gets destroyed
myFont.unload();
myFont = null;
}
public void checkKeys(int iKey, long currTick){
long elapsedTick = 0;
//loop through the keys
for (int i = 0; i < 5; i++){
// by default, key not pressed by user
isDown[i] = false;
// is user pressing the key
if ((iKey & keyValue[i]) != 0){
elapsedTick = currTick - keyTick[i];
//is it time to toggle key state?
if (elapsedTick >= keyDelay){
// save the current time
keyTick[i] = currTick;
// toggle the state to down or pressed
isDown[i] = true;
}
}
}
}
public String replaceCharAt(String s, int pos, char c) {
if (pos < (s.length() - 1)){
return s.substring(0, pos) + c + s.substring(pos + 1);
} else {
return s.substring(0, pos) + c;
}
}
public void updatePrompt(long currTick){
// get text length
int len = prText.length();
// get current selected characters ASCII value
int prOrd = (int)prText.charAt(prSelected);
// is it time to change to cursors visibility
if ((currTick - prStart) >= prDelay){
// update starting time
prStart = currTick;
// toggle cursor visibility
prShow = !prShow;
}
if (isDown[leftKey]){
// if not first character
if (prSelected > 0){
// select previous character in string
prSelected--;
}
} else if (isDown[rightKey]){
// if not last character
if (prSelected < (len - 1)){
// select next character in string
prSelected++;
}
} else if (isDown[upKey]){
if (prOrd == 97){ // small leter a
prOrd = 90; // jump to capital letter Z
} else if (prOrd == 65){ // capital letter A
prOrd = 32; // jump to space character
} else if (prOrd == 32){ // space character
prOrd = 122; // jump to small leter z
} else if ((prOrd > 97) || (prOrd > 65)){
prOrd--; // previous character
}
// replace the selected character with the new character
prText = replaceCharAt(prText, prSelected, (char)prOrd);
} else if (isDown[downKey]){
if (prOrd == 32){ // space character
prOrd = 65; // jump to capilat letter A
} else if (prOrd == 90){ // capital letter Z
prOrd = 97; // jump to small letter a
} else if (prOrd == 122){ // small letter z
prOrd = 32; // jump to space character
} else if ((prOrd < 90) || (prOrd < 122)){
prOrd++; // next character
}
// replace the selected character with the new character
prText = replaceCharAt(prText, prSelected, (char)prOrd);
}
}
public void drawPrompt(Graphics g, int x, int y){
// get length of the string
int len = prText.length();
// loop through the characters
for (int i = 0; i < len; i++){
// get ASCII value
int cIndex = (int)prText.charAt(i);
// compute next drawing position - X
int cx = x + (i * prSpacing);
// draw the character
myFont.drawChar(g, cIndex, cx, y, prWidth, prHeight);
// compute cusor position - Y
int cy = y + 6;
// if current char is selected
if (i == prSelected){
// if cursor shoud be visible
if (prShow) {
//draw the cursor using char 95 or underscore character
myFont.drawChar(g, 95, cx, cy, prWidth, prHeight);
}
} else {
//draw the cursor using char 95 or underscore character
myFont.drawChar(g, 95, cx, cy, prWidth, prHeight);
}
}
}
public void run() {
int iKey = 0;
int screenW = getWidth();
int screenH = getHeight();
long lCurrTick = 0; // current system time in milliseconds;
String sName = "";
load();
g = getGraphics();
while(isRunning){
lCurrTick = System.currentTimeMillis();
iKey = getKeyStates();
checkKeys(iKey, lCurrTick);
updatePrompt(lCurrTick);
if (isDown[fireKey]){
isRunning = false;
}
//restore the clipping rectangle to full screen
g.setClip(0, 0, screenW, screenH);
//set drawing color to black
g.setColor(0x000000);
//fill the screen with blackness
g.fillRect(0, 0, screenW, screenH);
myFont.drawString(g, "Enter your name:", 10, 20);
drawPrompt(g, 10, 40);
myFont.drawString(g, "LEFT/RIGHT - move cursor", 10, 80);
myFont.drawString(g, "UP/DOWN - change letter", 10, 100);
myFont.drawString(g, "Hello " + prText.trim() + "!", 10, 140);
flushGraphics();
try{
Thread.sleep(30);
} catch (Exception ex){
}
}
g = null;
unload();
fParent.destroyApp(false);
fParent = null;
}
}
Đến đây là hết các bài học của Devlin.
Chúc các bạn thành công !!