J2ME Wyklad


Programowanie na komórki
J2ME, MIDP 2.0
Adam Sawicki
http://regedit.gamedev.pl/
sawickiap@poczta.onet.pl
Grudzień 2007
W prezentacji wykorzystane są fragmenty dokumentacji MIDP 2.1 firmy Sun.
Programowanie na komórki

W czym pisać programy na komórki?

To zależy od klasy telefonu.
(nic) Java, C++ i inne...
Java
2
Agenda

Wprowadzenie do J2ME

Midlet

GUI

Wejście-wyjście

Internet

Grafika 2D

Programowanie gier 2D

Kilka drobiazgów

Baza danych

Multimedia

Rozszerzenia
3
Wprowadzenie do J2ME
J2ME
Wireless Toolkit
Język Java w J2ME
4
Co to jest J2ME?

Skróty, dużo skrótów...

J2ME  Java 2 Platform, Micro Edition

Platforma Java na komórki (inne to J2SE i J2EE)

CLDC  Connected Limited Device Configuration

MIDP  Mobile Information Device Profile

JSR  Java Specification Request

Specyfikacje dodatków (np. JSR-184 to M3G)

W praktyce używamy dokumentacji MIDP, ona
zawiera też dokumentację CLDC

MIDP 1.0  CLCD 1.0, MIDP 2.0  CLDC 1.1
5
Jakie jest J2ME?

Ten sam język Java

Używamy tego samego kompilatora javac

Zupełnie nowa biblioteka standardowa

Podajemy niestandardowy classpath

Liczne ograniczenia sprzętu

Mało pamięci RAM

Wolny procesor

Niska rozdzielczość wyświetlacza, mało kolorów

Kiepska klawiatura

Duże zróżnicowanie klawiatur, ekranów, wydajności
6
Co jest potrzebne?

JDK  Java Development Kit

Zawiera w sobie JRE - Java Runtime Environment

Java Wireless Toolkit

Edytor lub IDE

jEdit, Notatnik, Vim, Emacs, ...

NetBeans, Eclipse, ...
Wszystko dostępne za darmo (java.sun.com).
7
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class HelloWorld
extends MIDlet
Hello World!
implements CommandListener
{
public HelloWorld()
{
}
//////
// Implementacja MIDlet
protected void startApp()
throws MIDletStateChangeException
{
Alert alert = new Alert("Hello World!");
alert.setCommandListener(this);
alert.addCommand(new Command("Koniec", Command.EXIT, 0));
Display display = Display.getDisplay(this);
display.setCurrent(alert);
}
protected void pauseApp()
{
}
protected void destroyApp(boolean unconditional)
throws MIDletStateChangeException
{
}
//////
// Implementacja CommandListener
public void commandAction(Command c, Displayable d)
{
if (c.getCommandType() == Command.EXIT)
notifyDestroyed();
}
8
}
Ręczne budowanie midletu

Midlet można zbudować ręcznie z wiersza
poleceń

Kompilacja: javac (JDK)

Preweryfikacja: preverify (WTK)

Napisanie pliku MANIFEST.MF

Utworzenie paczki: jar (JDK)

Napisanie pliku JAD

Emulacja: emulator (WTK)

Wysłanie na komórkę

...ale nie trzeba, można prościej!
9
Wireless Toolkit

Sun Java(TM) Wireless Toolkit for CLDC

Tytułowy program pakietu Wireless Toolkit

Proste IDE do midletów
10
Wireless Toolkit

Operuje na projektach

Przechowuje je w Documents and
Settings\LOGIN\j2mewtk\apps\NAZWA

Zakłada kilka katalogów

src  pliki JAVA

res  obrazki, dzwięki i inne zasoby

lib  dodatkowe biblioteki

classes, tmpclasses  pliki tymczasowe

bin  pliki wynikowe JAR, JAD

Polecenia

Kompilacja: Build

Emulacja: Run

Utworzenie paczki: Create Package

Nie posiada edytora!
11
Język Java

Dostępny mamy normalny język Java

Klasy, wyjątki

byte, short, int, long, boolean, char itd.

String, StringBuffer

float, double  od wersji MIDP 2.0

NIE MA generics, enum itp.  to jest Java 1.2
12
Wielowątkowość

J2ME to platforma ograniczona, ale
nowoczesna.

Można, a nawet trzeba pisać wielowątkowo i
zajmować się synchronizacją.

JEST dostęna wielowątkowość:

Słowo kluczowe synchronized

Metody klasy bazowej Object: wait, notify, notifyAll

Klasa Thread, interfejs Runnable
13
Biblioteka standardowa

Podstawowe typy

java.lang  Boolean, Character, Integer itd.

java.lang  String, StringBuffer

Matematyka

java.lang.Math  abs, max, sin, floor, sqrt, ...

BRAKUJE exp, log, pow, atan2

java.util.Random  generator liczb pseudolosowych

Struktury danych

java.util  Vector, Stack, Hashtable
14
Midlet
javax.microedition.midlet
15
Midlet

Midlet  program na komórkę w J2ME

Analogicznie do: aplet, servlet

NIE MA kolejki komunikatów ani jawnej pętli jak w Windows API

JEST sterowany zdarzeniami, jak programy w Delphi czy C#

Posiada STAN
16
Midlet  szkielet klasy
import javax.microedition.midlet.*;
public class HelloWorld extends MIDlet
{
public HelloWorld()
{
}
protected void startApp()
throws MIDletStateChangeException
{
}
protected void pauseApp()
{
}
protected void destroyApp(boolean unconditional)
throws MIDletStateChangeException
{
}
}
17
Midlet - zdarzenia

startApp()

Program staje się aktywny

Dokonać inicjalizacji, wczytać zasoby, pokazać coś na ekranie

Jeśli nie udaje się uaktywnić, rzucić wyjątek
MIDletStateChangeException

Jeśli nieodwracalny błąd, wywołać notifyDestroyed

pauseApp()

Program staje się nieaktywny

Zwolnić wszystkie zasoby zajmujące dużo procesora lub pamięci

destroyApp(boolean unconditional)

Program zostaje wyłączony

Zapisać trwałe dane

Jeśli odmawia wyłączenia, rzucić wyjątek
MIDletStateChangeException (tylko jeśli unconditional == false)
18
Midlet  zmiana stanu

notifyDestroyed()

Wywołać jeśli program chce się zakończyć

destroyApp nie zostanie już wywołane!

notifyPaused()

Wywołać jeśli program chce się stać nieaktywny

resumeRequest()

Wywołać jeśli program chce się stać aktywny

platformRequest(String URL)

Wywołać aby system obsłużył podany URL, np.
tel:NUMER
19
GUI
javax.microedition.lcdui
20
GUI - Klasy
Pakiet:
javax.microedition.lcdui
21
Wysoki i niski poziom

API wysokiego poziomu

Zawiera: kontrolki GUI i polecenia

Dla: aplikacji biznesowych

Abstrakcyjne, niezależne od rozdzielczości

Rysowane wg stylu danego telefonu

API niskiego poziomu

Zawiera: możliwość rysowania i reakcji na wejście z
klawiatury

Dla: gier

Zależne od wyświetlacza i klawiatury danego
telefonu

Wygląd i obsługa implementowane samodzielnie
22
Alert

Alert  ekran z komunikatem tekstowym

Coś jak znany z Windowsa MessageBox :)
// Utwórz ekran komunikatu
Alert alert = new Alert(
// title
"Błąd",
"Nie można otworzyć pliku.", // alertText
// alertImage
null,
// alertType
AlertType.ERROR);
// Pobierz singleton wyświetlacza
Display display = Display.getDisplay(this);
// Pokaż komunikat na wyświetlaczu
display.setCurrent(alert);
23
Command i CommandListener

Zaimplementować w jakiejś klasie L interfejs CommandListener

Utworzyć jakiś ekran E typu pochodnego od Displayable

Wywołać:
E.setCommandListener(L_obj);

Wywołać dowolną liczbę razy:
E.addCommand(new Command(label, commandType, priority));

Pokazać E na wyświetlaczu:
display.setCurrent(E);

Czekać na zdarzenia...
24
Command i CommandListener
public class TestMidlet extends MIDlet implements CommandListener
{
protected void startApp() throws MIDletStateChangeException
{
Alert alert = new Alert("Wyjście", "Czy na pewno chcesz wyjść?",
null, null);
alert.setCommandListener(this);
alert.addCommand(new Command("Zakończ", Command.EXIT, 0));
alert.addCommand(new Command("Wróć", Command.BACK, 0));
Display display = Display.getDisplay(this);
display.setCurrent(alert);
}
public void commandAction(Command c, Displayable d)
{
if (c.getCommandType() == Command.EXIT)
notifyDestroyed();
else if (c.getCommandType() == Command.BACK)
Display.getDisplay(this).setCurrent(m_OtherScreen);
}
25
Command

label  nazwa

commandType  semantyka (znaczenie)
Stałe: Command.BACK, CANCEL, EXIT, HELP,
OK, SCREEN, STOP, do pozostałych ITEM

priority  określa kolejność
26
Command

Telefon sam określa rozmieszczenie i sposób
uruchamiania poleceń

Kiedy poleceń jest dużo, zapewnia menu
27
Obrazek

Obrazek w pamięci reprezentuje klasa Image

Obrazek może być Mutable lub Immutable

Jedyny na pewno wspierany format to PNG
28
Obrazek - wykorzystanie
try
{
Image image = Image.createImage("/Ikonka.png");
Alert alert = new Alert(
"Wyjście",
"Czy na pewno chcesz wyjść?",
Plik w podkatalogu res/
image,
null);
alert.setCommandListener(this);
alert.addCommand(new Command("Zakończ", Command.EXIT, 0));
alert.addCommand(new Command("Wróć", Command.BACK, 0));
Display display = Display.getDisplay(this);
display.setCurrent(alert);
}
catch (java.io.IOException e)
{
e.printStackTrace();
}
29
Lista
List m_List;
protected void startApp() throws MIDletStateChangeException
{
m_List = new List("Lista", Choice.EXCLUSIVE);
m_List.append("Opcja 1", null);
m_List.append("Opcja 2", null);
m_List.append("Opcja 3", null);
m_List.setCommandListener(this);
m_List.addCommand(new Command("Zakończ", Command.EXIT, 0));
m_List.addCommand(new Command("OK", Command.OK, 0));
Display display = Display.getDisplay(this);
display.setCurrent(m_List);
}
public void commandAction(Command c, Displayable d)
{
if (c.getCommandType() == Command.EXIT)
notifyDestroyed();
else if (c.getCommandType() == Command.OK)
GoFurther(m_List.getSelectedIndex());
}
30
Lista wielokrotnego wyboru
List m_List;
protected void startApp() throws MIDletStateChangeException
{
m_List = new List("Lista", Choice.MULTIPLE);
m_List.append("Opcja 1", null);
m_List.append("Opcja 2", null);
m_List.append("Opcja 3", null);
m_List.setCommandListener(this);
m_List.addCommand(new Command("Zakończ", Command.EXIT, 0));
m_List.addCommand(new Command("OK", Command.OK, 0));
Display display = Display.getDisplay(this);
display.setCurrent(m_List);
}
public void commandAction(Command c, Displayable d)
{
if (c.getCommandType() == Command.EXIT)
notifyDestroyed();
else if (c.getCommandType() == Command.OK)
GoFurther(m_List.isSelected(0), m_List.isSelected(1), m_List.isSelected(2));
}
31
Lista jako menu
List m_List;
protected void startApp() throws MIDletStateChangeException
{
m_List = new List("Lista", Choice.IMPLICIT);
m_List.append("Opcja 1", null);
m_List.append("Opcja 2", null);
m_List.append("Opcja 3", null);
m_List.setCommandListener(this);
m_List.setSelectCommand(new Command("Wybierz", Command.ITEM, 0));
Display display = Display.getDisplay(this);
display.setCurrent(m_List);
}
public void commandAction(Command c, Displayable d)
{
if (c.getCommandType() == Command.ITEM)
GoFurther(m_List.getSelectedIndex());
}
32
Czcionka
Font font = Font.getFont(
Font.FACE_MONOSPACE,
Font.STYLE_BOLD,
Font.SIZE_LARGE);
m_List = new List("Lista", Choice.IMPLICIT);
m_List.append("Opcja 1", null);
m_List.append("Opcja 2", null);
m_List.append("Opcja 3", null);
m_List.setFont(0, font);
m_List.setFont(1, font);
m_List.setFont(2, font);
33
Czcionka

face: size:
FACE_MONOSPACE SIZE_LARGE
FACE_PROPORTIONAL SIZE_MEDIUM
FACE_SYSTEM SIZE_SMALL

Nie zawsze dostajemy
style:
pożądane, różniące się
STYLE_BOLD
czcionki!
STYLE_ITALIC
STYLE_PLAIN
STYLE_UNDERLINED
34
TextBox
TextBox m_TextBox;
protected void startApp() throws MIDletStateChangeException
{
m_TextBox = new TextBox("Login", "", 32, 0);
m_TextBox.setCommandListener(this);
m_TextBox.addCommand(new Command("OK", Command.OK, 0));
Display display = Display.getDisplay(this);
display.setCurrent(m_TextBox);
}
public void commandAction(Command c, Displayable d)
{
if (c.getCommandType() == Command.OK)
GoFurther(m_TextBox.getString());
}
35
TextBox - Constraints

Ograniczenia:
TextField.ANY
TextField.EMAILADDR
TextField.NUMERIC
TextField.PHONENUMBER
TextField.URL
TextField.DECIMAL

Modyfikatory:
TextField.PASSWORD
TextField.UNEDITABLE
TextField.SENSITIVE
TextField.NON_PREDICTIVE
TextField.INITIAL_CAPS_WORD
TextField.INITIAL_CAPS_SENTENCE
36
Formularz
m_Form = new Form("Formularz");
Item item;
item = new StringItem("Twoje dane", "Tu wpisz swoje dane", 0);
item.setLayout(Item.LAYOUT_2 | Item.LAYOUT_NEWLINE_AFTER);
m_Form.append(item);
item = new TextField("Imię", "Twoje imię", 32, 0);
item.setLayout(Item.LAYOUT_2 | Item.LAYOUT_NEWLINE_AFTER);
m_Form.append(item);
item = new TextField("Nazwisko", "Twoje nazwisko", 32, 0);
item.setLayout(Item.LAYOUT_2 | Item.LAYOUT_NEWLINE_AFTER);
m_Form.append(item);
m_Form.setCommandListener(this);
m_Form.addCommand(new Command("OK", Command.OK, 0));
Display display = Display.getDisplay(this);
display.setCurrent(m_Form);

Kilka różnych kontrolek na jednym ekranie.
37
Kontrolki statyczne
m_Form = new Form("Formularz");
Image image;
try
{
image = Image.createImage("/Ikonka2.png");
}
catch (java.io.IOException e)
{
e.printStackTrace();
image = null;
}
m_Form.append(new StringItem(null, "Napis...", Item.PLAIN));
m_Form.append(new Spacer(32, 32));
m_Form.append(new ImageItem(null, image, ImageItem.LAYOUT_DEFAULT, null));
m_Form.setCommandListener(this);
m_Form.addCommand(new Command("OK", Command.OK, 0));
Display display = Display.getDisplay(this);
display.setCurrent(m_Form);
38
TextField

Pole do wprowadzania tekstu

Flagi takie same jak dla TextBox
m_Form.append(new TextField(
"URL",
"http://www.google.pl/",
256,
TextField.URL));
39
Gauge

Suwak do wybierania wartości liczbowej
m_Form.append(new Gauge(
"Głośność", // label
true, // interactive
// maxValue
9,
5)); // initialValue
40
DateTime

Wybór daty i/lub czasu
m_Form.append(new DateField(
"Data", DateField.DATE));
m_Form.append(new DateField(
"Czas", DateField.TIME));
m_Form.append(new DateField(
"Data i czas", DateField.DATE_TIME));
41
ChoiceGroup

Lista, podobnie jak ekran List
String[] elements = {
"Opcja 1", "Opcja 2", "Opcja 3"
};
m_Form.append(new ChoiceGroup(
"ChoiceGroup", // label
Choice.EXCLUSIVE, // choiceType
// elements
elements,
// imageElements
null));
42
ChoiceGroup - rodzaje

Choice.EXCLUSIVE

Choice.MULTIPLE

Choice.POPUP
43
Alert raz jeszcze - Indicator

Inne zastosowanie kontrolki Gauge

Pokazywanie postępu wewnątrz ekranu Alert
Alert alert = new Alert(
"Loading",
"Trwa ładowanie gry, proszę czekać...",
null,
AlertType.INFO);
alert.setIndicator(new Gauge(
null, // label
Musi tak być
false, // interactive
9, // maxValue
Flagi: INDEFINITE,
0)); // initialValue
CONTINUOUS_IDLE,
alert.setTimeout(Alert.FOREVER);
CONTINUOUS_RUNNING,
INCREMENTAL_IDLE,
INCREMENTAL_UPDATING
44
Ticker

Tekst przelatujący u góry ekranu

Dostępny na dowolnym ekranie  metoda setTicker
klasy Displayable
Alert alert = new Alert(
"Loading",
"Trwa ładowanie gry, proszę czekać...",
null,
AlertType.INFO);
alert.setTimeout(Alert.FOREVER);
Ticker ticker = new Ticker("REKLAMA!!!");
alert.setTicker(ticker);
45
Timer, TimerTask

java.util.Timer  pozwala zakolejkować
zadania do wykonania za/co określony czas

java.util.TimerTask  klasa bazowa dla zadań

Każdy Timer to osobny wątek

Treść zadania jest wykonywana w osobnym wątku.

Czy nie warto czasem napisać własny wątek
zamiast używać Timera?
46
Timer - przykład
Gauge m_Gauge; private class MyTask
java.util.Timer m_Timer;
extends java.util.TimerTask
{
protected void startApp()
public void run()
throws MIDletStateChangeException {
{ m_Gauge.setValue(
m_Gauge.getValue() + 1);
Alert alert = new Alert(
"Loading",
}
"Trwa ładowanie gry, proszę czekać...", }
null,
AlertType.INFO);
alert.setTimeout(Alert.FOREVER);
m_Gauge = new Gauge(null, false, 9, 0);
alert.setIndicator(m_Gauge);
Display display = Display.getDisplay(this);
display.setCurrent(alert);
m_Timer = new java.util.Timer();
m_Timer.schedule(new MyTask(), 500, 500);
}
47
Wejście-wyjście
java.io
48
Wejście-wyjście

Pakiet java.io - strumienie
49
Wejście-wyjście

InputStream - abstrakcyjna klasa bazowa do odczytywania danych binarych
jako bajty

OutputStream - abstrakcyjna klasa bazowa do zapisywania bajtów jako
dane binarne

DataInput - interfejs do odczytywania danych binarnych jako różne typy

DataInputStream - nakładka na InputStream do odczytywania danych
binarnych jako różne typy

DataOutput - interfejs do zapisywania różnych typów jako dane binarne

DataOutputStream - nakładka na OutputStream do zapisywania
różnych typów jako dane binarne

Writer  abstrakcyjna klasa bazowa do zapisywania znaków

OutputStreamWriter - nakładka na strumień zapisująca do niego znaki

Reader - abstrakcyjna klasa bazowa do odczytywania znaków

InputStreamReader - nakładka na strumień odczytująca z niego znaki
50
Wczytanie pliku tekstowego
String s;
try
{
java.io.InputStream is =
this.getClass().getResourceAsStream("/Test.txt");
StringBuffer sb = new StringBuffer();
int i;
while ( (i = is.read()) != -1 )
sb.append( (char)i );
s = sb.toString();
}
catch (java.io.IOException e)
{
e.printStackTrace();
s = "";
}
// Plik res\Test.txt
51
Wczytanie pliku binarnego
String s;
try
{
java.io.InputStream is =
this.getClass().getResourceAsStream("/Test.bin");
java.io.DataInputStream dis =
new java.io.DataInputStream(is);
int i = dis.readInt();
s = Integer.toString(i);
}
catch (java.io.IOException e)
{
e.printStackTrace();
s = "";
}
// Plik res\Test.bin (Big Endian!)
52
Internet
javax.microedition.io
53
Internet

HTTP
javax.microedition.io.HttpConnection

HTTPS, SSL/TLS
javax.microedition.io.HttpsConnection
javax.microedition.io.SecureConnection

TCP, UDP
javax.microedition.io.SocketConnection
javax.microedition.io.ServerSocketConnection
javax.microedition.io.DatagramConnection
javax.microedition.io.UDPDatagramConnection

Push
javax.microedition.io.PushRegistry

Port szeregowy
javax.microedition.io.CommConnection
54
HTTP - przykład
import java.io.*;
import javax.microedition.io.*;
try {
HttpConnection c =
(HttpConnection)Connector.open("http://www.google.pl");
int ResponseCode = c.getResponseCode();
if (ResponseCode != HttpConnection.HTTP_OK)
// Błąd!
String ContentType = c.getType();
int Length = (int)c.getLength();
InputStream is = c.openInputStream();
...
}
catch (java.io.IOException e)
{
...
}
55
Grafika 2D
Canvas
Graphics
56
Canvas

Klasa javax.microedition.lcdui.Canvas

Dziedziczy z Displayable

Ekran całkowicie do dyspozycji dla programisty

Ręczne rysowanie i obsługa wejścia

Sposób użycia:

Zdefiniować klasę dziedziczącą z Canvas

Zaimplementować metody:
Obowiązkowo: paint
Opcjonalnie: sizeChanged, showNotify, hideNotify,
keyPressed, keyReleased, keyRepeated,
pointerDragged, pointerPressed, pointerReleased
57
Canvas - przykład
private class MyCanvas extends Canvas
{
protected void paint(Graphics g)
{
g.setColor(0xFF000000);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0xFFFFFFFF);
g.drawString("Hello World!", 4, 4,
Graphics.TOP | Graphics.LEFT);
}
}
protected void startApp()
throws MIDletStateChangeException
{
MyCanvas canvas = new MyCanvas();
Display display = Display.getDisplay(this);
display.setCurrent(canvas);
}
58
Sprawdzanie możliwości

Pamięć
Runtime rt = Runtime.getRuntime();
rt.totalMemory(), rt.freeMemory()

Wyświetlacz
Display d = Display.getDisplay(this);
d.isColor(), d.numColors(), d.numAlphaLevels()

Canvas
c.getWidth(), c.getHeight(),
c.isDoubleBuffered(), c.hasPointerEvents(),
c.hasPointerMotionEvents(), c.hasRepeatEvents()
59
Różnorodność możliwości

Klawiatura

Zestaw wymaganych klawiszy + niestandardowe

Problemy z klawiszami wciskanymi jednocześnie

Urządzenie wskazujące

Całkowicie opcjonalnie

Ekran

Bardzo różnorodne rozdzielczości
128x128, 128x160 176x220, 240x320 i inne...

Niektóre ekrany są wyższe, inne szersze!
640x200, 320x240 i inne...

FullScreen  nawet wtedy mogą pozostać paski!
canvas.setFullScreenMode(true);
60
Graphics  ustawienia

Prostokąt przycinania

Kolor

Czcionka

Styl linii  SOLID, DOTTED

Początek układu współrzędnych
61
Graphics  rysowanie
g.drawLine(4, 4, 32, 32);
g.drawRect(36, 4, 28, 28);
g.drawRoundRect(68, 4, 28, 28, 16, 16);
g.drawArc(100, 4, 28, 28, 20, 200);
g.fillTriangle(4, 36, 32, 50, 4, 64);
g.fillRect(36, 36, 28, 28);
g.fillRoundRect(68, 36, 28, 28, 16, 16);
g.fillArc(100, 36, 28, 28, 20, 200);
62
Graphics  tekst, obrazek
g.drawString("Hello World!", getWidth()/2, 4,
Graphics.TOP | Graphics.HCENTER);
g.drawImage(m_Image, getWidth()/2, getHeight()/2,
Graphics.HCENTER | Graphics.VCENTER);
Anchor:
Graphics.BOTTOM, TOP,
VCENTER, BASELINE,
LEFT, VCENTER, RIGHT
63
Canvas - klawiatura
private class MyCanvas extends Canvas
{
protected void keyPressed(int keyCode)
{
// ...
}
Canvas.KEY_NUM0  KEY_NUM9
KEY_STAR (*)
protected void keyReleased(int keyCode)
{
KEY_POUND (#)
// ...
if (keyCode > 0)
}
char ch = (char)keyCode;
Niestandardowe...
protected void keyRepeated(int keyCode)
{
// ...
}
// ...
Działa tylko jeśli hasRepeatEvents()
64
Klawisze do gier

Mapowanie klawiszy zwykłych na akcje gry:
getGameAction

Stałe akcji gry:
Canvas.UP, DOWN, LEFT, RIGHT, FIRE,
GAME_A, GAME_B, GAME_C, GAME_D
protected void keyPressed(int keyCode)
{
int game_action = getGameAction(keyCode);
if (game_action == Canvas.FIRE)
Fire();
else if (game_action == Canvas.UP)
Jump();
// ...
}
65
Programowanie gier 2D
GameCanvas
Layer
LayerManager
66
GameCanvas

Pakiet javax.microedition.lcdui.game

Klasy przeznaczone do pisania gier 2D

Klasa GameCanvas

Dziedziczy z Canvas

Zapewnie podwójne buforowanie obrazu

Umożliwia odpytywanie o wciśnięte klawisze

Pętlę gry trzeba zrealizować samemu

Za pomocą Timera lub

Za pomocą własnego wątku
67
Pętla gry  propozycja
MyCanvas m_MyCanvas;
private class MyCanvas
Thread m_Thread;
extends GameCanvas
implements Runnable
protected void startApp()
{
throws MIDletStateChangeException
public MyCanvas()
{
{
m_MyCanvas = new MyCanvas();
super(false);
setFullScreenMode(true);
Display display =
}
Display.getDisplay(this);
display.setCurrent(m_MyCanvas);
public void run()
{
m_Thread = new Thread(m_MyCanvas);
Graphics g = getGraphics();
m_Thread.start();
while (true) {
}
int keyState = getKeyStates();
if ((keyState & LEFT_PRESSED) != 0)
// ...
// Obliczenia...
// Rysowanie...
g.setColor(0xFF000000);
g.fillRect(0, 0,
getWidth(), getHeight());
flushGraphics(); // Koniec rysowania
}
}
}
68
Wejście z klawiatury
Stałe GameCanvas.
LEFT_PRESSED

getKeyStates()
RIGHT_PRESSED
UP_PRESSED
Zwraca flagi bitowe z wciśniętymi w
DOWN_PRESSED
danej chwili klawiszami
FIRE_PRESSED
GAME_A_PRESSED

GAME_B_PRESSED
super(false);
GAME_C_PRESSED
Zdarzenia keyPressed, keyReleased,
GAME_D_PRESSED
keyRepeated nie będą generowane -
wydajność!
69
Pomiar czasu i FPS
long StartTime = System.currentTimeMillis();
long LastTime = 0;
long t = 0, dt = 0;
long LastFpsTime = 0;
int FpsCounter = 0, Fps = 0;
while (true) {
int KeyStates = getKeyStates();
CalcFrame(t, dt, KeyStates);
DrawFrame(g);
DrawFPS(Fps);
flushGraphics();
LastTime = t;
t = System.currentTimeMillis() - StartTime;
dt = t  LastTime;
FpsCounter++;
if (LastFpsTime + 1000 <= t) {
Fps = FpsCounter;
FpsCounter = 0;
LastFpsTime += 1000;
}
}

System.currentTimeMillis() - zwraca 64-bitową liczbę milisekund od 1
stycznia 1970.

Warto stosować Thread.sleep() - oszczędność energii!
70
Layer

Klasa javax.microedition.lcdui.game.Layer

Reprezentuje pojedynczy obiekt graficzny 2D
(wbrew nazwie!)

Dwie klasy pochodne: Sprite, TiledLayer

Zawiera:

Pozycję

Stan widoczności

Rozmiar (tylko do odczytu)

Umiejętność narysowania
się na Graphics
71
Sprite
Sprite m_Sprite;
public MyCanvas()
{
// ...
m_Sprite = new Sprite(image);
m_Sprite.setPosition(4, 4);
}
public void run()
{
// ...
int keyState = getKeyStates();
if ((keyState & LEFT_PRESSED ) != 0) m_Sprite.move(-1 * (int)dt, 0);
if ((keyState & RIGHT_PRESSED) != 0) m_Sprite.move( 1 * (int)dt, 0);
if ((keyState & UP_PRESSED ) != 0) m_Sprite.move(0, -1 * (int)dt);
if ((keyState & DOWN_PRESSED ) != 0) m_Sprite.move(0, 1 * (int)dt);
// ...
m_Sprite.paint(g);
// ...
}
Pojedynczy obrazek
72
Sprite - transformacje

Obrazek może być obrócony lub odbity

Niedostępne w zwykłym Image!

Skalowanie i obracanie o dowolny kąt nadal
niedostępne :(
73
Sprite  piksel odniesienia

defineReferencePixel() - ustawia piksel odniesienia

setRefPixelPosition() - ustawia obrazek tak żeby
piksel odniesienia był w danym miejscu

Działa także z obrazkami obróconymi
74
Sprite - animacja

Obrazek może się składać z wielu klatek animacji

Domyślnie animację tworzą klatki pokazywane kolejno

Można ustawić własną sekwencję klatek
setFrameSequence()

Animacją trzeba sterować ręcznie
setFrame(), prevFrame(), nextFrame()
75
TiledLayer

Obiekt pokazujący tablicę kafelków
kopiowanych z obrazka zródłowego
76
TiledLayer  rodzaje kafelków

= 0  pusta komórka

> 0  fragment obrazka zródłowego

< 0  komórka animowana

Zastosowanie  np. animowana woda
77
Sprite - kolizje

Wsparcie dla kolizji między obiektami

Także z kafelkami TiledLayer

Także na poziomie przezroczystości
pojedynczych pikseli !!!
78
LayerManager

Klasa upraszczająca zarządzanie obiektami 2D

Jej wykorzystanie jest opcjonalne.

Przechowuje listę obiektów klasy Layer

Pamięta kolejność

Pozwala je narysować jednym wywołaniem

Oddzielenie prostokąta ekranu od prostokąta
rysowanej sceny

Zaleta #1: Rysowanie widoku tylko
w ograniczonym prostokącie

Zaleta #2: Przewijanie mapy
79
Kilka drobiazgów
Wibracja
Właściwości systemu
Obsługa błędów
80
Wibracja

Dodatkowy efekt zmysłowy!

Niedostępny na PC

Chyba, że gracz ma joypad z Force Feedback i
używasz DirectInput...
Display display = Display.getDisplay(this);
display.vibrate(200);

Wywołanie jest asynchroniczne  nie blokuje

Podawany czas jest w milisekundach

0 oznacza wyłączenie wibracji
81
Właściwości systemu

System.getProperty

Właściwości standardowe
microedition.profiles
microedition.configuration
microedition.locale
itd...

Właściwości dodatkowe
microedition.m3g.version
Bluetooth.api.version
video.encodings
itd...
82
Obsługa błędów

Trzeba zawsze łapać wyjątki lub deklarować ich
zgłaszanie

Jak zawsze w Javie.

Są to np.:

java.io.IOException
podczas wczytywania plików

java.lang.InterruptedException
przy oczekiwaniu w Thread, np. join(), sleep()

W emulatorze działa System.out.println()

A więc także Throwable.printStackTrace()
Image image;
try {
image = Image.createImage("/Obrazek.png );
}
catch (java.io.IOException e) {
e.printStackTrace();
image = null;
}
83
Baza danych
javax.microedition.rms.RecordStore
84
Baza danych???

MIDP nie daje dostępu do systemu plików

Chyba, że przez rozszerzenie  JSR-75

Nie ma innej możliwości zachowania trwałych
danych niż RMS

RMS  Record Management System

Pakiet javax.microedition.rms, klasa
RecordStore

Aplikacja może tworzyć i używać baz danych.
Każda ma nazwę.

Baza danych składa się z rekordów. Każdy ma
identyfikator oraz treść  surowe dane binarne.
85
Dodawanie rekordu
import java.io.*;
import javax.microedition.rms.*;
try
{
RecordStore rs = RecordStore.openRecordStore("Baza1", true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeUTF("Mieczysław");
dos.writeInt(1000);
byte[] data = baos.toByteArray();
int rec_id = rs.addRecord(data, 0, data.length);
}
catch (RecordStoreException e) { }
catch (IOException e) { }
86
Enumeracja rekordów
try
{
RecordStore rs = RecordStore.openRecordStore("Baza1", true);
RecordEnumeration re = rs.enumerateRecords(null, null, false);
while (re.hasNextElement())
{
int id = re.nextRecordId();
ByteArrayInputStream bais =
new ByteArrayInputStream(rs.getRecord(id));
DataInputStream dis = new DataInputStream(bais);
String name = dis.readUTF();
int score = dis.readInt();
System.out.println(name + " - " + score);
}
}
catch (RecordStoreException e) { }
catch (IOException e) { }
87
Enumeracja - filtrowanie
private class MyFilter implements RecordFilter
{
public boolean matches(byte[] candidate)
{
try {
ByteArrayInputStream bais = new ByteArrayInputStream(candidate);
DataInputStream dis = new DataInputStream(bais);
String name = dis.readUTF();
int score = dis.readInt();
return (score > 1000);
}
catch (IOException e) { e.printStackTrace(); return false; }
}
}
...
RecordEnumeration re = rs.enumerateRecords(new MyFilter(), null, false);
88
Enumeracja - sortowanie
private class MyComparator implements RecordComparator
{
public int compare(byte[] rec1, byte[] rec2)
{
try {
ByteArrayInputStream bais1 = new ByteArrayInputStream(rec1);
ByteArrayInputStream bais2 = new ByteArrayInputStream(rec2);
DataInputStream dis1 = new DataInputStream(bais1);
DataInputStream dis2 = new DataInputStream(bais2);
int score1 = dis1.readInt();
int score2 = dis2.readInt();
String name1 = dis1.readUTF();
String name2 = dis2.readUTF();
int cmp = name1.compareTo(name2);
if (cmp < 0) return RecordComparator.PRECEDES;
else if (cmp == 0) return RecordComparator.EQUIVALENT;
else return RecordComparator.FOLLOWS;
}
catch (IOException e) {
e.printStackTrace();
return RecordComparator.PRECEDES;
}
}
}
...
RecordEnumeration re = rs.enumerateRecords(null, new MyComparator(), false);
89
Inne możliwości

Operacje na rekordach
addRecord, getRecord, getRecordSize, deleteRecord

Nasłuchiwanie zmian
addRecordListener, removeRecordListener,
interfejs RecordListener

Informacje o bazie danych
getNumRecords, getSize, getSizeAvailable,
getLastModified, getVersion

Operacje na bazach danych
listRecordStores, openRecordStore,
deleteRecordStore

Współdzielenie baz danych między midletami...
90
MIDP 2.0 Media API
javax.microedition.media
91
MIDP 2.0 Media API

Zapewnia podstawowe API do odtwarzania
multimediów

Tylko dzwięk

Rozszerzane przez Mobile Media API (JSR-135)

Nie gwarantuje obsługi żadnych formatów

Jedynie proste generowanie odgłosów
92
MIDP 2.0 Media API
<>
Manager

Manager
<>

Klasa statyczna

Dostarcza informacji na temat możliwości multimedialnych
urządzenia

Umożliwia tworzenie Playerów
<>
Player

Player

Odtwarza konkretny zasób multimedialny
<>

Pozwala na sterowanie odtwarzaniem
<>

Posiada stany
Control

Pozwala na pobieranie kontrolerów

Control

Pozwala na regulację parametrów odtwarzania, np.
głośności
93
Odtwarzanie dzwięku
try {
Player p = Manager.createPlayer("http://adres.pl/ding.wav");
p.setLoopCount(5);
p.start();
}
catch (IOException ioe) { }
catch (MediaException me) { }
try {
InputStream is = getClass().getResourceAsStream("/ding.wav");
Player p = Manager.createPlayer(is, "audio/X-wav");
p.start();
}
catch (IOException ioe) { }
catch (MediaException me) { }
94
Obsługiwane formaty

Content Type  typ MIME, np.:

Dzwięk WAV: audio/x-wav

Dzwięk AU: audio/basic

Dzwięk MP3: audio/mpeg

Dzwięk MIDI: audio/midi

Tone sequence: audio/x-tone-seq
95
Player  stany
96
Rozszerzenia
MIDP 2.0 to nie koniec...
97
Przykłady rozszerzeń

Grafika 3D

Mobile 3D Graphics API  M3G (JSR-184)

Mascot Capsule

Grafika i multimedia

Mobile Media API (JSR-135)

Nokia UI API

Dostęp do plików

FileConnection (JSR-75)

Dostęp do danych PIM (kontakty itp.)

PIM (JSR-75)

Aączność

Java APIs for Bluetooth (JSR-82)

Wireless Messaging API  WMA (JSR-120)

Wiele innych...
98


Wyszukiwarka

Podobne podstrony:
Sieci komputerowe wyklady dr Furtak
Wykład 05 Opadanie i fluidyzacja
WYKŁAD 1 Wprowadzenie do biotechnologii farmaceutycznej
mo3 wykladyJJ
ZARZĄDZANIE WARTOŚCIĄ PRZEDSIĘBIORSTWA Z DNIA 26 MARZEC 2011 WYKŁAD NR 3
Wyklad 2 PNOP 08 9 zaoczne
Wyklad studport 8
Kryptografia wyklad
Budownictwo Ogolne II zaoczne wyklad 13 ppoz
wyklad09
Sporzadzanie rachunku przepływów pienieżnych wykład 1 i 2
fcs wyklad 5
Wyklad08 Zaopatrz wWode

więcej podobnych podstron