Java Applet游戏2D窗口滚动
我正在尝试在Java Applet中开发2D RPG游戏。 现在我有一个简单的椭圆形,玩家可以使用Left,Right,Up和Down来移动,并且与applet边界的碰撞停止。 问题是,我想创造一个玩家可以移动的区域的巨大世界(2000x2000x)。 不过,我只希望他们一次只能看到600x400x的屏幕。 如果他们继续向右移动,我希望屏幕跟随他们,同样的上下左右。 谁能告诉我如何做到这一点? 这是我的代码到目前为止:
import java.awt.*; import java.awt.event.KeyEvent; import java.applet.Applet; import java.awt.event.KeyListener; import javax.swing.*; public class Main extends Applet implements Runnable, KeyListener { private Image dbImage; private Graphics dbg; Thread t1; int x = 0; int y = 0; int prevX = x; int prevY = y; int radius = 40; boolean keyReleased = false; public void init() { setSize(600, 400); } public void start() { addKeyListener(this); t1 = new Thread(this); t1.start(); } public void destroy() { } public void stop() { } public void paint(Graphics g) { //player g.setColor(Color.RED); g.fillOval(x, y, radius, radius); } public void update(Graphics g) { dbImage = createImage (this.getSize().width, this.getSize().height); dbg = dbImage.getGraphics(); // initialize buffer if (dbImage == null) { } // clear screen in background dbg.setColor(getBackground()); dbg.fillRect(0, 0, this.getSize().width, this.getSize().height); // draw elements in background dbg.setColor(getForeground()); paint(dbg); // draw image on the screen g.drawImage(dbImage, 0, 0, this); } @Override public void run() { while (true) { //x++; repaint(); try { t1.sleep(17); } catch (Exception e) { } } } public boolean CheckCollision(String dir) { if (x <= 0 && dir.equals("L")) { x = prevX; return true; } else if (y <= 0 && dir.equals("U")) { y = prevY; return true; } else if (x >= (getWidth() - radius) && dir.equals("R")) { System.out.println(getWidth()); x = prevX; return true; } else if (y >= (getHeight() - radius) && dir.equals("D")) { y = prevY; return true; } return false; } @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_RIGHT: if (!CheckCollision("R")) { x += 4; prevX = x; } break; case KeyEvent.VK_LEFT: if (!CheckCollision("L")) { x -= 4; prevX = x; } break; case KeyEvent.VK_UP: if (!CheckCollision("U")) { y -= 4; prevY = y; } break; case KeyEvent.VK_DOWN: if (!CheckCollision("D")) { y += 4; prevY = y; } break; } } @Override public void keyReleased(KeyEvent arg0) { // TODO Auto-generated method stub } @Override public void keyTyped(KeyEvent arg0) { // TODO Auto-generated method stub } }
这是滚动可视区域的一个基本示例,其中虚拟世界很大,然后是视图区域。
这基本上保持了一些参数。 它保持了世界上顶部/左边的观点和球员在世界上的位置。
这些值被转换回现实世界坐标(其中0x0是可视区域的左上angular)。
这个例子还使用了BufferedImage#getSubImage
来使绘制更容易。 你也可以计算地图到视图的偏移位置,但这需要…
import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class MiddleEarth { public static void main(String[] args) { new MiddleEarth(); } public MiddleEarth() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new WorldPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class WorldPane extends JPanel { private BufferedImage map; private BufferedImage party; private Point viewPort; private Point partyPoint; private BufferedImage view; public WorldPane() { try { map = ImageIO.read(getClass().getResource("/MiddleEarth.jpg")); party = ImageIO.read(getClass().getResource("/8BitFrodo.png")); viewPort = new Point(0, (map.getHeight() / 2) - 100); partyPoint = new Point(party.getWidth() / 2, (map.getHeight() / 2)); // Virtual Point... } catch (IOException exp) { exp.printStackTrace(); } InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); ActionMap am = getActionMap(); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "goRight"); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "goLeft"); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "goUp"); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "goDown"); am.put("goRight", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { moveParty(10, 0); } }); am.put("goLeft", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { moveParty(-10, 0); } }); am.put("goUp", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { moveParty(0, -10); } }); am.put("goDown", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { moveParty(0, 10); } }); } protected void moveParty(int xDelta, int yDelta) { partyPoint.x += xDelta; partyPoint.y += yDelta; Point view = fromWorld(partyPoint); if (view.x > getWidth() - (party.getWidth() / 2)) { viewPort.x += xDelta; if (viewPort.x + getWidth() > map.getWidth()) { viewPort.x = map.getWidth() - getWidth(); partyPoint.x = map.getWidth() - (party.getWidth() / 2) - 1; } invalidate(); } else if (view.x < party.getWidth() / 2) { viewPort.x += xDelta; if (viewPort.x < 0) { viewPort.x = 0; partyPoint.x = (party.getWidth() / 2); } invalidate(); } System.out.println(view + "; " + getHeight()); if (view.y > getHeight() - (party.getHeight() / 2)) { viewPort.y += yDelta; if (viewPort.y + getHeight() > map.getHeight()) { viewPort.y = map.getHeight() - getHeight(); partyPoint.y = map.getHeight() - (party.getHeight() / 2) - 1; } invalidate(); } else if (view.y < party.getHeight() / 2) { viewPort.y += yDelta; if (viewPort.y < 0) { viewPort.y = 0; partyPoint.y = (party.getHeight() / 2); } invalidate(); } repaint(); } @Override public void invalidate() { view = null; super.invalidate(); } public BufferedImage getView() { if (view == null && getWidth() > 0 && getHeight() > 0) { view = map.getSubimage(viewPort.x, viewPort.y, getWidth(), getHeight()); } return view; } @Override public Dimension getPreferredSize() { return new Dimension(400, 400); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); if (map != null) { g2d.drawImage(getView(), 0, 0, this); Point real = fromWorld(partyPoint); int x = real.x - (party.getWidth() / 2); int y = real.y - (party.getHeight()/ 2); g2d.drawImage(party, x, y, this); } g2d.dispose(); } protected Point fromWorld(Point wp) { Point p = new Point(); px = wp.x - viewPort.x; py = wp.y - viewPort.y; return p; } } }
这是我在我的引擎中做的。
我会保留两个variablesOffSetX
和OffSetY
并计算他们的每一步,以这样的球员中心。
OffSetX = 0; OffSetY = 0; if (MAP_WIDTH > WINDOW_WIDTH) { OffSetX = Math.round(WINDOW_WIDTH / 2 - obj.getX() - TILE_SIZE); OffSetX = Math.min(OffSetX, 0); OffSetX = Math.max(OffSetX, WINDOW_WIDTH - MAP_WIDTH); } if (MAP_HEIGHT > WINDOW_HEIGHT) { OffSetY = Math.round(WINDOW_HEIGHT / 2 - obj.getY() - TILE_SIZE); OffSetY = Math.min(OffSetY, 0); OffSetY = Math.max(OffSetY, WINDOW_HEIGHT - MAP_HEIGHT); }
然后在位置(OffSetX, OffSetY)
绘制地图(OffSetX, OffSetY)
这些添加到要绘制的对象的原始位置。
您可能想要跳过不可见的渲染对象。