google-site-verification: google61fe8ba583a51912.html
Tile-Based Game Map in Java

Overview

A tile-based game map system using 2D arrays to represent game worlds. This implementation supports different tile types, layering, pathfinding, and rendering.

Core Classes

1. Tile Type Enum

public enum TileType { GRASS(1, true, 1.0f, "Grass"), WATER(2, false, 0.0f, "Water"), SAND(3, true, 0.8f, "Sand"), FOREST(4, true, 1.2f, "Forest"), MOUNTAIN(5, false, 0.0f, "Mountain"), ROAD(6, true, 0.5f, "Road"), WALL(7, false, 0.0f, "Wall"), DOOR(8, true, 0.7f, "Door"); private final int id; private final boolean walkable; private final float movementCost; private final String name; TileType(int id, boolean walkable, float movementCost, String name) { this.id = id; this.walkable = walkable; this.movementCost = movementCost; this.name = name; } // Getters public int getId() { return id; } public boolean isWalkable() { return walkable; } public float getMovementCost() { return movementCost; } public String getName() { return name; } public static TileType getById(int id) { for (TileType type : values()) { if (type.id == id) return type; } return GRASS; } }

2. Tile Class

public class Tile { private TileType type; private int x, y; private boolean visible; private boolean explored; private List<Entity> entities; public Tile(TileType type, int x, int y) { this.type = type; this.x = x; this.y = y; this.visible = true; this.explored = false; this.entities = new ArrayList<>(); } public void addEntity(Entity entity) { entities.add(entity); } public void removeEntity(Entity entity) { entities.remove(entity); } public boolean hasEntities() { return !entities.isEmpty(); } public List<Entity> getEntities() { return new ArrayList<>(entities); } // Getters and setters public TileType getType() { return type; } public void setType(TileType type) { this.type = type; } public int getX() { return x; } public int getY() { return y; } public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } public boolean isExplored() { return explored; } public void setExplored(boolean explored) { this.explored = explored; } public boolean isWalkable() { return type.isWalkable() && entities.stream().noneMatch(Entity::isBlocking); } @Override public String toString() { return String.format("Tile[%s at (%d,%d)]", type.getName(), x, y); } }

3. Game Map Class

public class GameMap { private Tile[][] tiles; private int width; private int height; private String name; private List<MapLayer> layers; public GameMap(int width, int height, String name) { this.width = width; this.height = height; this.name = name; this.tiles = new Tile[width][height]; this.layers = new ArrayList<>(); initializeTiles(); } private void initializeTiles() { for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { tiles[x][y] = new Tile(TileType.GRASS, x, y); } } } public void setTile(int x, int y, TileType type) { if (isInBounds(x, y)) { tiles[x][y].setType(type); } } public Tile getTile(int x, int y) { if (isInBounds(x, y)) { return tiles[x][y]; } return null; } public boolean isInBounds(int x, int y) { return x >= 0 && x < width && y >= 0 && y < height; } public boolean isWalkable(int x, int y) { Tile tile = getTile(x, y); return tile != null && tile.isWalkable(); } public List<Point> findPath(int startX, int startY, int endX, int endY) { return AStarPathfinder.findPath(this, startX, startY, endX, endY); } public void addLayer(MapLayer layer) { layers.add(layer); } public void generateRandomMap() { Random random = new Random(); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { TileType type = TileType.GRASS; double rand = random.nextDouble(); if (rand < 0.1) type = TileType.WATER; else if (rand < 0.2) type = TileType.FOREST; else if (rand < 0.25) type = TileType.MOUNTAIN; else if (rand < 0.3) type = TileType.SAND; setTile(x, y, type); } } generateRoads(); } private void generateRoads() { Random random = new Random(); // Create a horizontal road int roadY = height / 2; for (int x = 0; x < width; x++) { setTile(x, roadY, TileType.ROAD); // Add some vertical connections if (random.nextDouble() < 0.1) { int connectionLength = random.nextInt(3) + 2; for (int y = roadY + 1; y < Math.min(height, roadY + connectionLength); y++) { setTile(x, y, TileType.ROAD); } } } } // Getters public int getWidth() { return width; } public int getHeight() { return height; } public String getName() { return name; } public List<MapLayer> getLayers() { return new ArrayList<>(layers); } }

4. A* Pathfinding Implementation

public class AStarPathfinder { public static List<Point> findPath(GameMap map, int startX, int startY, int endX, int endY) { if (!map.isWalkable(endX, endY)) { return Collections.emptyList(); } PriorityQueue<Node> openSet = new PriorityQueue<>(); Map<Point, Node> allNodes = new HashMap<>(); Node startNode = new Node(startX, startY, null, 0, heuristic(startX, startY, endX, endY)); openSet.add(startNode); allNodes.put(new Point(startX, startY), startNode); while (!openSet.isEmpty()) { Node current = openSet.poll(); if (current.x == endX && current.y == endY) { return reconstructPath(current); } for (Point neighbor : getNeighbors(map, current.x, current.y)) { Node neighborNode = allNodes.getOrDefault(neighbor, new Node(neighbor.x, neighbor.y)); float tentativeG = current.gCost + getMovementCost(map, current.x, current.y, neighbor.x, neighbor.y); if (tentativeG < neighborNode.gCost) { neighborNode.parent = current; neighborNode.gCost = tentativeG; neighborNode.hCost = heuristic(neighbor.x, neighbor.y, endX, endY); neighborNode.fCost = neighborNode.gCost + neighborNode.hCost; if (!openSet.contains(neighborNode)) { openSet.add(neighborNode); } allNodes.put(neighbor, neighborNode); } } } return Collections.emptyList(); // No path found } private static List<Point> reconstructPath(Node endNode) { List<Point> path = new ArrayList<>(); Node current = endNode; while (current != null) { path.add(0, new Point(current.x, current.y)); current = current.parent; } return path; } private static List<Point> getNeighbors(GameMap map, int x, int y) { List<Point> neighbors = new ArrayList<>(); int[][] directions = {{0,1}, {1,0}, {0,-1}, {-1,0}}; // 4-directional movement for (int[] dir : directions) { int newX = x + dir[0]; int newY = y + dir[1]; if (map.isInBounds(newX, newY) && map.isWalkable(newX, newY)) { neighbors.add(new Point(newX, newY)); } } return neighbors; } private static float heuristic(int x1, int y1, int x2, int y2) { return Math.abs(x1 - x2) + Math.abs(y1 - y2); // Manhattan distance } private static float getMovementCost(GameMap map, int fromX, int fromY, int toX, int toY) { Tile tile = map.getTile(toX, toY); return tile != null ? tile.getType().getMovementCost() : 1.0f; } private static class Node implements Comparable<Node> { int x, y; Node parent; float gCost; // Distance from start float hCost; // Heuristic to end float fCost; // gCost + hCost Node(int x, int y) { this(x, y, null, Float.MAX_VALUE, Float.MAX_VALUE); } Node(int x, int y, Node parent, float gCost, float hCost) { this.x = x; this.y = y; this.parent = parent; this.gCost = gCost; this.hCost = hCost; this.fCost = gCost + hCost; } @Override public int compareTo(Node other) { return Float.compare(this.fCost, other.fCost); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Node node = (Node) obj; return x == node.x && y == node.y; } @Override public int hashCode() { return Objects.hash(x, y); } } }

5. Map Layer System

public abstract class MapLayer { protected String name; protected boolean visible; protected float opacity; public MapLayer(String name) { this.name = name; this.visible = true; this.opacity = 1.0f; } public abstract void render(Graphics2D g, int tileSize, int offsetX, int offsetY); // Getters and setters public String getName() { return name; } public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } public float getOpacity() { return opacity; } public void setOpacity(float opacity) { this.opacity = opacity; } } public class ObjectLayer extends MapLayer { private List<MapObject> objects; public ObjectLayer(String name) { super(name); this.objects = new ArrayList<>(); } public void addObject(MapObject object) { objects.add(object); } public void removeObject(MapObject object) { objects.remove(object); } @Override public void render(Graphics2D g, int tileSize, int offsetX, int offsetY) { if (!visible) return; Composite originalComposite = g.getComposite(); g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity)); for (MapObject obj : objects) { obj.render(g, tileSize, offsetX, offsetY); } g.setComposite(originalComposite); } public List<MapObject> getObjectsAt(int x, int y, int tileSize) { return objects.stream() .filter(obj -> obj.containsPoint(x, y, tileSize)) .collect(Collectors.toList()); } } public abstract class MapObject { protected int x, y; protected String name; protected Map<String, String> properties; public MapObject(int x, int y, String name) { this.x = x; this.y = y; this.name = name; this.properties = new HashMap<>(); } public abstract void render(Graphics2D g, int tileSize, int offsetX, int offsetY); public abstract boolean containsPoint(int pointX, int pointY, int tileSize); public void setProperty(String key, String value) { properties.put(key, value); } public String getProperty(String key) { return properties.get(key); } // Getters and setters public int getX() { return x; } public int getY() { return y; } public String getName() { return name; } } public class CharacterObject extends MapObject { private Color color; private String character; public CharacterObject(int x, int y, String name, String character, Color color) { super(x, y, name); this.character = character; this.color = color; } @Override public void render(Graphics2D g, int tileSize, int offsetX, int offsetY) { g.setColor(color); g.setFont(new Font("Arial", Font.BOLD, tileSize / 2)); int screenX = x * tileSize + offsetX; int screenY = y * tileSize + offsetY; FontMetrics fm = g.getFontMetrics(); int textWidth = fm.stringWidth(character); int textHeight = fm.getHeight(); g.drawString(character, screenX + (tileSize - textWidth) / 2, screenY + (tileSize + textHeight) / 2 - fm.getDescent()); } @Override public boolean containsPoint(int pointX, int pointY, int tileSize) { int screenX = x * tileSize; int screenY = y * tileSize; return pointX >= screenX && pointX < screenX + tileSize && pointY >= screenY && pointY < screenY + tileSize; } }

6. Map Renderer

public class MapRenderer { private GameMap map; private int tileSize; private int viewportWidth; private int viewportHeight; private int offsetX; private int offsetY; public MapRenderer(GameMap map, int tileSize, int viewportWidth, int viewportHeight) { this.map = map; this.tileSize = tileSize; this.viewportWidth = viewportWidth; this.viewportHeight = viewportHeight; this.offsetX = 0; this.offsetY = 0; } public void render(Graphics2D g) { renderMapTiles(g); renderLayers(g); renderGrid(g); } private void renderMapTiles(Graphics2D g) { int startX = Math.max(0, offsetX / tileSize); int startY = Math.max(0, offsetY / tileSize); int endX = Math.min(map.getWidth(), startX + viewportWidth / tileSize + 1); int endY = Math.min(map.getHeight(), startY + viewportHeight / tileSize + 1); for (int x = startX; x < endX; x++) { for (int y = startY; y < endY; y++) { Tile tile = map.getTile(x, y); if (tile != null) { renderTile(g, tile, x, y); } } } } private void renderTile(Graphics2D g, Tile tile, int x, int y) { int screenX = x * tileSize - offsetX; int screenY = y * tileSize - offsetY; // Draw tile background based on type Color tileColor = getTileColor(tile.getType()); g.setColor(tileColor); g.fillRect(screenX, screenY, tileSize, tileSize); // Draw tile border g.setColor(Color.BLACK); g.drawRect(screenX, screenY, tileSize, tileSize); // Draw entities on tile if (tile.hasEntities()) { renderEntities(g, tile, screenX, screenY); } // Fog of war effect if (!tile.isVisible()) { g.setColor(new Color(0, 0, 0, 150)); g.fillRect(screenX, screenY, tileSize, tileSize); } else if (!tile.isExplored()) { g.setColor(new Color(0, 0, 0, 100)); g.fillRect(screenX, screenY, tileSize, tileSize); } } private Color getTileColor(TileType type) { switch (type) { case GRASS: return new Color(34, 139, 34); case WATER: return new Color(0, 105, 148); case SAND: return new Color(238, 214, 175); case FOREST: return new Color(0, 100, 0); case MOUNTAIN: return new Color(139, 137, 137); case ROAD: return new Color(210, 180, 140); case WALL: return new Color(100, 100, 100); case DOOR: return new Color(139, 69, 19); default: return Color.GRAY; } } private void renderEntities(Graphics2D g, Tile tile, int screenX, int screenY) { g.setColor(Color.RED); g.fillOval(screenX + tileSize/4, screenY + tileSize/4, tileSize/2, tileSize/2); } private void renderLayers(Graphics2D g) { for (MapLayer layer : map.getLayers()) { layer.render(g, tileSize, -offsetX, -offsetY); } } private void renderGrid(Graphics2D g) { g.setColor(new Color(0, 0, 0, 50)); for (int x = 0; x <= viewportWidth; x += tileSize) { g.drawLine(x - (offsetX % tileSize), 0, x - (offsetX % tileSize), viewportHeight); } for (int y = 0; y <= viewportHeight; y += tileSize) { g.drawLine(0, y - (offsetY % tileSize), viewportWidth, y - (offsetY % tileSize)); } } public void centerOn(int x, int y) { offsetX = Math.max(0, x * tileSize - viewportWidth / 2); offsetY = Math.max(0, y * tileSize - viewportHeight / 2); // Clamp to map boundaries offsetX = Math.min(offsetX, map.getWidth() * tileSize - viewportWidth); offsetY = Math.min(offsetY, map.getHeight() * tileSize - viewportHeight); } public Point screenToWorld(int screenX, int screenY) { int worldX = (screenX + offsetX) / tileSize; int worldY = (screenY + offsetY) / tileSize; return new Point(worldX, worldY); } // Getters and setters public void setOffset(int x, int y) { this.offsetX = Math.max(0, Math.min(x, map.getWidth() * tileSize - viewportWidth)); this.offsetY = Math.max(0, Math.min(y, map.getHeight() * tileSize - viewportHeight)); } }

7. Complete Example with Swing GUI

public class TileMapGame extends JPanel { private GameMap map; private MapRenderer renderer; private int tileSize = 32; private Point selectedTile = null; private Point playerPosition = new Point(5, 5); public TileMapGame() { initializeMap(); setupUI(); setupInput(); } private void initializeMap() { map = new GameMap(50, 50, "Fantasy World"); map.generateRandomMap(); // Add player character ObjectLayer objectLayer = new ObjectLayer("Objects"); objectLayer.addObject(new CharacterObject(playerPosition.x, playerPosition.y, "Player", "P", Color.BLUE)); map.addLayer(objectLayer); renderer = new MapRenderer(map, tileSize, 800, 600); renderer.centerOn(playerPosition.x, playerPosition.y); } private void setupUI() { setPreferredSize(new Dimension(800, 600)); setBackground(Color.BLACK); } private void setupInput() { addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { Point worldPos = renderer.screenToWorld(e.getX(), e.getY()); selectedTile = worldPos; if (SwingUtilities.isLeftMouseButton(e)) { // Move player to selected tile if (map.isWalkable(worldPos.x, worldPos.y)) { movePlayerTo(worldPos.x, worldPos.y); } } else if (SwingUtilities.isRightMouseButton(e)) { // Change tile type map.setTile(worldPos.x, worldPos.y, TileType.WATER); } repaint(); } }); addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { handleKeyPress(e); } }); setFocusable(true); } private void movePlayerTo(int x, int y) { List<Point> path = map.findPath(playerPosition.x, playerPosition.y, x, y); if (!path.isEmpty()) { // Simple movement - just jump to destination // In a real game, you'd animate through the path playerPosition.setLocation(x, y); renderer.centerOn(x, y); // Update fog of war updateVisibility(); } } private void updateVisibility() { // Simple visibility - reveal tiles around player int visionRange = 5; for (int dx = -visionRange; dx <= visionRange; dx++) { for (int dy = -visionRange; dy <= visionRange; dy++) { int x = playerPosition.x + dx; int y = playerPosition.y + dy; if (map.isInBounds(x, y)) { Tile tile = map.getTile(x, y); tile.setExplored(true); tile.setVisible(true); } } } } private void handleKeyPress(KeyEvent e) { int newX = playerPosition.x; int newY = playerPosition.y; switch (e.getKeyCode()) { case KeyEvent.VK_W: case KeyEvent.VK_UP: newY--; break; case KeyEvent.VK_S: case KeyEvent.VK_DOWN: newY++; break; case KeyEvent.VK_A: case KeyEvent.VK_LEFT: newX--; break; case KeyEvent.VK_D: case KeyEvent.VK_RIGHT: newX++; break; } if (map.isWalkable(newX, newY)) { playerPosition.setLocation(newX, newY); renderer.centerOn(newX, newY); updateVisibility(); repaint(); } } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; // Enable anti-aliasing g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); renderer.render(g2d); // Draw selected tile highlight if (selectedTile != null) { int screenX = selectedTile.x * tileSize - renderer.offsetX; int screenY = selectedTile.y * tileSize - renderer.offsetY; g2d.setColor(new Color(255, 255, 0, 100)); g2d.fillRect(screenX, screenY, tileSize, tileSize); g2d.setColor(Color.YELLOW); g2d.drawRect(screenX, screenY, tileSize, tileSize); } // Draw UI information drawUI(g2d); } private void drawUI(Graphics2D g2d) { g2d.setColor(Color.WHITE); g2d.drawString(String.format("Player: (%d, %d)", playerPosition.x, playerPosition.y), 10, 20); g2d.drawString("WASD/Arrows: Move | Click: Move/Select | Right-click: Edit", 10, 40); if (selectedTile != null) { Tile tile = map.getTile(selectedTile.x, selectedTile.y); if (tile != null) { g2d.drawString(String.format("Selected: %s (%d, %d) - Walkable: %s", tile.getType().getName(), selectedTile.x, selectedTile.y, tile.isWalkable()), 10, 60); } } } public static void main(String[] args) { SwingUtilities.invokeLater(() -> { JFrame frame = new JFrame("Tile-Based Game Map"); TileMapGame game = new TileMapGame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(game); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); game.requestFocusInWindow(); }); } }

Features Included

  1. Tile System: Different terrain types with properties
  2. Pathfinding: A* algorithm for movement
  3. Layered Rendering: Support for multiple map layers
  4. Fog of War: Visibility and exploration system
  5. Object System: Game objects on the map
  6. Interactive GUI: Mouse and keyboard controls
  7. Map Generation: Procedural map creation

Extensions

You can extend this system with:

  • Different map generators (caves, dungeons, cities)
  • Saving/loading maps
  • Multiple map levels
  • Combat system
  • Inventory and items
  • NPC AI and behavior
  • Multiplayer support

This tile-based map system provides a solid foundation for various types of 2D games including RPGs, strategy games, and roguelikes.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper