Java - движение и столкновение со стеной на карте 2d-массива

Я создаю pacman для проекта, но у меня возникли проблемы с перемещением моего персонажа. В частности, реализуя столкновение со стеной, я могу нормально перемещать персонажа, но не тогда, когда пытаюсь добавить столкновение со стеной... Очевидно, я делаю что-то не так.

Когда я пытаюсь передвигаться, я не могу, он обнаруживает ввод клавиш со стрелками, но не перемещает персонажа, из-за этого я считаю, что мой оператор if неверен, поэтому я попытался просто настроить его так, чтобы он обрабатывал столкновение со стеной при подъеме. Это привело к появлению кажущихся невидимыми стен или мест, куда я просто не мог переместиться. Я смотрел на это некоторое время и еще не нашел решения.

Текущий код

    package main;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;

public class game extends Canvas implements Runnable {
    final static String NL = System.getProperty("line.separator");
    public static int tile;
    public boolean running = false;
    public String name = "PacMan";
    public static final int WIDTH = 23;//578;
    public static final int HEIGHT = 29;//720;
    public static final int SCALE = 25;
    private JFrame frame;
    private Image bImage;
    private Graphics db;
    private input input;
    public static int playerPosX = 0;
    public static int playerPosY = 0;
    public static int playerHeight = 16;
    public static int playerWidth = 16;
    public static int speed = 3;

    static BufferedImage background = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
    static BufferedImage pacman = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
    static BufferedImage settingsBackground = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
    static BufferedImage level1 = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
    static BufferedImage level2 = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
    static BufferedImage points = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
    static BufferedImage point = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
    static BufferedImage wall = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
    static BufferedImage blackBack = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
    static BufferedImage pointBack = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;


    public static boolean key_down = false;
    public static boolean key_up = false;
    public static boolean key_right = false;
    public static boolean key_left = false;
    public boolean wallDrawn = false;
    public boolean loaded = false;

    public static int tc = 0;
    public int tileX, tileY, pTileX, pTileY;

    public game() {


        setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
        setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE)); // keeps
                                                                        // the
                                                                        // canvas
                                                                        // same
                                                                        // size
        setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));

        frame = new JFrame(name); // creates the frame for our game
        input = new input();

        //if(MAIN_MENU == true && GAME == false){
        //  buttons = new buttons(frame.getContentPane()); //draws the buttons based on the code in our graphics/buttons.java
        //}

        frame.setLayout(new BorderLayout());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // ends program on click of the Exit button in the top right corner
        frame.setLocationByPlatform(true);
        frame.addKeyListener(new input() );
        frame.add(this, BorderLayout.CENTER);
        frame.pack(); // keeps size correct
        frame.setResizable(false); //keep this false, if set to true whenever someone resizes it our code will not function correctly
        frame.setVisible(true);

        this.addKeyListener(input);
        this.createBufferStrategy(2); //I think this is double buffering, honestly not too sure
    }


    public static void main(String[] args) {
        new game().start();
    }

    public void resLoader() {
        try {
            background = ImageIO.read(new File("res\\Background.png"));
            wall = ImageIO.read(new File("res\\maptile.png"));
            pacman = ImageIO.read(new File("res\\pacman.png"));
            settingsBackground = ImageIO.read(new File("res\\Background.png"));
            level1 = ImageIO.read(new File("res\\level1.png"));
            point = ImageIO.read(new File("res\\Points for pacman.png"));
            blackBack = ImageIO.read(new File("res\\blackBack.png"));
            pointBack = ImageIO.read(new File("res\\points.png"));

        } catch (IOException e) {
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~ WARNING! ~~~~~~~~~~~~~~~~~~~~~~~~~~");
            System.out.println("There was am Image unsuccessfully loaded!" + NL + "The game may not work properly, check the load images method for spelling errors!");
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~ WARNING! ~~~~~~~~~~~~~~~~~~~~~~~~~~" + NL + NL);
        }
    }

    public void run() 
    {
        long lastTime = System.nanoTime();
        double nsPerTick = 1000000000 / 60D;
        long lastTimer = System.currentTimeMillis();
        double delta = 0;

        int frames = 0;
        int ticks = 0;

        while (running == true) {
            long now = System.nanoTime();
            delta += (now - lastTime) / nsPerTick;
            lastTime = now;
            boolean render = false;

            while (delta >= 1) {
                ticks++;
                tick();
                delta -= 1;
                render = true;

            }

                try {
                    Thread.sleep(3);        //keep the Frames from going to high
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            if(render == true){
            frames++;
            render();
            }

            if (System.currentTimeMillis() - lastTimer >= 1000) {
                lastTimer +=1000;
                //System.out.println("Frames: " + frames + "   Ticks: " + ticks); 

                frames = 0;
                ticks = 0;
            }
        }

        try {
            Thread.sleep(3);        //keep the Frames from going to high
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void start() {
        resLoader();
        new Thread(this).start();
        running = true;

    }

    public synchronized void stop() {
        running = false;
    }

    public void render () {
        Graphics g = getGraphics();
        if (bImage == null) {

          bImage = createImage (this.getSize().width, this.getSize().height);
          db = bImage.getGraphics ();

        }

        db.setColor (getBackground ());
        db.fillRect (0, 0, this.getSize().width, this.getSize().height);

        paint (db);

        g.drawImage (bImage, 0, 0, this);
      }



            public void paint (Graphics g) {

            for (int x = 0; x < main.map.width; x++)
              { 
                 for (int y = 0; y < main.map.height; y++)
                 {
                     tile = main.map.level1[y][x];
                     tileX = x * 21;
                     tileY = y * 26;


                 if(tile == 0){
                     g.drawImage(wall,tileX,tileY, 21, 26,null);
                 }

                 if(tile == 1){
                     g.drawImage(pointBack,tileX, tileY, (int)21.42857142857143, 26,null);
                    }

                 if(tile == 2){
                     g.drawImage(blackBack,tileX, tileY, (int)21.42857142857143, 26,null);
                 }

                 if(tile == 5) {
                     if(!loaded) {
                         pTileX = tileX;
                         pTileY = tileY;
                         loaded = true;
                     }
                     g.drawImage(blackBack,tileX, tileY, (int)21.42857142857143, 26,null);
                 }
                 }
              }
            g.drawImage(pacman, pTileX, pTileY, playerWidth, playerHeight, null);
        }





    public void tick() {
        playerPosX = pTileX / 21;
        playerPosY = pTileY / 26;

        int wallPosX, wallPosY;

        if(key_up && map.level1[playerPosX][playerPosY] == 1){
            pTileY = pTileY - speed;
        }

        if(key_down && map.level1[playerPosX][playerPosY] == 1){
            pTileY += speed;
        }

        if(key_left && map.level1[playerPosX][playerPosY] == 1){
            pTileX -= speed;
        }

        if(key_right && map.level1[playerPosX][playerPosY] == 1){
            pTileX += speed;
        }
    }
}

package main;

import java.awt.event.*;

public class input implements KeyListener
{




    public void keyTyped(KeyEvent e) {
    }



    /** Here we have a rather basic key listener
     *  It is set that you can only go left right up or down right now
     *  If more directions are needed delete the "other key = false"
     */

    public void keyPressed(KeyEvent e) {
        System.out.println(KeyEvent.getKeyText(e.getKeyCode()));
        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            game.key_right = true;
            game.key_left = false;
            game.key_up = false;
            game.key_down = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            game.key_right = false;
            game.key_left = true;
            game.key_up = false;
            game.key_down = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_UP) {
            game.key_right = false;
            game.key_left = false;
            game.key_up = true;
            game.key_down = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            game.key_right = false;
            game.key_left = false;
            game.key_up = false;
            game.key_down = true;
        }
    }





    public void keyReleased(KeyEvent e) {
        System.out.println(e.getKeyCode());
        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            game.key_right = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            game.key_left = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_UP) {
            game.key_up = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_DOWN) {
           game.key_down = false;
        }
    }
}

public class map {
    public static int width = 28;
    public static int height = 28;
    public static int[][] level1 = {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0},
        {0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0},
        {0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0},
        {0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0},
        {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
        {0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0},
        {0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0},
        {0,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0},
        {0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,0,0,0,2,2,0,0,0,1,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,0,0,1,1,1,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,0,0,2,2,2,2,0,0,1,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0},
        {0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0},
        {0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0},
        {0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0},
        {0,1,1,1,0,0,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,0,0,1,1,1,0},
        {0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0},
        {0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0},
        {0,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0},
        {0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0},
        {0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0},
        {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    };
}

person roke    schedule 23.10.2014    source источник
comment
getGraphics(); невероятно беспокоит, используете ли вы компонент на основе Swing для рендеринга или используете BufferedStrategy?   -  person MadProgrammer    schedule 24.10.2014
comment
ах, спасибо, что указали на это, я изначально использовал BufferStrattegy, но у меня не было плавного изображения (мигание), поэтому я попытался реализовать двойную буферизацию. Думаю, я забыл поставить bs в методе рендеринга. Я довольно новичок в программировании, поэтому я не уверен на 100% в ответе на ваш вопрос. Если бы это помогло, я мог бы опубликовать остальную часть моего кода.   -  person roke    schedule 24.10.2014
comment
Рассмотрите возможность предоставления запускаемого примера, демонстрирующего вашу проблему. Это приведет к меньшей путанице и лучшим ответам.   -  person MadProgrammer    schedule 24.10.2014
comment
Я вижу много проблем с вашим кодом, помимо обнаружения столкновений. Эти проблемы либо есть, либо будут вызывать проблемы с обнаружением столкновений. Дополнительную помощь можно найти на codereview.stackexchange.com.   -  person ug_    schedule 24.10.2014
comment
У меня сложилось впечатление, что проверка кода предназначена только для завершенного кода.   -  person roke    schedule 24.10.2014
comment
@roke Это так, но хотя ваш код не является полной игрой, он работает без ошибок. Вы можете спросить что-нибудь вроде «У меня есть частично работающая игра, но мне нужна помощь в улучшении моего кода». IMO, в вашем коде много несоответствий, и есть несколько мест, где изменение архитектуры устранит некоторые неотложные проблемы. Я исправил некоторые из них, и ваше обнаружение столкновений несколько сработало, но есть еще много других, которые нуждаются в улучшении. Обнаружение столкновений немного сложно, если вы не делали этого раньше, и проблемы с вашей кодовой базой сделают это намного сложнее.   -  person ug_    schedule 24.10.2014
comment
@roke Я обновил свой ответ, зацени его и желаю удачи.   -  person ug_    schedule 24.10.2014


Ответы (1)


Кроме какой-то глупой ошибки вроде || вместо &&. Было бы очень сложно определить, в чем проблема для чего-то подобного, не видя гораздо больше приложения. Однако дам несколько советов:

  1. Прежде чем добавлять обнаружение столкновений, убедитесь, что ваше движение работает идеально.
  2. При добавлении обнаружения столкновений упростите тестирование до минимума, например, добавьте только 1 стену. Затем постарайтесь изо всех сил пробить эту стену, например, двигаться в нее под каждым углом и следить за странным поведением, например, невозможностью двигаться после столкновения или движения через стену.
  3. Вы обнаружите проблемы при реализации обнаружения столкновений, если вы заставите своего персонажа двигаться, ТОГДА искать столкновения. Вы должны изучить координаты, где будет следующий «шаг», и если вы ударились о стену, то не двигайтесь. Подумайте об этом, если вы перемещаете своего персонажа после того, как программа обнаружит столкновение и увидит, что вы ударились о стену, то технически вы находитесь внутри стены! Вы должны рассчитать следующие координаты для своего персонажа и, если нет столкновения, передать эти координаты персонажу.

Убедитесь, что вы тщательно отлаживаете свою программу, добавляя операторы println: System.out.println("collision!") или отладчик, как вам больше нравится.

это действительно должен быть комментарий, но он слишком длинный...


Редактировать: я вижу, что ваш вопрос о CodeReview был закрыт, вероятно, это было связано с тем, что вы сказали: «мой код не работает». Я сказал вам спросить об этом там, чтобы они в основном делали то, что я делаю здесь. Улучшайте и реорганизовывайте свой код, а не исправляйте обнаружение столкновений. Мне удалось исправить это для вас, но я прокомментировал это, так что ПОСМОТРИТЕ НА МОИ КОММЕНТАРИИ В КОДЕ!!! :)

Вот некоторые из проблем, которые я нашел

  1. Ваши классы должны следовать соглашениям об именах Java, это означает, что CamelCase должен начинаться с заглавных букв, поэтому игра должна быть Game, ввод будет Input ect... Статические переменные всегда должны быть UPPER_SNAKE_CASE, статические переменные всегда объявляются в верхней части класса .
  2. Область ваших переменных повсюду, например, класс Input обнаруживает входные данные, но изменяет переменные в классе Game. Одним из преимуществ ООП является то, что вы можете разбить определенные области вашего приложения на определенные классы, чего вы не хотите, так это объект бога, который у вас есть сейчас с вашим классом Game. Это становится подавляющим и трудноуправляемым. Продолжайте делать это, и очень скоро у вас будет неуправляемая программа.
  3. У вас какие-то странные имена переменных или их странное использование. Я смотрю на pTileX, pTileY, playerPosX, playerPosY, tileX, tileY. Почему 6 переменных? Вам нужно только 2, x и y положение игрока. По этим координатам местоположение плитки можно было найти в любое время.
  4. Ваш класс карты в настоящее время практически бесполезен. У вас есть несколько статических переменных, но вы не используете с ними структуру ООП! У вас есть правильная идея, но давайте воспользуемся структурой класса.
  5. Никогда не объявляйте что-либо как глобальную переменную, если в этом нет необходимости, всегда уменьшайте область действия вашей переменной и увеличивайте ее по мере необходимости. Когда вы станете более опытным программистом, у вас будет больше знаний о том, какой должна быть область действия.

Я собираюсь дать вам исправленную версию вашего кода, для будущих дополнений потребуются дополнительные методы перемещения и другие вещи. Я удалил МНОГО глобальных переменных, которые было просто глупо иметь там.

Game.class

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;

import javax.swing.JFrame;

public class Game extends Canvas implements Runnable {
    public static int playerHeight = 16;
    public static int playerWidth = 16;
    public static int movementSpeed = 3;

    public boolean running = false;

    private JFrame frame;
    private Image bufferImage;
    private Graphics bufferGraphics;
    private Input input;

    private Point playerLocation = null;

    private GameMap currentLevel;


    public Game() {
        Resources.loadResources();
        loadLevel(new GameMap.Level1());

        frame = new JFrame("PacMan"); // creates the frame for our game
        input = new Input();

        // if(MAIN_MENU == true && GAME == false){
        // buttons = new buttons(frame.getContentPane()); //draws the buttons
        // based on the code in our graphics/buttons.java
        // }

        frame.setLayout(new BorderLayout());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // ends program on
                                                                // click of the
                                                                // Exit button
                                                                // in the top
                                                                // right corner
        frame.setLocationByPlatform(true);
        frame.addKeyListener(new Input());
        frame.add(this, BorderLayout.CENTER);
        frame.pack(); // keeps size correct
        frame.setResizable(false); // keep this false, if set to true whenever
                                    // someone resizes it our code will not
                                    // function correctly
        frame.setVisible(true);

        this.addKeyListener(input);
        this.createBufferStrategy(2); // I think this is double buffering,
                                        // honestly not too sure
    }

    /**
     * Loads the level into the game, also changes the dimensions of the window to fit the game
     * @param gameMap
     */
    private void loadLevel(GameMap gameMap) {
        currentLevel = gameMap;

        playerLocation = convertMapLocationToScreenLocation(currentLevel.getPlayerStartLocation());

        Dimension canvasSize = new Dimension(currentLevel.getWidth()*Resources.TILE_WIDTH, currentLevel.getHeight()*Resources.TILE_HEIGHT);

        setMinimumSize(canvasSize);
        setMaximumSize(canvasSize);
        setPreferredSize(canvasSize);

    }

    public void run() {
        long lastTime = System.nanoTime();
        double nsPerTick = 1000000000 / 60D;
        long lastTimer = System.currentTimeMillis();
        double delta = 0;

        int frames = 0;
        int ticks = 0;

        while (running == true) {
            long now = System.nanoTime();
            delta += (now - lastTime) / nsPerTick;
            lastTime = now;
            boolean render = false;

            while (delta >= 1) {
                ticks++;
                tick();
                delta -= 1;
                render = true;

            }

            try {
                Thread.sleep(20); // keep the Frames from going to high
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (render == true) {
                frames++;
                render();
            }

            if (System.currentTimeMillis() - lastTimer >= 1000) {
                lastTimer += 1000;

                frames = 0;
                ticks = 0;
            }
        }

        try {
            Thread.sleep(20); // keep the Frames from going to high
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void start() {
        new Thread(this).start();
        running = true;

    }

    public synchronized void stop() {
        running = false;
    }

    public void render() {
        Graphics g = getGraphics();
        if (bufferImage == null) {

            bufferImage = createImage(this.getSize().width, this.getSize().height);
            bufferGraphics = bufferImage.getGraphics();

        }

        bufferGraphics.setColor(Color.orange);
        bufferGraphics.fillRect(0, 0, this.getSize().width, this.getSize().height);

        drawGame(bufferGraphics);

        g.drawImage(bufferImage, 0, 0, this);
    }

    // this had to be renamed to drawGame, the paint method is used by AWT objects. This caused a serious bug where you would be constantly repainting.
    public void drawGame(Graphics g) {

        for (int x = 0; x < currentLevel.getWidth(); x++) {
            for (int y = 0; y < currentLevel.getHeight(); y++) {
                int tile = currentLevel.getTileAt(x, y);
                int tileX = x * Resources.TILE_WIDTH;
                int tileY = y * Resources.TILE_HEIGHT;

                if (tile == GameMap.TILE_WALL) {
                    g.drawImage(Resources.WALL, tileX, tileY, Resources.TILE_WIDTH, Resources.TILE_HEIGHT, null);
                }

                if (tile == GameMap.TILE_NOTHING) {
                    g.drawImage(Resources.BLACK_BACK, tileX, tileY,
                            Resources.TILE_WIDTH, Resources.TILE_HEIGHT, null);
                }

                if (tile == GameMap.TILE_POINT) {
                    g.drawImage(Resources.POINT, tileX, tileY,
                            Resources.TILE_WIDTH, Resources.TILE_HEIGHT, null);
                }
                /* This is not a good way to find the first location for the player, knowing that location belongs to the Map class
                if (tile == 5) {
                    if (!loaded) {
                        playerPosX = tileX;
                        playerPosY = tileY;
                        loaded = true;
                    }
                    g.drawImage(blackBack, tileX, tileY,
                            (int) 21.42857142857143, 26, null);
                }*/
            }
        }
        g.drawImage(Resources.PACMAN, playerLocation.x, playerLocation.y, playerWidth, playerHeight, null);
    }

    public void tick() {

        int nextPlayerPosX = playerLocation.x;
        int nextPlayerPosY = playerLocation.y;

        if (input.key_up) {
            nextPlayerPosY -= movementSpeed;
        }

        if (input.key_down) {
            nextPlayerPosY += movementSpeed;
        }

        if (input.key_left) {
            nextPlayerPosX -= movementSpeed;
        }

        if (input.key_right) {
            nextPlayerPosX += movementSpeed;
        }

        // lets make sure the next location doesnt collide with a wall, if so then dont move the pacman!
        if(!doesPlayerCollideWith(nextPlayerPosX, nextPlayerPosY, GameMap.TILE_WALL)) {
            playerLocation.setLocation(nextPlayerPosX, nextPlayerPosY);
        }
    }

    /**
     * Looks at the players screen location and gets the map tiles for each corner.
     * @param screenX
     * @param screenY
     * @return the 4 map tiles for each corner of the pac man given the screenX and screenY
     */
    private int[] getPlayerCornerCollisions(int screenX, int screenY) {
        int[] corners = new int[4];
        Point tileLocation = convertScreenLocationToMapLocation(screenX, screenY);
        corners[0] = currentLevel.getTileAt(tileLocation.x, tileLocation.y);

        tileLocation = convertScreenLocationToMapLocation(screenX + playerWidth, screenY);
        corners[1] = currentLevel.getTileAt(tileLocation.x, tileLocation.y);

        tileLocation = convertScreenLocationToMapLocation(screenX, screenY + playerHeight);
        corners[2] = currentLevel.getTileAt(tileLocation.x, tileLocation.y);

        tileLocation = convertScreenLocationToMapLocation(screenX + playerWidth, screenY + playerHeight);
        corners[3] = currentLevel.getTileAt(tileLocation.x, tileLocation.y);
        return corners;
    }

    /**
     * Checks if any corners of the player intersects with the given mapTileType
     * @param screenX
     * @param screenY
     * @param mapTileType
     * @return true if the player intersects with the given map tile type
     */
    public boolean doesPlayerCollideWith(int screenX, int screenY, int mapTileType) {
        for(int tileType : getPlayerCornerCollisions(screenX, screenY)) {
            if(tileType == mapTileType) {
                return true;
            }
        }
        return false;
    }

    /**
     * Takes the screen location and converts it to a coordinate in the map
     * @param location
     * @return
     */
    public Point convertScreenLocationToMapLocation(Point location) {
        return convertScreenLocationToMapLocation(location.x, location.y);
    }
    public Point convertScreenLocationToMapLocation(int x, int y) {
        return new Point(x/Resources.TILE_WIDTH, y/Resources.TILE_HEIGHT);
    }

    public Point convertMapLocationToScreenLocation(Point location) {
        return convertMapLocationToScreenLocation(location.x, location.y);
    }
    public Point convertMapLocationToScreenLocation(int x, int y) {
        return new Point(x*Resources.TILE_WIDTH, y*Resources.TILE_HEIGHT);
    }

    public static void main(String[] args) {
        new Game().start();
    }
}

GameMap.class — ранее Map.class

import java.awt.Point;

public abstract class GameMap {
    public static final int TILE_WALL = 0;
    public static final int TILE_NOTHING = 1;
    public static final int TILE_POINT = 2;
    public static final int TILE_START_LOCATION = 5;



    public abstract int getWidth();
    public abstract int getHeight();
    public abstract int[][] getLevelData();
    public abstract java.awt.Point getPlayerStartLocation();

    public int getTileAt(int x, int y) {
        return getLevelData()[y][x];
    }

    public static class Level1 extends GameMap {

        @Override
        public int getWidth() {
            return LEVEL_1_DATA[0].length;
        }

        @Override
        public int getHeight() {
            return LEVEL_1_DATA.length;
        }

        @Override
        public int[][] getLevelData() {
            return LEVEL_1_DATA;
        }

        @Override
        public Point getPlayerStartLocation() {
            for(int y=0;y<LEVEL_1_DATA.length;y++) {
                for(int x=0;x<LEVEL_1_DATA[y].length;x++) {
                    if(LEVEL_1_DATA[y][x] == GameMap.TILE_START_LOCATION) {
                        return new Point(x, y);
                    }
                }
            }
            // should never reach this unless we forgot to put a start location.
            throw new RuntimeException("No player start location could be found!");
        }
    }

    private static int[][] LEVEL_1_DATA = {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0},
        {0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0},
        {0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0},
        {0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0},
        {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
        {0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0},
        {0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0},
        {0,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0},
        {0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,0,0,0,2,2,0,0,0,1,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,0,0,1,1,1,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,0,0,2,2,2,2,0,0,1,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,0},
        {0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0},
        {0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0},
        {0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0},
        {0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0},
        {0,1,1,1,0,0,1,1,1,1,1,1,1,5,1,1,1,1,1,1,1,1,0,0,1,1,1,0},
        {0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0},
        {0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0},
        {0,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0},
        {0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0},
        {0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0},
        {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    };
}

Input.class

import java.awt.event.*;

public class Input implements KeyListener
{
    public boolean key_down = false;
    public boolean key_up = false;
    public boolean key_right = false;
    public boolean key_left = false;

    public void keyTyped(KeyEvent e) {
    }

    /** Here we have a rather basic key listener
     *  It is set that you can only go left right up or down right now
     *  If more directions are needed delete the "other key = false"
     */

    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            key_right = true;
            key_left = false;
            key_up = false;
            key_down = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            key_right = false;
            key_left = true;
            key_up = false;
            key_down = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_UP) {
            key_right = false;
            key_left = false;
            key_up = true;
            key_down = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            key_right = false;
            key_left = false;
            key_up = false;
            key_down = true;
        }
    }





    public void keyReleased(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            key_right = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            key_left = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_UP) {
            key_up = false;
        }
        if (e.getKeyCode() == KeyEvent.VK_DOWN) {
           key_down = false;
        }
    }
}

Resource.class

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public final class Resources {
    private final static String NL = System.getProperty("line.separator");

    public static final int TILE_WIDTH = 23;// 578;
    public static final int TILE_HEIGHT = 29;// 720;

    public static BufferedImage BACKGROUND = new BufferedImage(TILE_WIDTH, TILE_HEIGHT,
            BufferedImage.TYPE_INT_RGB);
    public static BufferedImage PACMAN = new BufferedImage(TILE_WIDTH, TILE_HEIGHT,
            BufferedImage.TYPE_INT_RGB);
    public static BufferedImage SETTINGS_BACKGROUND = new BufferedImage(TILE_WIDTH, TILE_HEIGHT,
            BufferedImage.TYPE_INT_RGB);
    public static BufferedImage LEVEL1 = new BufferedImage(TILE_WIDTH, TILE_HEIGHT,
            BufferedImage.TYPE_INT_RGB);
    /*public static BufferedImage LEVEL2 = new BufferedImage(TILE_WIDTH, TILE_HEIGHT,
            BufferedImage.TYPE_INT_RGB);*/
    public static BufferedImage POINTS = new BufferedImage(TILE_WIDTH, TILE_HEIGHT,
            BufferedImage.TYPE_INT_RGB);
    public static BufferedImage POINT = new BufferedImage(TILE_WIDTH, TILE_HEIGHT,
            BufferedImage.TYPE_INT_RGB);
    public static BufferedImage WALL = new BufferedImage(TILE_WIDTH, TILE_HEIGHT,
            BufferedImage.TYPE_INT_RGB);
    public static BufferedImage BLACK_BACK = new BufferedImage(TILE_WIDTH, TILE_HEIGHT,
            BufferedImage.TYPE_INT_RGB);
    public static BufferedImage POINT_BACK = new BufferedImage(TILE_WIDTH, TILE_HEIGHT,
            BufferedImage.TYPE_INT_RGB);

    public static void loadResources() {
        try {
            BACKGROUND = ImageIO.read(new File("res\\Background.png"));
            WALL = ImageIO.read(new File("res\\maptile.png"));
            PACMAN = ImageIO.read(new File("res\\pacman.png"));
            SETTINGS_BACKGROUND = ImageIO.read(new File("res\\Background.png"));
            LEVEL1 = ImageIO.read(new File("res\\level1.png"));
            POINT = ImageIO.read(new File("res\\Points for pacman.png"));
            BLACK_BACK = ImageIO.read(new File("res\\blackBack.png"));
            POINT_BACK = ImageIO.read(new File("res\\points.png"));

        } catch (IOException e) {
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~ WARNING! ~~~~~~~~~~~~~~~~~~~~~~~~~~");
            System.out.println("There was am Image unsuccessfully loaded!"
                            + NL
                            + "The game may not work properly, check the load images method for spelling errors!");
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~ WARNING! ~~~~~~~~~~~~~~~~~~~~~~~~~~"
                            + NL + NL);

        }
    }
}
person ug_    schedule 23.10.2014
comment
Большое спасибо, перечисленные вами 5 проблем я обязательно запомню и не повторю в будущем. Просмотр исправленного вами кода на самом деле мне очень помогает, теперь я знаю, как правильно структурировать свои будущие проекты и остальную часть этого. Если бы я мог проголосовать за ваш ответ 20 раз, я бы это сделал! Кроме того, рекомендации по именованию переменных довольно изящны и очень логичны. Еще раз спасибо за все, что вы сделали! - person roke; 25.10.2014
comment
@roke Я рад, что помог. Обязательно прочтите об Объекте Бога en.wikipedia.org/wiki/God_object . Начинающему ООП-программисту очень легко подсунуть этот шаблон проектирования. Всегда думайте о том, какой класс должен знать, какую информацию и почему! - person ug_; 25.10.2014