JPanel не отображается в другом JPanel - PullRequest
0 голосов
/ 01 мая 2018

Я пытаюсь создать 2D гоночную игру, добавив Jpanel поверх Jpanel. Это делается с помощью 2 классов, которые я разместил ниже.

Проблема в том, что автомобиль никогда не появляется на трассе ... Я действительно не уверен, что мне не хватает ... любая помощь более чем приветствуется!

Заранее спасибо!

Car.java

public class Car extends JPanel implements Runnable 
{

   private static final long serialVersionUID = 007;
   private BufferedImage car = null;
   private float x = 100F, y = 100F;
   private Thread driveThread = new Thread(this);
   private double currentAngle = 0; // angel of the car
   private static int[] key = new int[256]; // keyboard input
   private float MAX_SPEED = 7F;
   private float speed = 0F; // speed of our racing car
   private float acceleration = 0.15F;
   private int player;
   private boolean playable = true;


   public Car(int player) 
   {

      this.player = player;

      this.setSize(super.getHeight(), super.getWidth());
      this.setFocusable(true); // enables keyboard

      try 
      {
         if (player == 1) 
         {
            //red car
            car = ImageIO.read(this.getClass().getResource(
                  "/imagesCar/first-0.png"));

            System.out.println(car.getColorModel());
         } else if(player == 2)
         {
            //blue car
            car = ImageIO.read(this.getClass().getResource(
                  "/imagesCar/second-0.png"));
            x = x +30;
         }

      } catch (IOException e) {
         System.out.println("dupi");
      }

      // starts the drive thread
      startGame();

   }

   private void startGame() {
      driveThread.start();
   }

   @Override
   protected void paintComponent(Graphics g) 
   {

      super.paintComponent(g);
      this.setOpaque(false);

      // rotation 
      Graphics2D g2d = (Graphics2D) g;
      AffineTransform rot = g2d.getTransform();
      // Rotation at the center of the car
      float xRot = x + 12.5F;
      float yRot = y + 20F;
      rot.rotate(Math.toRadians(currentAngle), xRot, yRot);
      g2d.setTransform(rot);
      //Draws the cars new position and angle
      g2d.drawImage(car, (int) x, (int) y, 50, 50, this);

   }

   protected void calculateCarPosition() {

      //calculates the new X and Y - coordinates 
      x += Math.sin(currentAngle * Math.PI / 180) * speed * 0.5;
      y += Math.cos(currentAngle * Math.PI / 180) * -speed * 0.5;

   }

   protected void carMovement() {

      // Player One Key's
      if (player == 1) {

         if (key[KeyEvent.VK_LEFT] != 0) {
            currentAngle-=2;

         } else if (key[KeyEvent.VK_RIGHT] != 0) {
            currentAngle+=2;
         }

         if (key[KeyEvent.VK_UP] != 0) {

            if (speed < MAX_SPEED) {

               speed += acceleration;
            }

         } else if (key[KeyEvent.VK_DOWN] != 0 && speed > -1) {
            speed = speed - 0.1F;
         }
         speed = speed * 0.99F;

      } else {

         //Player Two Key's

         if (key[KeyEvent.VK_A] != 0) {
            currentAngle -= 2;

         } else if (key[KeyEvent.VK_D] != 0) {
            currentAngle += 2;
         }

         if (key[KeyEvent.VK_W] != 0) {

            if (speed < MAX_SPEED) {

               speed += acceleration;
            }

         } else if (key[KeyEvent.VK_S] != 0 && speed > -1) {
            speed = speed - 0.1F;
         }
         //reduce speed when no key is pressed
         speed = speed * 0.99F;
      }

   }

   public void getUnderground() {

   }
   // get key events!
   final protected void processKeyEvent(KeyEvent e) {
      key[e.getKeyCode()] = e.getID() & 1;
   }

   @Override
   public void run() {
      while (true) {

         repaint();
         carMovement();
         calculateCarPosition();


         try {
            Thread.sleep(10);
         } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
         }

      }

   }

}

RaceTrack.java

public class RaceTrack extends JPanel
{

@Override
public void paintComponent(Graphics g)
{
    Color c1 = Color.green;

    g.setColor(c1);
    g.fillRect(150, 200, 550, 300);
    Color c2 = Color.black;
    g.setColor(c2);
    g.drawRect(50, 100, 750, 500); // outer edge
    g.drawRect(150, 200, 550, 300); // inner edge
    Color c3 = Color.yellow;
    g.setColor(c3);
    g.drawRect(100, 150, 650, 400); // mid-lane marker
    Color c4 = Color.white;
    g.setColor(c4);
    g.drawLine(425, 500, 425, 600); // start line

}
}

основной

public static void main(String[] args) {

    JFrame mainFrame = new JFrame();

    mainFrame.setSize(850,650);
    mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    Container content = mainFrame.getContentPane();

    RaceTrack track = new RaceTrack();

    Car carP1 = new Car(1);

    track.add(carP1);

    content.add(track);

    mainFrame.setVisible(true);
}

1 Ответ

0 голосов
/ 01 мая 2018
  1. Автомобиль не имеет определенных подсказок по размеру, поэтому его размер по умолчанию равен 0x0
  2. При добавлении Car к RaceTrack, использующему FlowLayout, макет Car будет соответствовать предпочтительному размеру 0x0
  3. Swing не является потокобезопасным, поэтому, вероятно, у вас также есть куча условий / нарушений в потоке потоков

Не уверен, что это правильный способ решить эту проблему

Не используйте компоненты для этой цели, эта проблема просто кричит о рисовании до конца.

Существует множество блогов и учебных пособий по базовой разработке игр, поэтому я не хочу тратить много времени на изучение одного и того же материала.

По сути, вам нужно определить серию «атрибутов» для объектов, которые вы хотите использовать в своей игре («сущности» АКА). Не все объекты должны быть окрашиваемыми, некоторые могут запускать другие действия или просто действовать как «маркеры» для использования другими объектами.

В этом примере я определяю две основные сущности, «подвижные» и «окрашиваемые». «Окрашиваемый» объект может быть статичным (т.е. гусеница) или «подвижным» (т. Е. Автомобиль)

Цель состоит в том, чтобы предоставить изолированную концепцию функциональности, которую можно легко применить к объектам Verity, чтобы «описать» их функциональность и назначение в игре.

Например ...

public interface MovableEntity extends Entity {
    public void update(Rectangle bounds);
}

public interface PaintableEntity extends Entity {
    public void paint(Graphics2D g2d, ImageObserver imageObserver, Rectangle bounds);
}

Итак, в вашем случае Car - это и Paintable, и Movable.

Ваш «движок» будет вести один или несколько списков этих «сущностей» и обрабатывать их соответствующим образом.

В этом примере просто используется Swing Timer в качестве "основного цикла"

mainLoop = new Timer(5, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
        // Lots of collision detection and other awesome stuff
        for (MovableEntity entity : movableEntitys) {
            entity.update(bounds);
        }
        repaint();
    }
});
mainLoop.start();

Это обеспечивает уровень безопасности потока, так как Timer запускается в контексте потока диспетчеризации событий, что означает, что пока мы обновляем сущности, они не могут быть нарисованы.

Затем мы просто используем JPanel метод paintComponent, чтобы действовать как процесс рендеринга ...

protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
    for (PaintableEntity paintable : paintableEntities) {
        Graphics2D g2d = (Graphics2D) g.create();
        paintable.paint(g2d, this, bounds);
        g2d.dispose();
    }
}

Это довольно широкий пример, основанный на простой демонстрации основных понятий. Существуют гораздо более сложные возможные решения, которые будут следовать тем же основным принципам.

Лично я бы определил некий «путь», который будет действовать как трек, по которому автомобили будут затем рассчитывать свои позиции на основе различных факторов, но это несколько более сложное решение, чем требуется сейчас. Но если вам действительно интересно, это может выглядеть как-то вот так

Пример запуска ...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.ImageObserver;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    /*
    You could have entities which can collide which have collision detection
    capabilities

    Some entities don't need to be painted and may provide things like
    visual or audio affects
    */

    public interface Entity {
    }

    public interface MovableEntity extends Entity {
        public void update(Rectangle bounds);
    }

    public interface PaintableEntity extends Entity {
        public void paint(Graphics2D g2d, ImageObserver imageObserver, Rectangle bounds);
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new GamePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GamePane extends JPanel {

        // Could use a single list and filter it, but hay
        private List<PaintableEntity> paintableEntities;
        private List<MovableEntity> movableEntitys;

        private Timer mainLoop;

        public GamePane() {
            paintableEntities = new ArrayList<>(25);
            movableEntitys = new ArrayList<>(25);

            paintableEntities.add(new TrackEntity());

            CarEntity car = new CarEntity();
            paintableEntities.add(car);
            movableEntitys.add(car);

            mainLoop = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
                    // Lots of collision detection and other awesome stuff
                    for (MovableEntity entity : movableEntitys) {
                        entity.update(bounds);
                    }
                    repaint();
                }
            });
            mainLoop.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
            for (PaintableEntity paintable : paintableEntities) {
                Graphics2D g2d = (Graphics2D) g.create();
                paintable.paint(g2d, this, bounds);
                g2d.dispose();
            }
        }

    }

    public class CarEntity implements PaintableEntity, MovableEntity {

        private int delta = 1;

        private int xDelta = 0;
        private int yDelta = delta;

        private int xPos = 2;
        private int yPos = 2;

        private int size = 4;

        @Override
        public void paint(Graphics2D g2d, ImageObserver imageObserver, Rectangle bounds) {
            g2d.translate(bounds.x, bounds.y);
            g2d.setColor(Color.RED);
            g2d.fillRect(xPos - size / 2, yPos - size / 2, size, size);
        }

        @Override
        public void update(Rectangle bounds) {
            xPos += xDelta;
            yPos += yDelta;

            if (xPos + (size / 2) > bounds.x + bounds.width) {
                xPos = bounds.x + bounds.width - (size / 2);
                xDelta = 0;
                yDelta = -delta;
            } else if (xPos - (size / 2) < bounds.x) {
                xPos = bounds.x + (size / 2);
                xDelta = 0;
                yDelta = delta;
            }

            if (yPos + (size / 2) > bounds.y + bounds.height) {
                yPos = bounds.y + bounds.height - (size / 2);
                xDelta = delta;
                yDelta = 0;
            } else if (yPos - (size / 2) < bounds.y) {
                yPos = bounds.y + (size / 2);
                xDelta = -delta;
                yDelta = 0;
            }
        }

    }

    public class TrackEntity implements PaintableEntity {

        @Override
        public void paint(Graphics2D g2d, ImageObserver imageObserver, Rectangle bounds) {
            g2d.translate(bounds.x, bounds.y);
            g2d.setColor(Color.BLUE);
            g2d.drawRect(2, 2, bounds.width - 4, bounds.height - 4);
        }

    }

}
...