Để lưu điểm số high-score, trước tiên bạn cần hiểu Input Output trong MIDlet và cách sử dụng RecordStores.

Streams

Hãy bắt đầu với các operation IO đơn giãn. Các lớp mà thực thi chúng nằm trong gói java.io. Java ME chỉ có một ít các lớp sẵn có trong Java SE nhưng tất cả chúng đều rất hữu ích.

  • Input Stream, Output Stream: Các lớp cơ sở cho các byte streams kiểu binary.
  • ByteArrayInputStream, ByteArrayOutputStream: các mảng buffer stream trong bộ nhớ.
  • DataInputStream, DataOutputStream: Đọc và ghi các kiểu dữ liệu nguyên thủy Java(int, float, String, …) sang streams.
  • Reader, Writer: Các lớp cơ sở cho các stream character.
  • OutputStreamWriter, InputStreamReader: Các lớp đọc stream character sử dụng encodings.

Để học cách sử dụng các class này, ta tạo file settings để lưu trữ các options của game.

  • Số lượt chơi(life)
  • Tốc độ Ball
  • Thời gian hoàn tất một level
  • Số điểm khi ném trúng một Brick

Để thực thi file Settings này, ta tạo một file text settings.txt, nơi mà mỗi dòng của nó đại diện cho một setting. Tên setting và giá trị của nó được phân cách bởi dầu ‘=’.

ball_speed=2
start_lifes=4
level_time=100
brick_points=10

Thêm file này vào tài nguyên của project và đảm bảo rằng file này được lưu trong file JAR với các lớp. Để truy cập vào file này, bạn sử dụng hàm getResourceAsStream(String name) từ lớp Class. Phương thức này cho phép bạn truy cập vào bất kỳ file nào trong file JAR. Với đường dẫn root “/” tham chiếu tới root level của file JAR. Ví dụ, đoạn mã sau dùng để truy cập vào file settings.txt của file JAR:

InputStream is = this.getClass().getResourceAsStream(“/settings.txt”);
// read first byte
byte c= is.read();

Đoạn code trên trả về byte đầu tiên của file, nhưng cái thực sự bạn cần là đọc một dòng như là một stream character. Sử dụng InputStreamReader để thay thế và phân tích mỗi dòng cho mỗi cặp giá trị <key><value>.

public class Settings {
public Hashtable values;

public Settings(String file) {
values = new Hashtable(10);
read(file);
}

public String getSetting(String key){
return (String)(values.get(key));
}

/**
* Opens the file and reads all the settings to the hashtable
* @param file, the name of the file to read
*/

public void read(String file){
// open file
InputStream is = this.getClass().getResourceAsStream(file);
InputStreamReader isr = new InputStreamReader(is);
// create a buffer to store lines
StringBuffer lineBuffer = new StringBuffer();
int c;
try {
c = isr.read();
while(c != -1){
lineBuffer.append((char)c);
c = isr.read();
// checks for end of line character
if ( c == 10 || c==-1){
// cleans extra spaces or end of lines chars
String line = lineBuffer.toString().trim();
// splits the string using the = character
int pos = line.indexOf(“=”);
if (pos != -1){
// adds a new setting
String key = line.substring(0,pos);
String value = line.substring(pos+1);
values.put(key, value);
}
// clean buffer
lineBuffer.setLength(0);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Bây giờ, bạn chỉ cần sử dụng lớp này trong phương thức init() của lớp Canvas:

public void initSettings(){
Settings setting = new Settings(“/settings.txt”);
BALL_SPEED = Integer.parseInt(setting.getSetting(“ball_speed”));
MAX_LIFES = Integer.parseInt(setting.getSetting(“start_lifes”));
MAX_TIME = Integer.parseInt(setting.getSetting(“level_time”));
BRICK_SCORE = Integer.parseInt(setting.getSetting(“brick_points”));
}

public void init(){
initSettings();
[…]
}

Lúc này, file cấu hình đã được tạo ra, tuy nhiên phương thức getResourceAsStream chỉ cho phép đọc file lưu trữ trong file JAR nhưng ko cho phép ghi vào chúng.

RecordStore

Để ghi vào tập tin, bạn cần sử dụng lớp RecordStore. Lớp này cho phép tạo một database nhỏ, nơi mà bạn có thể lưu trữ hay lấy dữ liệu liên tục mỗi khi start MIDlet.

Class này được cài đặt trong gói javax.microedition.rms và nó cung cấp những phương thức  constructor tĩnh sau đây:

  • openRecordStore(String recordStoreName, boolean createIfNecessary)
  • openRecordStore(String recordStoreName, boolean createIfNecessary, int authmode, boolean writable)
  • openRecordStore(String recordStoreName, String vendorName, String suiteName)

Những phương pháp này cho phép tạo ra và / hoặc mở một RecordStore. Tên của các RecordStore phân biệt hoa thường và có chiều dài tối đa 32 ký tự. Một trong những tính năng đặc biệt mà cần phải được đưa vào account là setting authmode. Thiết lập này có hai giá trị:

  • AUTHMODE_PRIVATE: trong chế độ này, chỉ có MIDlet tạo ra RecordStore mới được phép truy cập nó.
  • AUTHMODE_ANY: Trong chế độ này, bất kỳ MIDlet nào đều có thể mở RecordStore, đọc dữ liệu từ nó và nếu setting writable thiết lập là true, thì có thể ghi dữ liệu vào nó.Điều này cho phép chia sẽ dữ liệu giữa các MIDlet, vì thế có thể 2 game cùng chia sẽ một bảng high-score. Các RecordStore được xác định duy nhất bởi tên tạo ra nó, MIDlet-Name, MIDlet-Vendor trong file JAD.

Sau khi bạn mở RecordStore, bạn có thể thêm record vào nó. Mỗi record được làm bằng một mảng byte và nó có một ID duy nhất khi nó được tạo ra. Sau khi bạn tạo một record, bạn có thể lấy dữ liệu của nó, thay đổi dữ liệu, và xóa nó. Các phương thức sau đây hỗ trợ các thao tác đó:

  • addRecord(byte[] data, int offset, int numBytes)
  • deleteRecord(int recordId)
  • setRecord(int recordId, byte[] newData, int offset, int numBytes)

Kích thước tối đa của RecordStore phụ thuộc vào thiết bị. Bạn có thể sử dụng phương thức getSizeAvailable(), tuy nhiên giá trị trả về của phương thức này không thực sự chính xác trong hầu hết các loại thiết bị.

Sử dụng RecordStore để lưu trữ high score như sau:

public void saveData() {
try {
// open records store options
RecordStore options = RecordStore.openRecordStore(“options”, true);
byte[] data = saveOptions();
// check if record store not empty
if (options.getNumRecords() != 0) {
// update the settings
options.setRecord(1, data, 0, data.length);
} else {
// adds the settings
options.addRecord(data, 0, data.length);
}
// closes the record store
options.closeRecordStore();
} catch (RecordStoreException ex) {
}
}

public byte[] saveOptions() {
// create a byte array stream to store data temporarily
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
try {
dos.writeBoolean(soundOn);
// write scores
for (int i =0; i < scores.length; i++){
dos.writeInt(scores[i].value);
dos.writeUTF(scores[i].name);
dos.writeLong(scores[i].when.getTime());
}
// push all the data to the byte array stream
dos.flush();
} catch (IOException ex) {

}
// returns bytes from stream
return baos.toByteArray();
}

Trong ví dụ trên, tên RecordStore là “options” được dùng để lưu trữ settings và high scores. Đối với quá trình lưu trữ, một DataOutputStream với một ByteArrayOutputStream để convert dữ liệu sang một mảng nhị phân.

Bây giờ bạn cần cài đặt việc đọc dữ liệu:

public void loadData() {
try {
RecordStore options = RecordStore.openRecordStore(“options”, true);
// check if record store not empty
if (options.getNumRecords() != 0) {
loadOptions(options.getRecord(1));
}
options.closeRecordStore();
} catch (RecordStoreException ex) {
}
}

public void loadOptions(byte[] data) {
// create a byte array stream to store data temporarily
ByteArrayInputStream bais = new ByteArrayInputStream(data);
// creates a data input stream to read from
DataInputStream dis = new DataInputStream(bais);
try {
soundOn = dis.readBoolean();
// read scores
for (int i = 0; i < scores.length; i++) {
int value = dis.readInt();
String name = dis.readUTF();
Date date = new Date(dis.readLong());
scores[i] = new Score(value, name, date);
}
dis.close();
} catch (IOException ex) {

}
}

Ở đoạn code trên, bạn chỉ cần mở một RecordStore và kiểm tra xem nó đã được tạo hay chưa. Nếu có, thì lấy dữ liệu và sử dụng DataInputStream kết hợp với ByteArrayInputStream.

Bây giờ, bạn chỉ cần đưa chúng vào phần startup và shutdown MIDlet.

public void initOptions() {
soundOn = true;
if (scores == null) {
scores = new Score[10];
for (int i = 0; i < scores.length; i++) {
scores[i] = new Score(0, “Empty”, new Date());
}
}
// loads data in record stores if available
loadData();
}

public void exit() {
// store high scores and setting to record store
saveData();
notifyDestroyed();
}

Đến lúc này, bạn đã có thể lưu trữ high score và settings của player. Trong bài tiếp theo, chúng ta sẽ được tìm hiểu cách sử dụng settings và thêm vào âm thanh cho game.

Download code ở đây.Chúc các bạn thành công.

Các bài liên quan:
Phát triển game đơn giãn trên Mobile(P1)
Phát triển game đơn giãn trên Mobile(P2)
Phát triển game đơn giãn trên Mobile(P3)
Phát triển game đơn giãn trên Mobile(P4)
Phát triển game đơn giãn trên Mobile(P6)
Phát triển game đơn giãn trên Mobile(P7)