我正在用Java创建一个复古的街机游戏。游戏的屏幕分辨率为304 x 256,我想保留它以保留游戏的复古特征(视觉效果,动画,字体阻挡等)。
但是当我在大型桌面显示器上渲染它时,它太小了,正如人们所期望的那样。
我希望能够通过一个常数因子来扩展窗口,而不必编写各种paint(Graphics)
方法来了解有关扩展的事实。也就是说,我希望渲染代码相信屏幕是304 x 256.我也不想更改桌面分辨率或进入全屏独占模式。只是想要一个缩小像素的大窗口。
我正在寻找以下内容:
scale(myJFrame, 4);
并让所有内容自动放大。
UPDATE:关于输入,我的游戏恰好使用键盘输入,所以我自己不需要trashgod描述的逆变换。我仍然可以想象其他人会需要它,所以我认为这是一个恰当的建议。
答案 0 :(得分:2)
建议here建议的一种方法是依靠drawImage()
来扩展内容的图像。您的游戏会在BufferedImage
的图形上下文中呈现自己,而不是paintComponent()
的实现。如果游戏包含鼠标交互,则必须缩放鼠标坐标,如图所示。在下面的变体形式中,我为CENTER
面板提供了SCALE = 8
倍数的首选尺寸,并将原始图标添加为WEST
的{{1}} }}。默认情况下,BorderLayout
会忽略组件的首选大小,您可能希望将其添加到CENTER
的(可能是嵌套的)面板中。调整框架大小以查看效果。
FlowLayout
f.setLayout(new FlowLayout());
f.add(new Grid(NAME));
//f.add(new JLabel(ICON), BorderLayout.WEST);
答案 1 :(得分:2)
建议here的一种方法是依赖图形上下文的scale()
方法,并构造为逆变换以在鼠标坐标和图像坐标之间进行转换。在下面的示例中,请注意原始图像的大小为256 x 256,而显示的图像按SCALE = 2.0
缩放。鼠标悬停在图像的中心上方;工具提示显示显示中的任意点和原始中心点(127,127)。
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** @see https://stackoverflow.com/a/2244285/230513 */
public class InverseTransform {
private static final double SCALE = 2.0;
public static void main(String[] args) {
JFrame frame = new JFrame("Inverse Test");
BufferedImage image = getImage(256, 'F');
AffineTransform at = new AffineTransform();
at.scale(SCALE, SCALE);
frame.add(new ImageView(image, at));
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static BufferedImage getImage(int size, char c) {
final Font font = new Font("Serif", Font.BOLD, size);
BufferedImage bi = new BufferedImage(
size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.white);
g2d.fillRect(0, 0, size, size);
g2d.setPaint(Color.blue);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
int x = (size - fm.charWidth(c)) / 2;
int y = fm.getAscent() + fm.getDescent() / 4;
g2d.drawString(String.valueOf(c), x, y);
g2d.setPaint(Color.black);
g2d.drawLine(0, y, size, y);
g2d.drawLine(x, 0, x, size);
g2d.fillOval(x - 3, y - 3, 6, 6);
g2d.drawRect(0, 0, size - 1, size - 1);
g2d.dispose();
return bi;
}
private static class ImageView extends JPanel {
private BufferedImage image;
private AffineTransform at;
private AffineTransform inverse;
private Graphics2D canvas;
private Point oldPt = new Point();
private Point newPt;
@Override
public Dimension getPreferredSize() {
return new Dimension( // arbitrary multiple of SCALE
(int)(image.getWidth() * SCALE * 1.25),
(int)(image.getHeight() * SCALE * 1.25));
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
try {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
inverse = g2d.getTransform();
inverse.invert();
g2d.translate(this.getWidth() / 2, this.getHeight() / 2);
g2d.transform(at);
g2d.translate(-image.getWidth() / 2, -image.getHeight() / 2);
inverse.concatenate(g2d.getTransform());
g2d.drawImage(image, 0, 0, this);
} catch (NoninvertibleTransformException ex) {
ex.printStackTrace(System.err);
}
}
ImageView(final BufferedImage image, final AffineTransform at) {
this.setBackground(Color.lightGray);
this.image = image;
this.at = at;
this.canvas = image.createGraphics();
this.canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
this.canvas.setColor(Color.BLACK);
this.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
Point m = e.getPoint();
Point i = e.getPoint();
try {
inverse.inverseTransform(m, i);
setToolTipText("<html>Mouse: " + m.x + "," + m.y
+ "<br>Inverse: " + i.x + "," + i.y + "</html>");
} catch (NoninvertibleTransformException ex) {
ex.printStackTrace();
}
}
});
}
}
}
答案 2 :(得分:2)
感谢trashgod用他的两个答案指出了正确的方向。我能够将两个答案的元素结合起来,得到适合我需要做的事情。
首先,我的目标是扩展整个UI而不是扩展单个图标或其他简单组件。通过“整个UI”,我特别指的是包含使用JPanel
布局的多个自定义子组件的BorderLayout
。没有JButtons
或任何其他交互式Swing组件,也没有鼠标输入(它都是基于键盘的输入),所以我只需要将一个304 x 256 JPanel
缩放3倍或者4。
这就是我的所作所为:
package bb.view;
import javax.swing.JComponent;
import javax.swing.JPanel;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import static bb.BBConfig.SCREEN_WIDTH_PX; // 304
import static bb.BBConfig.SCREEN_HEIGHT_PX; // 256
public class Resizer extends JPanel {
private static final int K = 3;
private static final Dimension PREF_SIZE =
new Dimension(K * SCREEN_WIDTH_PX, K * SCREEN_HEIGHT_PX);
private static final AffineTransform SCALE_XFORM =
AffineTransform.getScaleInstance(K, K);
public Resizer(JComponent component) {
setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
add(component);
}
@Override
public Dimension getPreferredSize() {
return PREF_SIZE;
}
@Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setTransform(SCALE_XFORM);
super.paint(g2);
}
}
解决方案的一些重要元素:
FlowLayout
收缩包装子组件,这就是我想要的。 (感谢trashgod。)也就是说,我不希望孩子扩展以填充Resizer
首选大小,因为这会破坏子组件的布局。特别是它在子组件的CENTER
和SOUTH
区域之间创造了巨大的差距。FlowLayout
配置为左对齐和hgap,vgap = 0.这样我的缩放变换也会将缩放版本固定在左上角。AffineTransform
来完成缩放。 (再次感谢trashgod。)paint()
代替paintComponent()
,因为Resizer
只是一个包装器。我不想画边框。我基本上想拦截paint()
调用,插入缩放变换,然后让JPanel.paint()
执行通常的操作。BufferedImage
。最终结果是UI很大,但除了Resizer
之外的所有代码都认为UI是304 x 256。