4

我正在尝试为我正在制作的 2D 游戏实现一个摄像头......目标是让摄像头将玩家保持在中心,并使精灵相对于摄像头。

为了掌握normalocity 的帖子,我尝试通过制作一个 Camera Test 项目开始简单,在该项目中,我通过将精灵绘制到 JPanel 并移动“相机”对象(即 JPanel)来模拟相机,然后设置精灵的 x,y 相对于此。

正如我所说,相机是 JPanel ......我添加了一个“世界”,它是一个带有x,y of 0,0, 和的类w=1000, h=1000。我已经包含了精灵相对于世界的位置以及相机。当我向上移动相机时,精灵向下移动,玩家按预期停留在中间..

在此处输入图像描述

但如果我继续按下,精灵似乎会不断地绘制自己。

在此处输入图像描述

我的问题是:

  • 给出下面的代码,我在实现相机方面是否走在正确的轨道上?
  • 为什么精灵开始在那里绘制自己?它应该从 viewPort/JPanel 上消失

谢谢!

现在PaintComponent(g)添加后,我的 JPanel bg 灰色现在会滑落。这应该发生吗?

在此处输入图像描述


编辑:我的程序的 SSCCE:

主类:

import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;

@SuppressWarnings("serial")
public class MainSSCCE extends JFrame {
static MainSSCCE runMe;

public MainSSCCE() {
    JFrame f = new JFrame("Camera Test");
    CameraSSCCE cam = new CameraSSCCE(0, 0, 500, 500);
    f.add(cam);
    f.setSize(cam.getWidth(), cam.getHeight());    
    f.setVisible(true);
    f.setResizable(false);
    f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); 
    Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
    f.setLocation( (screensize.width - f.getWidth())/2,
         (screensize.height - f.getHeight())/2-100 );
}

public static void main(String[] args) {
    runMe = new MainSSCCE();
}
}

相机类:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;

//Camera is the JPanel that will draw all objects... each object location will be in relation to the World
public class CameraSSCCE extends JPanel implements KeyListener {
    //add world to camera...
    private static final long serialVersionUID = 1L;
    private int camX, camY, camH, camW;
    private SpriteSSCCE sprite;
    private PlayerSSCCE player;
    private WorldSSCCE world;

    public CameraSSCCE(int x, int y, int w, int h) {
        camX = x;
        camY = y;
        camW = w;       
        camH = h;   
        sprite = new SpriteSSCCE(this, 300, 300, 20, 20);
        player = new PlayerSSCCE(this, camW/2, camH/2, 25, 40);
        world = new WorldSSCCE(this, 0, 0, 1000, 1000);

        addKeyListener(this);
        setFocusable(true);
    }

    public int getWidth() {
        return camW;
    }

    public int getHeight() {
        return camH;
    }    

    @Override   
    protected void paintComponent(Graphics g) { 
        super.paintComponent(g);

        //cam is 500 x 500
        g.setColor(Color.gray);
        g.fillRect(camX, camY, camW, camH);     

        //draw sprite at JPanel location if in camera sight
        if (((sprite.getX()-camX) >= camX) && ((sprite.getX()-camX) <= (camX+camW)) && ((sprite.getY()-camY) >= camY) && ((sprite.getY()-camY) <= (camY+camH))) {
            g.setColor(Color.green);
            g.fillRect(sprite.getX()-camX, sprite.getY()-camY, 20, 20); 

            //Cam Sprite Location
            g.setColor(Color.white);
            g.drawString("Camera Sprite Location: (" + (sprite.getX()-camX) + ", " + (sprite.getY()-camY) + ")", sprite.getX()-camX, sprite.getY()-camY);                   
        }

        //Player location (center of Camera... Camera follows player)
        g.setColor(Color.cyan);
        g.fillRect(player.getX()-player.getWidth(), player.getY()-player.getWidth(), player.getWidth(), player.getHeight());

        g.setColor(Color.white);
        //World Sprite Location
        g.drawString("World Sprite Location: (" + sprite.getX() + ", " + sprite.getY() + ")", sprite.getX(), sprite.getY());

        //Cam Player Location
        g.drawString("Cam Player Location: (" + (camW/2-player.getWidth()) + ", " + (camH/2-player.getHeight()) + ")", camW/2-player.getWidth(), camH/2-player.getHeight());
    }

    public void keyPressed(KeyEvent e) {
        //move camera right in relation to World
        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            camX+=5;
        }
        //move camera left in relation to World
        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            camX-=5;
        }
        //move camera up in relation to World
        if (e.getKeyCode() == KeyEvent.VK_UP) {
            camY-=5;
        }
        //move camera down in relation to World
        if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            camY+=5;
        }
        repaint();
    }   

    public void keyReleased(KeyEvent e) {}
    public void keyTyped(KeyEvent e) {}

}

世界一流:

public class WorldSSCCE {
    private int x, y, w, h;
    private CameraSSCCE camera;

    public WorldSSCCE(CameraSSCCE cam, int x, int y, int w, int h) {
        camera = cam;               
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }

    public int getX() {
        return this.x;
    }

    public int getY() {
        return this.y;  
    }

    public int getWidth() {
        return this.w;
    }

    public int getHeight() {
        return this.h;
    }
}

玩家等级:

import java.awt.Dimension;

public class PlayerSSCCE {
    private int x, y, w, h;
    private CameraSSCCE cam;

    public PlayerSSCCE(CameraSSCCE cm, int x, int y, int w, int h) {
        cam = cm;               
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }

    public int getX() {
        return this.x;
    }

    public int getY() {
        return this.y;  
    }

    public int getWidth() {
        return this.w;
    }

    public int getHeight() {
        return this.h;
    }

    public void setX(int val) {
        this.x += val;
    }

    public void setY(int val) {
        this.y += val;
    }   
}

精灵类:

import java.awt.Color;
import java.awt.Graphics;

public class SpriteSSCCE {
    private int xLoc, yLoc, width, height;
    private CameraSSCCE world;

    public SpriteSSCCE(CameraSSCCE wld, int x, int y, int w, int h) {
        xLoc = x;
        yLoc = y;
        width = w;
        height = h;
        world = wld;    
    }

    public int getX() {
        return xLoc;
    }

    public int getY() {
        return yLoc;    
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }


    public void paintComponent(Graphics g) {
        g.setColor(Color.green);
        g.fillRect(xLoc, yLoc, width, height);      
    }


}
4

2 回答 2

3

1) 你没有通过调用来兑现油漆链super.paintComponent(g)paintComponent(..)

@Override
protected void paintComponent(Graphics g) {    
    super.paintComponent(g);

    //do drawing here
}

根据Java 文档

protected void paintComponent(Graphics g)

此外,如果你不调用 super 的实现,你必须遵守 opaque 属性,也就是说,如果这个组件是不透明的,你必须用不透明的颜色完全填充背景。如果您不尊重 opaque 属性,您可能会看到视觉伪影。

2) 还要注意我添加的@Override 注释public,以及我将修饰符更改protected为这就是实现类中定义的访问级别,除非出于特定原因,否则我们应该保留该级别。

3) Swing 还使用Keybindings 阅读如何使用键绑定

4) 还阅读了 Swing 中的并发性,特别是在Event Dispatch Thread上,它规定所有 Swing 组件都通过SwingUtillities.invokeXXX(..)块在 EDT 上创建:

SwingUtilities.invokeLater(new Runnable() {
   @Override
    public void run() {
         //create and manipulate swing components here
    }
});

5)您扩展JFrame类并创建一个实例,这不是您想要的,而是extends JFrame从类声明中删除:

public class MainSSCCE extends JFrame { //<-- Remove extends JFrame

    public MainSSCCE() {
       JFrame f = new JFrame("Camera Test");//<-- instance is created here
    }
}
于 2013-07-30T18:27:26.133 回答
2

您的世界是一个比屏幕(或重要的 jpanel)更大的虚拟区域。所有对象的位置都是相对于世界的。我们称它们为绝对坐标。

您的相机是世界的一小部分(您的面板)。通过移动它,您可以看到不同的世界部分。如果您可以像链接到的帖子中那样移动相机,那么在某些时候您将既看不到玩家也看不到其他精灵。

既然你的目标是让玩家在屏幕上居中,这对我们的世界意味着什么?这意味着玩家和相机相对于世界一起移动。

鉴于上述情况,像在第一个屏幕截图中那样绘制相机精灵是没有意义的。相机精灵应该是不可见的,或者它应该与玩家精灵绘制在相同的位置。在不改变玩家的情况下改变相机的绝对坐标也没有意义。这两个人正在一起移动。keyPressed()(在你的方法中考虑到这一点)

现在,当您绘图时,您是从相机的角度(或换句话说,在相机的坐标系中)进行绘图。从这个角度来看,相机总是看到一个 的矩形(0, 0, cameraWidth, cameraHeight)。这就是清除灰色区域时应该使用的方法。这将解决您的移动背景问题。因为摄像机和玩家总是有相同的绝对坐标,所以玩家总是在同一个地方(这就是我们想要的)。其余的精灵将相对于相机看到。

对于它们中的每一个,当您执行 (sprite.x - cam.x) 和 (sprite.y - cam.y) 时,您可以在相机的坐标系中平移它们。由于它们已被翻译,您只需要检查它们是否在相机的矩形内(0, 0, cameraWidth, cameraHeight)。如果他们是你继续画他们。如果不忽略它们。

我希望这会有所帮助

注意:cameraWidth, cameraHeight是您的 jpanel 的尺寸

于 2013-07-30T19:03:34.070 回答