Tečaj
:: Predavatelj: Žiga Hajduković. e-mail. .
:: Organizator: Dane Šoba. e-mail. Študentska organizacija FRI (ŠOFRI).
:: Domača stran tečaja: www.tetris1d.org/game/dev/tecaj/.
:: game/dev.si forum: www.tetris1d.org/game/dev/forum_si/.
:: Študentski info sistem FRI: http://www.student-info.net/fri/.
Čestitam vsem sodelujočim v velikem nagradnem tekmovanju za najboljšo mobilno igro, ki jo je sponzoriralo priznano podjetje Cocoasoft!
V zelo kratkem času vam je uspelo prekositi same sebe in ustvariti igre, ki so presegle vsa pričakovanja!
Objavili smo galerijo vseh sodelujočih iger, kjer si lahko ogledate posnetke akcije iz samih iger, pa tudi fotografije iz podelitve bogatih nagrad!
Ne pozabite na gd.si forum, kjer si bomo še naprej izmenjevali mnenja in pomagali drug drugemu na poti k izdelovanju najboljših mobilnih iger!
Lekcija 03 |
lekcija 02 :: domov :: lekcija 04 |
Epsilon 02 eclipse workspace project (sfx)
Odpri povezavo:
http://www.tetris1d.org/gdwap/na svojem mobilnem telefonu in si naloži Epsilon_02.
GameCanvas.java (izvorna datoteka)
GameSprite.java (izvorna datoteka)
Datoteka stage1.txt:
Tileset:J L ^ < > \ / v O G OOOOOOOOOOOOOOOO; ; OOvvvvvvvvvvvvOO; ; O/ \O; ; > <; B ; > <; ; > <; ; > <; ; > <; ; OL JO; VV ; OGL JGO; AA AA ; OOGL JGOO; VV ; \OOGL JGOO/; ; \OG/ \GO/ ; ; \/ \/ ; ; ; O O O O O ; JL ; ; JOOL ; ; L <OOG ; O O O O O ; > <GG> J; ; / <OO> <; ; GOO> \; ; \GG/ ; V V ; \/ ; V V ; J; V V ; J^^L JO; ; JOvvOL \O; ; JG/ O> \; AA ; <> J>/ J; A A ; J/ <G JO; ; J> JO/ JOG; AA ; J/ JO/ JOGO; A A ; GO^^^/ OOOG; ; GOGG/ OOGO; O ; OGOO OGOO; O ; vvvv Ovvv; ; ; ; JL JL ; ; JOOL JOOL ; O O ; JOOGO OGOOL; O ; OOGO> <OGOO; O O ; \GOGO OGOG/; O O ; OOO> <OOO ; O O O ; vvvv vvvv ; ; OOOO OOOO ; O O O ; \vv/ \vv/ ; O ; ; O O ; L J; O O ; > <O; ; / \; ; ; O O O O O ; JL ; ; JOOL ; ; L <OOG ; O O O O O ; > <GG> J; ; / <OO> <; ; GOO> \; ; \GG/ ; V V ; \/ ; V V ; J; V V ; J^^L JO; ; JOOOOL \O; ; JOOGOO> \; ; <OOOGO/ J; ; JOOOOG JO; P ;
Uporaben program za te namene je Paint Shop Pro. Na hitro si bomo pogledali, katere njegove funkcije pridejo prav pri izdelavi grafike za mobilne telefone.
Paziti je potrebno predvsem na:
public static final byte FIRE_COOLDOWN_WAIT_FRAMES = 10;
public static byte fire_cooldown_wait_frames_counter;
...
public static Vector playerFireSprites;
...
if (command_fire)
{
if (fire_cooldown_wait_frames_counter <= 0)
{
GameSprite fire = new GameSprite(SPRITE_IMAGE_FIRE, MAX_ENERGY_PLAYER_FIRE);
fire.x = player.x + player.w/2 - fire.w/2;
fire.y = player.y;
fire.vy = -4;
playerFireSprites.addElement(fire);
fire_cooldown_wait_frames_counter = FIRE_COOLDOWN_WAIT_FRAMES;
}
}
if (fire_cooldown_wait_frames_counter > 0)
fire_cooldown_wait_frames_counter--;
// create a new enemy
// which one?
byte img_id = (byte) (SPRITE_IMAGE_ENEMY1 + Math.abs(random.nextInt() % 3));
new_enemy = new GameSprite(img_id, MAX_ENERGY_ENEMY);
switch (img_id)
{
case SPRITE_IMAGE_ENEMY1:
new_enemy.vx = 0;
new_enemy.vy = 3;
break;
case SPRITE_IMAGE_ENEMY2:
new_enemy.vx = 0;
new_enemy.vy = 6;
break;
case SPRITE_IMAGE_ENEMY3:
new_enemy.vx = random.nextInt() % 2;
new_enemy.vy = 3 + random.nextInt() % 2;
break;
}
public static final short FIXED_FRAME_TIME = 60;
...
public void run()
{
while(gameRunning)
{
long frameStartTime = System.currentTimeMillis();
...
long frameTime = System.currentTimeMillis() - frameStartTime;
if (frameTime < FIXED_FRAME_TIME)
{
try {
Thread.sleep( FIXED_FRAME_TIME - frameTime );
} catch (Exception e) {};
}
break;
public static Image[] spriteImages; public static final byte SPRITE_IMAGE_PLAYER = 0; public static final byte SPRITE_IMAGE_FIRE = 1; public static final byte SPRITE_IMAGE_BOSS1 = 2; public static final byte SPRITE_IMAGE_ENEMY1 = 3; public static final byte SPRITE_IMAGE_ENEMY2 = 4; public static final byte SPRITE_IMAGE_ENEMY3 = 5; public static final byte SPRITE_IMAGE_EXPLOSION = 6; public static final byte SPRITE_IMAGE_MAX = 7; ... public static Image tileImage;
Novosti v GameSprite.java:
GameSprite(byte image_id, byte energy_max)
{
imageId = image_id;
w = spriteImageFrameWidths[image_id];
h = GameCanvas.spriteImages[image_id].getHeight();
frameId = 0;
frameIdMax = (byte) (GameCanvas.spriteImages[image_id].getWidth() / w - 1);
vx = 4;
vy = 4;
energyMax = energy_max;
energy = energyMax;
}
public static final byte[] spriteImageFrameWidths =
{
26, // SPRITE_IMAGE_PLAYER
4, // SPRITE_IMAGE_FIRE
45, // SPRITE_IMAGE_BOSS1
20, // SPRITE_IMAGE_ENEMY1
20, // SPRITE_IMAGE_ENEMY2
20, // SPRITE_IMAGE_ENEMY3
20, // SPRITE_IMAGE_EXPLOSION
};
paintSprite()
public static void paintSprite(Graphics g, GameSprite spr)
{
// set the clip window
int cx = spr.x;
int cy = spr.y;
int cw = GameSprite.spriteImageFrameWidths[spr.imageId];
int ch = spr.h;
g.setClip(cx, cy, cw, ch);
// calculate the x coordinate of where to start the image paint,
// so our frame will be seen through the clip window
int dx = spr.x - spr.frameId * GameSprite.spriteImageFrameWidths[spr.imageId];
// paint the sprites image at the calculated coordinates
g.drawImage(spriteImages[spr.imageId], dx, spr.y, Graphics.TOP | Graphics.LEFT);
// restore the clip window
g.setClip(0, 0, canvasWidth, canvasHeight);
}
Primeri:
player.frameId = SPRITE_FRAME_PLAYER_CENTER;
if (command_left)
{
player.frameId = SPRITE_FRAME_PLAYER_LEFT;
}
if (command_right)
{
player.frameId = SPRITE_FRAME_PLAYER_RIGHT;
}
public void updateExplosionSprites()
{
for (int i=0; i < explosionSprites.size(); i++)
{
GameSprite exp = (GameSprite) explosionSprites.elementAt(i);
// animate explosion!
exp.frameId++;
if (exp.frameId > exp.frameIdMax)
{
explosionSprites.removeElementAt(i);
// only increase counter, if not removed from list
i--;
}
}
...
// move enemy sprites
for (int i=0; i < enemySprites.size(); i++)
{
GameSprite enemy = (GameSprite) enemySprites.elementAt(i);
// animate sprite to next sprite frame every 3 frames
if (global_frame_counter % 3 == 0)
enemy.frameId = (byte) ( (enemy.frameId + 1) % (enemy.frameIdMax+1));
public static int player_gameover_explosion_counter;
public static int PLAYER_GAMEOVER_EXPLOSION_COUNTER = 40;
// check for game end
if (menu_command_2 ||
( (player.energy <= 0) &&
(player_gameover_explosion_counter == 0))
)
{
...
...
// player loses energy
player.energy -= player.energyMax/4;
if (player.energy <= 0 && player_gameover_explosion_counter < 0)
{
player_gameover_explosion_counter = PLAYER_GAMEOVER_EXPLOSION_COUNTER;
}
...
public void updateExplosionSprites()
{
...
// create a big bada boom player gameover explosion
// 7 - max explosion frames
// 2 - add an explosion every 2nd frame
if (player_gameover_explosion_counter > 7 &&
player_gameover_explosion_counter % 2 == 0
)
{
GameSprite new_exp = addExplosion(player);
new_exp.x += random.nextInt() % player.w;
new_exp.y += random.nextInt() % player.h;
}
}
Tileset
The 2 Stage layers: space background, tiled background
paintLevel, loadLevel
tileMap (in spriteMap)
class Level
{
public static final byte LEVEL_WIDTH = 16;
public static final byte LEVEL_HEIGHT = 64;
public static final byte TILE_SIZE = 16;
public static final byte TILES_PER_IMAGE_LINE = 5;
// space background position and scroll speed
public static int space_scroll_y;
public static int space_scroll_vy = -1;
// tiled background position and scroll speed
public static int tiles_scroll_y;
public static int tiles_scroll_vy = -2;
public static int tiles_scroll_x;
public static byte[] tileMap;
// a translation table for level tile text representation
// to tile index in the tile image
public static char[] tileTextToImageIndex =
{
'J', 'L','^','<','>',
'\\','/','v','O','G',
};
public static byte getTileIndex(char t)
{
byte idx = 0;
// find tile index
while ( idx < tileTextToImageIndex.length && tileTextToImageIndex[idx] != t )
idx++;
// handle unexisting or empty tiles
if (idx > tileTextToImageIndex.length)
idx = -1;
return idx;
}
public static void loadLevel(byte level_no)
{
levelno = level_no;
// intialize the scrolling background
space_scroll_y = 0;
space_scroll_vy = 1;
InputStream inputstream = null;
DataInputStream datainputstream = null;
tileMap = new byte[LEVEL_HEIGHT * LEVEL_WIDTH];
int map_row = 0;
int tile_map_col = 0;
int sprite_map_col = 0;
try
{
inputstream = new Object().getClass().getResourceAsStream( "/stage"+ level_no +".txt" );
datainputstream = new DataInputStream(inputstream);
char ch;
boolean eof = false;
while(!eof)
{
// read level text file lines
try
{
// load a row of tiles
tile_map_col = 0;
ch = ' ';
while (ch != ';')
{
ch = (char) datainputstream.readByte();
System.out.print(ch);
if (ch != ';')
{
tileMap[ map_row * LEVEL_WIDTH + tile_map_col ] = getTileIndex(ch);
tile_map_col++;
}
else
{
break;
}
}
// load a row of sprites
sprite_map_col = 0;
ch = ' ';
while (ch != ';')
{
...
...
...
}
// go to next line of text
map_row++;
}
catch (EOFException eofex)
{
eof = true;
}
}
inputstream.close();
inputstream = null;
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
public static void paintTiles(Graphics g)
{
// clip window coordinates
int cx, cy, cw, ch;
// image draw coordinates
int dx, dy;
int x;
int y = 0;
while (y <= GameCanvas.canvasHeight)
{
x = 0;
while (x <= GameCanvas.canvasWidth)
{
// get the tileMap index of the tile, located at tx, ty
int tx = tiles_scroll_x + x;
int ty = tiles_scroll_y + y;
int tile_idx = ty / TILE_SIZE * LEVEL_WIDTH + tx / TILE_SIZE;
// get tile index in image from tileMap
byte tile_image_idx = tileMap[tile_idx];
if ( tile_image_idx >= 0)
{
// get tile level coordinates from tile index
int tile_idx_x = (tile_idx % LEVEL_WIDTH) * TILE_SIZE;
int tile_idx_y = (tile_idx / LEVEL_WIDTH) * TILE_SIZE;
// translate to screen coordinates
tile_idx_x -= tiles_scroll_x;
tile_idx_y -= tiles_scroll_y;
int tile_col = tile_image_idx % TILES_PER_IMAGE_LINE;
int tile_row = tile_image_idx / TILES_PER_IMAGE_LINE;
cx = tile_idx_x;
cy = tile_idx_y;
cw = TILE_SIZE;
ch = TILE_SIZE;
// check clip window coordinates, if out of screen
if (cx < 0)
{
cw += cx;
cx = 0;
}
if (cy < 0)
{
ch += cy;
cy = 0;
}
// check clip window size, if out of screen
if (cx + cw > GameCanvas.canvasWidth)
cw -= (cx + cw) - GameCanvas.canvasWidth;
if (cy + ch > GameCanvas.canvasHeight)
ch -= (cy + ch) - GameCanvas.canvasHeight;
dx = tile_idx_x - tile_col * TILE_SIZE;
dy = tile_idx_y - tile_row * TILE_SIZE;
g.setClip(cx, cy, cw, ch);
g.drawImage(GameCanvas.tileImage, dx, dy, Graphics.TOP | Graphics.LEFT);
}
x += TILE_SIZE;
}
y += TILE_SIZE;
}
g.setClip(0, 0, GameCanvas.canvasWidth, GameCanvas.canvasHeight);
}
public static void paintSpaceBackground(Graphics g)
{
// fill the vast space
g.setColor(0x000000);
g.fillRect(0, 0, GameCanvas.canvasWidth, GameCanvas.canvasHeight);
// init scrolling background image tiling
int x = 0;
int y = space_scroll_y % GameCanvas.imgBackground.getHeight() - GameCanvas.imgBackground.getHeight();
// image is wide enough, so we only need to tile it in the vertical (y) direction
while (y < GameCanvas.canvasHeight)
{
g.drawImage(GameCanvas.imgBackground, x, y, Graphics.TOP | Graphics.LEFT);
y += GameCanvas.imgBackground.getHeight();
}
}
}
Epsilon, dodatna grafika (by geci)
Grafika za igro Axion Space, Cocoasoft
| game/dev.si | :: predlogi ? pišite: Žiga Hajduković |