Help with Rendering Maps in Our Educational 2D Game Using libtiled-java

Hello, everyone!

We are a team of beginner programmers currently working on an educational 2D game about computers. As we develop our game, we’ve encountered some challenges with rendering the game map. We are using the libtiled-java library to handle our maps, but we’ve run into several issues that are making development difficult.

We would really appreciate any guidance, suggestions, or alternative approaches that might help us solve these problems. If you have experience with libtiled-java or know of better solutions for rendering maps in a Java-based 2D game, please share your insights!

To give you a better idea of what we are working on, I’ll provide our code below. Any advice or recommendations would be greatly appreciated. Thanks in advance for your help!

Edit:

The game map is not displaying the intended visual environment. Instead of showing the correct map, the game window is filled with blue rectangular tiles that are obscuring the actual map content.

Additionally, the game includes a total of 5 bosses, each with its own unique map However, this issue persists preventing the correct environment from rendering.

Im sorry i didnt include the issue ill add more context for the code

package main;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JPanel;
import entity.Player;
import entity.Boss;
import entity.SteelWard;
import map.MapManager;
import entity.BlazeCinder;
import entity.Memorix;
import entity.Glitchron;
import ui.Dialog;
import ui.DialogManager;
import org.mapeditor.core.MapObject; // Add this import


public class GamePanel extends JPanel implements Runnable {

    //screen settings
    final int originalTileSize = 16;
    final int scale = 3;

    public final int tileSize = originalTileSize * scale; //48X48 TILE
    public final int maxScreenCol = 16;
    public final int maxScreenRow = 12;
    public final int screenWidth = tileSize * maxScreenCol;
    public final int screenHeight = tileSize * maxScreenRow;

    //Fps
    int fps = 60;

    // Game State
    public enum GameState {
        PLAY, DIALOG, COMBAT
    }
    private GameState gameState;

    // Systems
    KeyControl keyHandler = new KeyControl();
    Thread gameThread;
    public Player player;
    Dialog dialog;
    DialogManager dialogManager;
    Boss currentBoss;
    private Map<String, Boss> bosses = new HashMap<>();
    private Map<Boss, Boolean> bossesDefeated = new HashMap<>();
    public MapManager mapManager;

    public GamePanel() {
        this.setPreferredSize(new Dimension(screenWidth, screenHeight));
        this.setBackground(Color.black);
        this.setDoubleBuffered(true);
        this.addKeyListener(keyHandler);
        this.setFocusable(true);
        this.requestFocusInWindow();

        // Add mouse listeners
        MouseHandler mouseHandler = new MouseHandler();
        this.addMouseListener(mouseHandler);
        this.addMouseMotionListener(mouseHandler);

        setupGame();
    }

    private class MouseHandler extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            if (gameState == GameState.COMBAT && currentBoss != null) {
                if (currentBoss instanceof BlazeCinder) {
                    ((BlazeCinder) currentBoss).handleMousePress(e.getX(), e.getY());
                } else if (currentBoss instanceof Memorix) {
                    ((Memorix) currentBoss).handleMouseClick(e.getX(), e.getY());
                } else if (currentBoss instanceof Glitchron) {
                    ((Glitchron) currentBoss).handleMousePress(e.getX(), e.getY());
                }
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if (gameState == GameState.COMBAT && currentBoss != null) {
                if (currentBoss instanceof BlazeCinder) {
                    ((BlazeCinder) currentBoss).handleMouseRelease(e.getX(), e.getY());
                } else if (currentBoss instanceof Glitchron) {
                    ((Glitchron) currentBoss).handleMouseRelease(e.getX(), e.getY());
                }
            }
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (gameState == GameState.COMBAT && currentBoss != null) {
                if (currentBoss instanceof BlazeCinder) {
                    ((BlazeCinder) currentBoss).handleMouseDrag(e.getX(), e.getY());
                } else if (currentBoss instanceof Glitchron) {
                    ((Glitchron) currentBoss).handleMouseDrag(e.getX(), e.getY());
                }
            }
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            // Handle mouse movement if needed
        }
    }

    public void setupGame() {
        player = new Player(this, keyHandler);
        dialog = new Dialog(this);
        dialogManager = new DialogManager(dialog);
        mapManager = new MapManager(this);
        gameState = GameState.PLAY;

        // Start with intro dialog
        gameState = GameState.DIALOG;
        dialogManager.showStoryDialog("game_start");

        // Initialize bosses at specific tile positions
        bosses.put("steelward", new SteelWard(this, dialogManager, 20, 15)); // x=20, y=15 tiles
        bosses.put("blazecinder", new BlazeCinder(this, dialogManager, 40, 15));
        bosses.put("memorix", new Memorix(this, dialogManager, 60, 15));
        bosses.put("glitchron", new Glitchron(this, dialogManager, 80, 15));

        for (Boss boss : bosses.values()) {
            bossesDefeated.put(boss, false);
        }
    }

    public void startGameThread() {
        gameThread = new Thread(this);
        gameThread.start();
    }

    @Override
    public void run() {
        double drawInterval = 1000000000.0 / fps;
        double delta = 0;
        long lastTime = System.nanoTime();
        long timer = 0;
        int drawCount = 0;

        while (gameThread != null) {
            long now = System.nanoTime();
            delta += (now - lastTime) / drawInterval;
            timer += (now - lastTime);
            lastTime = now;

            if (delta >= 1) {
                update();
                repaint();
                delta--;
                drawCount++;
            }

            if (timer >= 1000000000) {
                System.out.println("FPS: " + drawCount);
                drawCount = 0;
                timer = 0;
            }
        }
    }

    public void update() {
        if (gameState == GameState.PLAY) {
            player.update();
            checkAreaTransitions();
            
            // Handle debug keys
            if (keyHandler.debugPressed) {
                keyHandler.debugPressed = false;
                toggleDebugVisualizations();
            }
        } else if (gameState == GameState.DIALOG) {
            if (keyHandler.spacePressed) {
                keyHandler.spacePressed = false;
                if (dialog.isActive()) {
                    dialog.nextLine();
                } else {
                    gameState = GameState.PLAY;
                }
            }
        } else if (gameState == GameState.COMBAT) {
            if (keyHandler.spacePressed) {
                keyHandler.spacePressed = false;
                if (dialog.isActive()) {
                    dialog.nextLine();
                }
            }
            // Handle combat input (A, B, C, D keys for quiz answers)
            handleCombatInput();

            // Update boss animations/state
            if (currentBoss != null) {
                currentBoss.update();
            }
        }
    }
    
    private void toggleDebugVisualizations() {
        // Toggle grid overlay
        mapManager.toggleDebugGrid();
        
        // Toggle collision overlay
        mapManager.toggleCollisionMapDisplay(); // This method is now properly defined
    }

    private void handleCombatInput() {
        if (currentBoss != null && !dialog.isActive() && currentBoss instanceof SteelWard) {
            String answer = null;
            if (keyHandler.aPressed) {
                answer = "A";
                keyHandler.aPressed = false;
            } else if (keyHandler.bPressed) {
                answer = "B";
                keyHandler.bPressed = false;
            } else if (keyHandler.cPressed) {
                answer = "C";
                keyHandler.cPressed = false;
            } else if (keyHandler.dPressed) {
                answer = "D";
                keyHandler.dPressed = false;
            }

            if (answer != null) {
                currentBoss.handleAnswer(answer);
                if (currentBoss.isDefeated()) {
                    gameState = GameState.PLAY;
                    currentBoss = null;
                }
            }
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;

        // Enable antialiasing for smoother rendering
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // Enable bilinear interpolation for image scaling
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        try {
            mapManager.draw(g2);


            // Draw active boss or all inactive bosses
            if (gameState == GameState.COMBAT && currentBoss != null) {
                currentBoss.draw(g2);
            } else {
                for (Boss boss : bosses.values()) {
                    if (!bossesDefeated.get(boss)) {
                        boss.draw(g2);
                    }
                }
            }
            player.draw(g2);

            if (dialog.isActive()) {
                dialog.draw(g2);
            }
        } catch (Exception e) {
            System.err.println("Error during rendering: " + e.getMessage());
            e.printStackTrace();

        }
        g2.dispose();
    }


    private void checkAreaTransitions() {
        if (gameState != GameState.PLAY) return;

        Rectangle playerBox = player.getCollisionBox();

        // Check for boss area entry
        for (Map.Entry<String, Boss> entry : bosses.entrySet()) {
            Boss boss = entry.getValue();

            //Make sure boss is created and not defeated
            if (!bossesDefeated.get(boss) && boss != null) {
                //Check player is near trigger and we aren't in a different arena
                if (boss.getTriggerArea().intersects(playerBox) && !mapManager.getCurrentMapKey().contains("arena")) {
                    startBossBattle(boss);
                    return;
                }
            }
        }

        // Check for boss defeat and return to lab
        if (currentBoss != null && currentBoss.isDefeated()) {
            bossesDefeated.put(currentBoss, true);
            currentBoss = null;
            returnToLab();
        }
    }

    private void startBossBattle(Boss boss) {
        currentBoss = boss;
        gameState = GameState.COMBAT;
        mapManager.loadMap(boss.getName() + "_arena"); // Loads BOSSNAME_arena.tmx
        mapManager.centerCamera(); // Centers camera on player
        resetPlayerPositionForArena();
        currentBoss.startBattle();
    }

    private void returnToLab() {
        gameState = GameState.PLAY;
        mapManager.loadMap("lab");
        mapManager.centerCamera();
        resetPlayerPositionForLab();
    }

    // Adjust these based on starting location in your lab map
    private void resetPlayerPositionForLab() {
        MapObject playerStart = mapManager.getMapObject("startpositions", "player_start");
        if (playerStart != null) {
            player.setX((int) playerStart.getX());
            player.setY((int) playerStart.getY());
        } else {
            System.out.println("No player_start object found in lab map!");
            //Fallback default
            player.setX(screenWidth/2);
            player.setY(screenHeight/2);
        }
    }

    // Adjust these based on starting locations in your arena maps
    private void resetPlayerPositionForArena() {
        MapObject playerStart = mapManager.getMapObject("startpositions", "player_start");
        if (playerStart != null) {
            player.setX((int) playerStart.getX());
            player.setY((int) playerStart.getY());
        } else {
            System.out.println("No player_start object found in arena map!");
            //Fallback default
            player.setX(screenWidth / 2);
            player.setY(screenHeight / 2);
        }


        if (currentBoss != null) {
            MapObject bossStart = mapManager.getMapObject("startpositions", "boss_start");
            if (bossStart != null) {
                currentBoss.setWorldX((int) bossStart.getX());
                currentBoss.setWorldY((int) bossStart.getY());
            } else {
                System.out.println("No boss_start object found in arena map. Keeping default position");
            }
            currentBoss.updateTriggerArea(); //VERY IMPORTANT to update it!!!
        }
     }
    
    // Add a getter for the currentBoss field
    public Boss getCurrentBoss() {
        return currentBoss;
    }
}

// TileManager.java
package tile;

import java.awt.Graphics2D;
import java.io.IOException;
import java.net.URL;
import main.GamePanel;
import org.mapeditor.core.Map;
import org.mapeditor.core.MapLayer;
import org.mapeditor.core.ObjectGroup;
import org.mapeditor.core.TileLayer;
import org.mapeditor.io.TMXMapReader;
import org.mapeditor.view.MapRenderer;
import org.mapeditor.view.OrthogonalRenderer;
import java.awt.Color;
import org.mapeditor.view.IsometricRenderer;
import org.mapeditor.view.HexagonalRenderer;
import util.WorldCoordinate;
import java.awt.geom.AffineTransform;

public class TileManager {

private GamePanel gp;
private Map currentMap;
public MapRenderer mapRenderer; // Changed to public for direct access
private boolean[][] collisionMap;
private int mapPixelWidth;
private int mapPixelHeight;
private int tmxTileWidth;  // Original tile width from the TMX file
private int tmxTileHeight; // Original tile height from the TMX file
private float scaleX;      // Scale factor for X direction
private float scaleY;      // Scale factor for Y direction
private boolean showCollisionMap = false;

public TileManager(GamePanel gp) {
    this.gp = gp;
}

public void loadMap(String mapPath) {
    try {
        TMXMapReader mapReader = new TMXMapReader();
        URL mapUrl = getClass().getResource(mapPath);

        if (mapUrl == null) {
            throw new IOException("Cannot find resource: " + mapPath);
        }

        currentMap = mapReader.readMap(mapUrl.toExternalForm());
        
        // Store the original TMX tile dimensions
        tmxTileWidth = currentMap.getTileWidth();
        tmxTileHeight = currentMap.getTileHeight();
        
        // Calculate scale factors between TMX tile size and game tile size
        scaleX = (float)gp.tileSize / tmxTileWidth;
        scaleY = (float)gp.tileSize / tmxTileHeight;
        
        System.out.println("TMX tile size: " + tmxTileWidth + "x" + tmxTileHeight);
        System.out.println("Game tile size: " + gp.tileSize + "x" + gp.tileSize);
        System.out.println("Scale factors: " + scaleX + "x" + scaleY);
        
        mapRenderer = createRenderer(currentMap);
        
        // Calculate map dimensions (in pixels)
        mapPixelWidth = Math.round(currentMap.getWidth() * tmxTileWidth * scaleX);
        mapPixelHeight = Math.round(currentMap.getHeight() * tmxTileHeight * scaleY);
        
        System.out.println("Map dimensions: " + mapPixelWidth + "x" + mapPixelHeight + " pixels");
        
        initializeCollisionMap();

    } catch (Exception e) {
        System.err.println("FATAL ERROR: Failed to load map: " + mapPath);
        e.printStackTrace();
        System.exit(1);
    }
}

private MapRenderer createRenderer(Map map) {
    switch (map.getOrientation()) {
        case ORTHOGONAL:
            return new OrthogonalRenderer(map);
        case ISOMETRIC:
            return new IsometricRenderer(map);
        case HEXAGONAL:
            return new HexagonalRenderer(map);
        default:
            throw new RuntimeException("Unsupported map orientation: " + map.getOrientation());
    }
}

private static final String[] COLLISION_LAYER_NAMES = {"collision", "Collision", "collisions", "Collisions"};

private void initializeCollisionMap() {
    collisionMap = new boolean[currentMap.getHeight()][currentMap.getWidth()];
    
    // Find collision layer
    TileLayer collisionLayer = findCollisionLayer();
    if (collisionLayer != null) {
        for (int y = 0; y < currentMap.getHeight(); y++) {
            for (int x = 0; x < currentMap.getWidth(); x++) {
                collisionMap[y][x] = collisionLayer.getTileAt(x, y) != null;
            }
        }
    }
}

private TileLayer findCollisionLayer() {
    for (String layerName : COLLISION_LAYER_NAMES) {
        for (MapLayer layer : currentMap.getLayers()) {
            if (layer instanceof TileLayer && 
                layer.getName().equalsIgnoreCase(layerName)) {
                return (TileLayer) layer;
            }
        }
    }
    return null;
}

@SuppressWarnings("unused")
private void createDefaultMap() {
    collisionMap = new boolean[gp.maxScreenRow][gp.maxScreenCol];
    for (int row = 0; row < gp.maxScreenRow; row++) {
        for (int col = 0; col < gp.maxScreenCol; col++) {
            collisionMap[row][col] = (row == 0 || row == gp.maxScreenRow - 1
                    || col == 0 || col == gp.maxScreenCol - 1);
        }
    }
}

public void draw(Graphics2D g2) {
    if (currentMap == null || mapRenderer == null) return;

    // Store original transform for restoration
    AffineTransform originalTransform = g2.getTransform();

    // Calculate scaled camera position
    int cameraX = gp.player.getX() - (gp.screenWidth / 2);
    int cameraY = gp.player.getY() - (gp.screenHeight / 2);

    // Clamp camera to scaled map bounds
    int maxX = getMapPixelWidth() - gp.screenWidth;
    int maxY = getMapPixelHeight() - gp.screenHeight;
    cameraX = Math.max(0, Math.min(cameraX, maxX));
    cameraY = Math.max(0, Math.min(cameraY, maxY));

    // Apply camera transform and scaling
    g2.translate(-cameraX, -cameraY);
    g2.scale(WorldCoordinate.SCALE, WorldCoordinate.SCALE);

    // Draw all visible layers
    for (MapLayer layer : currentMap.getLayers()) {
        if (layer.isVisible()) {
            if (layer instanceof TileLayer) {
                mapRenderer.paintTileLayer(g2, (TileLayer) layer);
            } else if (layer instanceof ObjectGroup) {
                mapRenderer.paintObjectGroup(g2, (ObjectGroup) layer);
            }
        }
    }

    // Restore original transform before UI elements
    g2.setTransform(originalTransform);
}

public void drawDebugCollisionMap(Graphics2D g2, int cameraX, int cameraY) {
    if (!showCollisionMap) return;
    
    g2.setColor(new Color(255, 0, 0, 100));
    for (int row = 0; row < collisionMap.length; row++) {
        for (int col = 0; col < collisionMap[0].length; col++) {
            if (collisionMap[row][col]) {
                int x = Math.round(col * tmxTileWidth * scaleX);
                int y = Math.round(row * tmxTileHeight * scaleY);
                int width = Math.round(tmxTileWidth * scaleX);
                int height = Math.round(tmxTileHeight * scaleY);
                g2.fillRect(x, y, width, height);
            }
        }
    }
}

public boolean isSolid(int worldX, int worldY) {
    // Convert world coordinates to TMX tile coordinates
    int tileX = (int)(WorldCoordinate.worldToTmx(worldX) / tmxTileWidth);
    int tileY = (int)(WorldCoordinate.worldToTmx(worldY) / tmxTileHeight);

    if (tileX < 0 || tileX >= currentMap.getWidth() || 
        tileY < 0 || tileY >= currentMap.getHeight()) {
        return true;
    }

    return collisionMap[tileY][tileX];
}

// Add debugging methods to help visualize coords
public void drawDebugGrid(Graphics2D g2) {
    g2.setColor(new Color(0, 255, 255, 60)); // Cyan with alpha
    
    for (int x = 0; x <= mapPixelWidth; x += Math.round(tmxTileWidth * scaleX)) {
        g2.drawLine(x, 0, x, mapPixelHeight);
    }
    
    for (int y = 0; y <= mapPixelHeight; y += Math.round(tmxTileHeight * scaleY)) {
        g2.drawLine(0, y, mapPixelWidth, y);
    }
}

// Add getters for tile dimensions
public float getScaleX() { return scaleX; }
public float getScaleY() { return scaleY; }
public int getTmxTileWidth() { return tmxTileWidth; }
public int getTmxTileHeight() { return tmxTileHeight; }

public int getMapPixelWidth() {
    return mapPixelWidth;
}

public int getMapPixelHeight() {
    return mapPixelHeight;
}

public Map getCurrentMap() {
    return currentMap;
}

// Add this method to get the map renderer
public MapRenderer getMapRenderer() {
    return mapRenderer;
}

// Add this method to toggle collision map display
public void toggleCollisionMapDisplay() {
    showCollisionMap = !showCollisionMap;
    System.out.println("Collision map display: " + (showCollisionMap ? "ON" : "OFF"));
}

}

// MapManager.java
package map;

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.HashMap;
import java.util.Map;
import main.GamePanel;
import tile.TileManager;
import org.mapeditor.core.MapObject;
import org.mapeditor.core.MapLayer;
import org.mapeditor.core.ObjectGroup;
import org.mapeditor.core.TileLayer;
import util.WorldCoordinate;

public class MapManager {
    private GamePanel gp;
    private TileManager tileM;
    private Map<String, String> mapCache;
    private String currentMapKey;
    private boolean showDebugGrid = false;

    public MapManager(GamePanel gp) {
        this.gp = gp;
        this.tileM = new TileManager(gp);
        this.mapCache = new HashMap<>();

        // Initialize map paths
        mapCache.put("lab", "/maps/MAPFloor1.tmx");
       // mapCache.put("steelward_arena", "/maps/SteelWardArena.tmx");
        //mapCache.put("blazecinder_arena", "/maps/BlazeCinderArena.tmx");
        //mapCache.put("memorix_arena", "/maps/MemorixArena.tmx");
        //mapCache.put("glitchron_arena", "/maps/GlitchronArena.tmx");
        //mapCache.put("exodus_arena", "/maps/ExodusArena.tmx");

        currentMapKey = "lab";
        loadMap(currentMapKey);
    }

    public void centerCamera() {
        gp.player.setX((gp.screenWidth / 2) - (gp.tileSize / 2)); // Center horizontally
        gp.player.setY((gp.screenHeight / 2) - (gp.tileSize / 2)); // Center vertically
        gp.player.direction = "down"; // Set default player direction
    }

    public void loadMap(String mapKey) {
        String mapPath = mapCache.get(mapKey);
        if (mapPath == null) {
            System.err.println("Invalid map key: " + mapKey);
            return;
        }

        try {
            tileM.loadMap(mapPath);
            currentMapKey = mapKey;
            System.out.println("Loaded map: " + mapKey);
            repositionMapObjects();
        } catch (Exception e) {
            System.err.println("Failed to load map: " + mapKey);
            e.printStackTrace();
        }
    }

    private void repositionMapObjects() {
        // Handle player spawn point
        MapObject playerSpawn = getMapObject("startpositions", "player_start");
        if (playerSpawn != null) {
            float tmxX = (float)playerSpawn.getX();
            float tmxY = (float)playerSpawn.getY();
            gp.player.setX(WorldCoordinate.tmxToWorld(tmxX));
            gp.player.setY(WorldCoordinate.tmxToWorld(tmxY));
        }

        // Handle boss spawn point if in arena
        if (currentMapKey.contains("arena") && gp.getCurrentBoss() != null) {
            MapObject bossSpawn = getMapObject("startpositions", "boss_start");
            if (bossSpawn != null) {
                gp.getCurrentBoss().setWorldX(WorldCoordinate.tmxToWorld((float)bossSpawn.getX()));
                gp.getCurrentBoss().setWorldY(WorldCoordinate.tmxToWorld((float)bossSpawn.getY()));
                gp.getCurrentBoss().updateTriggerArea();
            }
        }
    }

    public boolean checkCollision(int x, int y) {
        try {
            return tileM.isSolid(x, y);
        } catch (Exception e) {
            System.err.println("Collision check failed: " + e.getMessage());
            return true; // Safe default - treat as solid if check fails
        }
    }

    public void draw(Graphics2D g2) {
        org.mapeditor.core.Map map = tileM.getCurrentMap();
        
        if (map == null) {
            g2.setColor(Color.RED);
            g2.drawString("No map loaded!", 100, 100);
            return;
        }

        // Calculate camera center (target)
        int cameraX = gp.player.getX() - gp.screenWidth / 2;
        int cameraY = gp.player.getY() - gp.screenHeight / 2;

        // Clamp camera position to map boundaries
        cameraX = Math.max(0, Math.min(cameraX, tileM.getMapPixelWidth() - gp.screenWidth));
        cameraY = Math.max(0, Math.min(cameraY, tileM.getMapPixelHeight() - gp.screenHeight));

        // Apply camera transform (scrolling)
        g2.translate(-cameraX, -cameraY);
        
        // Apply the same scale factor to the renderer that we used to calculate map dimensions
        g2.scale(tileM.getScaleX(), tileM.getScaleY());

        // Draw each visible layer
        for (MapLayer layer : map.getLayers()) {
            if (layer.isVisible()) {
                if (layer instanceof TileLayer) {
                    tileM.mapRenderer.paintTileLayer(g2, (TileLayer) layer); // Use public field instead of getter
                } else if (layer instanceof ObjectGroup) {
                    tileM.mapRenderer.paintObjectGroup(g2, (ObjectGroup) layer); // Use public field instead of getter
                }
            }
        }
        
        // Restore scale before drawing debug info
        g2.scale(1/tileM.getScaleX(), 1/tileM.getScaleY());
        
        // Draw debug grid if enabled
        if (showDebugGrid) {
            tileM.drawDebugGrid(g2);
        }
        
        // Debug collision visualization
        tileM.drawDebugCollisionMap(g2, 0, 0);
        
        g2.translate(cameraX, cameraY);
    }

    public String getCurrentMapKey() {
        return currentMapKey;
    }

    public int getMapPixelWidth() {
        return tileM.getMapPixelWidth();
    }

    public int getMapPixelHeight() {
        return tileM.getMapPixelHeight();
    }

    public int getTileSize() {
        return gp.tileSize;
    }

    public MapObject getMapObject(String layerName, String objectName) {
        for (MapLayer layer : tileM.getCurrentMap().getLayers()) {
            if (layer instanceof ObjectGroup && layer.getName().equalsIgnoreCase(layerName)) {
                ObjectGroup objectGroup = (ObjectGroup) layer;
                for (MapObject object : objectGroup.getObjects()) {
                    if (object.getName().equalsIgnoreCase(objectName)) {
                        return object;
                    }
                }
            }
        }
        return null;
    }

    // Add a toggle for debugging grid
    public void toggleDebugGrid() {
        showDebugGrid = !showDebugGrid;
        System.out.println("Debug grid: " + (showDebugGrid ? "ON" : "OFF"));
    }

    // Add missing method for toggling collision map display
    public void toggleCollisionMapDisplay() {
        tileM.toggleCollisionMapDisplay();
    }
}

Not sure if I can help personally but it would be helpful to describe what issues you are having so that people know where you need help with the code