📄 world.java
字号:
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import java.util.Vector;
import java.io.InputStream;
import java.io.IOException;
/**
* A container and manager for all the tiles and actors within a single
* map/level/environment in a game.
* @author Martin J. Wells
*/
public class World
{
private static final int TILE_WIDTH=16;
private static final int TILE_HEIGHT=16;
/**
* Types of tiles as bytes. Each one directly maps to a frame in the world
* image file. Set NO_TILE to leave a tile empty (the default).
*/
public static final byte NO_TILE = 0;
public static final byte WALL_TILE = 1;
public static final byte START_ACTIVATOR_TILE = 2;
public static final byte FIGHTER_ACTIVATOR_TILE = 2;
public static final byte END_ACTIVATOR_TILE = 2;
public static final byte PLAYERSTART_TILE = 3;
private Vector actors;
private int viewX, viewY; // Current viewport coordinates.
private int viewWidth, viewHeight; // Size of the current viewport.
private int tilesWide, tilesHigh; // The number of tiles in the world.
private ImageSet tiles; // Images for the tiles.
private byte tileMap[][]; // Array of bytes representing the map
private Ship playerShip; // A link to the player's object.
/**
* Constructs a world containing the requested number of tiles across and
* down. The size of the rendering viewport should be set to the size of the
* screen unless you wish to render only a small portion.
* @param tilesWideArg Width of the world in tiles.
* @param tilesHighArg Height of the world in tiles.
* @param viewWidthArg Width of the view port.
* @param viewHeightArg Height of the view port.
*/
public World(int tilesWideArg, int tilesHighArg, int viewWidthArg,
int viewHeightArg)
{
tilesWide = tilesWideArg;
tilesHigh = tilesHighArg;
viewWidth = viewWidthArg;
viewHeight = viewHeightArg;
// Initialize the actor vector.
actors = new Vector();
// Load the tile graphics (16 x 16 pixel frames).
Image tileGraphics = ImageSet.loadClippedImage("/world.png", 0, 0,
16, 16);
tiles = new ImageSet(1);
tiles.addState(new Image[]{tileGraphics}, 0);
}
/**
* Set the reference to the player's ship.
*/
public void setPlayerShip(Ship s)
{
playerShip = s;
}
private String readString(InputStream is, byte terminator)
{
try
{
StringBuffer sb = new StringBuffer();
int b = is.read();
while (b != -1)
{
if (b == terminator)
{
return sb.toString();
} else
sb.append((char)b);
b = is.read();
}
return null;
}
catch(IOException e)
{
System.out.println("IOException: " + e);
e.printStackTrace();
return null;
}
}
/**
* Loads a level from the map data file using its name as the key.
* @param levelName The name of the level to load.
*/
public void loadLevel(String levelName)
{
try
{
InputStream is = null;
is = this.getClass().getResourceAsStream("/levels.dat");
// Read in the level name string.
boolean foundLevel = false;
// Loop through until you get to the correct level.
int b = is.read();
while (b != -1 && !foundLevel)
{
// Level name starts with a ! and terminates with a ~ character.
// The readString method wraps up reading the string from the
// stream.
if (b == '!')
{
// Got a start of level name char, read the name string.
String ln = readString(is, (byte) '~').toLowerCase();
if (ln.equals(levelName.toLowerCase()))
foundLevel = true;
}
// If the level hasn't been found yet you continue reading.
if (!foundLevel)
b = is.read();
}
// Test if the end of the file was reached, in which case the level
// load failed (possibly because of the wrong level name being used.
if (b == -1)
throw new Exception("unknown level: " + levelName);
// Load the level. Start by reading the height and width from the file.
byte[] buffer = new byte[2];
is.read(buffer);
String tws = new String(buffer, 0, 2);
is.read(buffer);
String ths = new String(buffer, 0, 2);
tilesWide = Integer.parseInt(tws);
tilesHigh = Integer.parseInt(ths);
tileMap = new byte[tilesHigh][tilesWide];
// Next you read all the tiles into the tilemap. Each tile is
// represented by 2 bytes (tile numbers can go up to a maximum of 99).
// The data is read into a 2 byte buffer and then added to the tilemap.
int bytesRead=0;
for (int ty=0; ty < tilesHigh; ty++)
{
for (int tx = 0; tx < tilesWide; tx++)
{
bytesRead = is.read(buffer);
if (bytesRead > 0)
{
tws = new String(buffer, 0, 2).trim();
if (tws.indexOf("-") != -1)
// If this starts throwing exceptions when loading the map
// check that the tile indecies in your map file doesn't
// have -1's in it. This is the default index for the
// empty tile in TileStudio. Always fill your levels with
// blank (NO_TILE) entries before editing it.
System.out.println("Oops, read a - at " + tx + ", " + ty);
byte c = Byte.parseByte(tws);
if (c == PLAYERSTART_TILE)
{
playerShip.setX(tx * TILE_WIDTH);
playerShip.setY(ty * TILE_HEIGHT);
} else
{
// All other tiles are handled either as walls or
// activators.
tileMap[ty][tx] = c;
}
}
}
}
}
catch (Exception e)
{
System.out.println("Exception loading map file : " + e);
e.printStackTrace();
}
}
/**
* Add an Actor to the world.
* @param a The Actor to add.
*/
public void addActor(Actor a)
{
actors.addElement(a);
}
/**
* Remove an Actor from the world.
* @param a The Actor object to remove.
*/
public void removeActor(Actor a)
{
actors.removeElement(a);
}
/**
* Set the current view port position (relative to world coordinates).
* @param viewXArg The x position of the view.
* @param viewYArg The y position of the view.
*/
public final void setView(int viewXArg, int viewYArg)
{
viewX = viewXArg;
viewY = viewYArg;
}
/**
* @param x A world coordinate on the x axis.
* @return The corresponding tile location at this position.
*/
public final int getTileX(int x) { return x / TILE_WIDTH; }
/**
* @param y A world coordinate on the y axis.
* @return The corresponding tile location at this position.
*/
public final int getTileY(int y) { return y / TILE_HEIGHT; }
/**
* Safely retrieves the tile type (byte) from a given x, y position (in
* world coorindates). The x and y position will be converted into a tile
* coordinate.
* @param x The x position in world coordinates.
* @param y The y position in world coordinates.
* @return The tile type (as a byte) at the specified location.
*/
public final byte getTile(int x, int y)
{
int tileX = getTileX(x);
int tileY = getTileY(y);
if (tileX < 0 || tileX >= tilesWide || tileY < 0 || tileY >= tilesHigh)
return -1;
return tileMap[ y / TILE_HEIGHT ][ x / TILE_WIDTH ];
}
/**
* Renders the world onto the graphics context. This version starts by
* drawing a star field then the tiles and finally the actors.
* @param graphics The graphics context upon which to draw.
*/
protected void render(Graphics graphics)
{
// Draw a star field scrolling 10 times slower than the view.
Tools.drawStarField(graphics, viewX / 10, viewY / 10,
viewWidth, viewHeight);
// Render the tiles that are within the current view port rectangle.
int startTileX = (viewX / TILE_WIDTH) - 1;
int startTileY = (viewY / TILE_HEIGHT) - 1;
int endTileX = ((Math.abs(viewX) + viewWidth) / TILE_WIDTH) + 1;
int endTileY = ((Math.abs(viewY) + viewHeight) / TILE_HEIGHT) + 1;
if (endTileX > tilesWide) endTileX = tilesWide;
if (endTileY > tilesHigh) endTileY = tilesHigh;
if (startTileX < 0) startTileX = 0;
if (startTileY < 0) startTileY = 0;
byte tileType = 0;
int xpos = 0;
int ypos = 0;
// Loop through all the rows of the tile map that need to be drawn, then
// all the columns. This starts at startTileY and goes down till we get to
// the last viewable row at endTileY, then for each of these it goes
// across the map from startTileX to endTileX.
for (int drawTileY = startTileY; drawTileY < endTileY; drawTileY++)
{
for (int drawTileX = startTileX; drawTileX < endTileX; drawTileX++)
{
if (drawTileY >= 0 && drawTileX >= 0)
{
// Access the entry corresponding to this location. Since most
// tile maps contains empty space the code also does a quick
// check to see if it can just ignore this entry if the byte
// equals the default value 0 (NO_TILE).
tileType = tileMap[drawTileY][drawTileX];
if (tileType == NO_TILE) continue; // quick abort if it's nothing
if (tileType >= START_ACTIVATOR_TILE &&
tileType <= END_ACTIVATOR_TILE)
activateTile(drawTileX, drawTileY);
else
{
// Calculate the x and y position of this tile.
xpos = (drawTileX * TILE_WIDTH) - viewX;
ypos = (drawTileY * TILE_HEIGHT) - viewY;
// Check whether this tile position is in the view port.
if (xpos > 0 - TILE_WIDTH && xpos < viewWidth &&
ypos > 0 - TILE_HEIGHT && ypos < viewHeight)
{
// Based on the byte value this code draws an image from the
// ImageSet loaded in the constructor. To keep this simpler
// I抳e mapped the frames in the graphics file to the same
// byte values in the map. That way the code doesn抰 need to
// translate the values when drawing them [--] if the tile map
// byte is the number two the second frame from the loaded
// world images will be drawn (note that I take one away from
// the byte value to account for zero meaning no tile in the
// world).
tiles.draw(graphics, 0, 0, xpos, ypos);
}
}
}
}
}
// Render all the actors on top.
for (int i = 0; i < actors.size(); i++)
{
Actor a = (Actor) actors.elementAt(i);
if (Tools.isPointInRect(a.getX(), a.getY(),
viewX - TILE_WIDTH, viewY - TILE_HEIGHT,
endTileX * TILE_WIDTH, endTileY * TILE_HEIGHT))
a.render(graphics, viewX, viewY);
}
}
/**
* Cycle the world. For this example we only have to call the cycle method
* on all the actors. Note that to simplify things I'm using an elasped time
* of 100 milliseconds. For a complete game this should be based on proper
* timeing (See the Star Assault GameScreen class code for an example of
* this).
*/
public void cycle()
{
for (int i = 0; i < actors.size(); i++)
((Actor) actors.elementAt(i)).cycle(100);
}
/**
* Called by the Actor to check if it has collided with anything in the
* world. See the Actor cycle method for more information.
* @param actorToCheck The Actor that needs to be checked.
* @return True if the actor is in a collision state with either a tile or
* another Actor.
*/
public boolean checkCollision(Actor actorToCheck)
{
// test if this actor object has hit a tile on layer 1 (we ignore layer 0)
// we look at all the tiles under the actor (we do a <= comparison so we
// include the bounding edge of the actor's rectangle
int actorBottom = actorToCheck.getY()+actorToCheck.getHeight();
int actorRightSide = actorToCheck.getX()+actorToCheck.getWidth();
for (int tileY=actorToCheck.getY(); tileY <= actorBottom;
tileY += TILE_HEIGHT)
{
for (int tileX=actorToCheck.getX(); tileX <= actorRightSide;
tileX += TILE_WIDTH)
{
if (getTile(tileX, tileY) > NO_TILE)
return true;
}
}
// Ignore bullet hits on other actors for now.
if (actorToCheck instanceof Bullet) return false;
// Did this ship hit another?
for (int j = 0; j < actors.size(); j++)
{
Actor another = (Actor)actors.elementAt(j);
if (another != actorToCheck && !(another instanceof Bullet) &&
Tools.isIntersectingRect(actorToCheck.getX(), actorToCheck.getY(),
actorToCheck.getWidth(),
actorToCheck.getHeight(),
another.getX(), another.getY(),
another.getWidth(),
another.getHeight()))
return true;
}
return false;
}
/**
* Activates a tile at a specific location.
* @param tileX
* @param tileY
*/
private final void activateTile(int tileX, int tileY)
{
byte tileType = tileMap[tileY][tileX];
int xpos = (tileX * TILE_WIDTH);
int ypos = (tileY * TILE_HEIGHT);
switch (tileType)
{
case FIGHTER_ACTIVATOR_TILE:
Ship s = new Ship(this, true, xpos, ypos);
addActor(s);
break;
}
// clear the activator tile
tileMap[tileY][tileX] = NO_TILE;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -