[英]How do you use re-size all Graphic2D
在 java 中,如何让游戏完全可实现! 但是逻辑和图形可以使用它吗? 我尝试过使用 SCALE 方法。 但这并不能让每台计算机都能完美地全屏显示。 所以我做了这个:
public void resize(int WIDTH, int HEIGHT, boolean UNDECORATED) {
frame.setPreferredSize(new Dimension(WIDTH, HEIGHT));
frame.setMaximumSize(new Dimension(WIDTH, HEIGHT));
frame.setMinimumSize(new Dimension(WIDTH, HEIGHT));
this.WIDTH = WIDTH;
this.HEIGHT = HEIGHT;
frame.setUndecorated(UNDECORATED);
frame.setSize(WIDTH, HEIGHT);
}
因此,您可以将屏幕尺寸设置为您想要的任何尺寸! 它可以工作,但图形无法使用它? Graphics2D 中是否有办法拉伸所有图形以使其适合? 例如,如果存在这样的方法:
G2D.resize(WIDTH, HEIGHT, Image.NEAREST_PARENT_RESCALE);
任何想法?
我尝试过的事情:
我不介意的事情
那么如何使用 Graphics2D、JFrame 制作完美的可重新封装游戏。
在最通用的形式中,可以将其视为图形编程的经典问题,即从世界坐标到屏幕坐标的转换。 您的世界坐标系中有一个大小为“1.0 x 1.0”的对象(无论它具有哪个单位)。 并且这个对象应该被绘制,使其在屏幕上具有例如“600 像素 * 600 像素”的大小。
从广义上讲,在 Swing 中至少有三个选项可以实现这一点:
Graphics2D
对象每一个都有可能的优点和缺点,以及隐藏的警告。
这可能看起来像一个简单的解决方案,但有一个潜在的缺点:图像本身具有一定的分辨率(大小)。 如果图像太小,并且您将其放大以填满屏幕,它可能会出现块状。 如果图像太大,并且您将其缩小以适应屏幕,则图像的像素可能会丢失。
在这两种情况下,缩放图像的过程都有几个调整参数。 事实上,缩放图像比乍一看要复杂得多。 详情可参考 Chris Campbell 的文章The Perils of Image.getScaledInstance() 。
Graphics2D
对象 Graphics2D
类已经提供了创建世界坐标系和屏幕坐标系之间转换所需的全部功能。 这是由Graphics2D
类通过在内部存储描述此转换的AffineTransform
来完成的。 这个AffineTransform
可以通过Graphics2D
对象直接修改:
void paintSomething(Graphics2D g) {
...
g.draw(someShape);
// Everything that is painted after this line will
// be painted 3 times as large:
g.scale(3.0, 3.0);
g.draw(someShape); // Will be drawn larger
}
必须注意正确管理存储在Graphics2D
对象中的变换。 一般来说,应该在应用其他变换之前创建原始AffineTransform
的备份,然后恢复这个原始变换:
// Create a backup of the original transform
AffineTransform oldAT = g.getTransform();
// Apply some transformations
g.scale(3.0, 4.0);
g.translate(10.0, 20.0);
// Do custom painting the the transformed graphics
paintSomething(g):
// Restore the original transformation
g.setTransform(oldAT);
(对最后一种方法的另一个建议: Graphics2D#setTransform
方法永远不应用于在现有变换之上应用新的坐标变换。它仅用于恢复“旧”变换,如本示例所示(以及此方法的文档))。
使用Graphics2D
类进行缩放的一个潜在缺点是之后,所有内容都将被缩放。 特别是,这种缩放也会影响线宽(即Stroke的宽度)。 例如,考虑这样的一系列调用:
// By default, this will paint a line with a width (stroke) of 1.0:
g.draw(someLine);
// Apply some scaling...
g.scale(10.0, 10.0);
// Now, this will paint the same line, but with a width of 10.
g.draw(someLine);
第二次调用将导致绘制一条 10 像素宽的线。 在许多情况下,这可能不是所希望的。 第三种选择可以避免这种影响:
世界坐标系和屏幕坐标系之间的转换也可以手动维护。 将其表示为AffineTransform
很方便。 AffineTransform
类可用于创建Shape
对象的转换版本,然后可以将其直接绘制到(未转换的) Graphics2D
对象中。 这是通过AffineTransform#createTransformedShape
方法完成的:
void paintSomething(Graphics2D g) {
...
// Draw some shape in its normal size
g.draw(someShape);
// Create a scaling transform
AffineTransform at = AffineTransform.getScaleInstance(3.0, 3.0);
// Create a scaled version of the shape
Shape transformedShape = at.createTransformedShape(someShape);
// Draw the scaled shape
g.draw(transformedShape);
}
这可能是最通用的方法。 唯一的潜在缺点是,当绘制许多小而简单的形状时,这将导致创建许多小的临时变换形状,这可能会导致性能下降。 (有一些方法可以缓解这个问题,但是详细的性能考虑和优化超出了这个答案的范围)。
下图显示了所有方法的比较。 绘制了一些示例对象(表示为Shape
对象)。 每行比较上述三种不同的缩放方法。 使用它们的“默认”大小,对象填充世界坐标中大小为 100x100 的矩形。 在前两行中,它们被放大以填充屏幕上 190x190 像素的区域。 在最后两行中,它们被缩小以填充屏幕上 60x60 像素的区域。 (选择这些尺寸是为了获得一些 1.9 和 0.6 的“奇数”缩放因子。例如,当缩放因子为整数或恰好为 0.5 时,某些效果(伪影)可能不会出现)。
对于放大和缩小,还有“标准”绘画方式和“高质量”绘画(每个面板标题中的“(HQ)”表示)之间的比较。 这里的“高质量”仅仅意味着渲染提示
KEY_ANTIALIAS = VALUE_ANTIALIAS_ON
KEY_RENDERING = VALUE_RENDER_QUALITY
已设置:
这是相应的程序,作为MCVE :
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ScalingMethodComparison
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new GridLayout(0,1));
Dimension larger = new Dimension(190,190);
Dimension smaller = new Dimension(60,60);
f.getContentPane().add(createPanel(larger, false));
f.getContentPane().add(createPanel(larger, true));
f.getContentPane().add(createPanel(smaller, false));
f.getContentPane().add(createPanel(smaller, true));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static JPanel createPanel(Dimension d, boolean highQuality)
{
JPanel p = new JPanel(new GridLayout(1,3));
for (ScalingMethodComparisonPanel.ScalingMethod scalingMethod :
ScalingMethodComparisonPanel.ScalingMethod.values())
{
p.add(createPanel(d, scalingMethod, highQuality));
}
return p;
}
private static JPanel createPanel(
Dimension d, ScalingMethodComparisonPanel.ScalingMethod scalingMethod,
boolean highQuality)
{
JPanel p = new JPanel(new GridLayout(1,1));
p.setBorder(BorderFactory.createTitledBorder(
scalingMethod.toString()+(highQuality?" (HQ)":"")));
JPanel scalingMethodComparisonPanel =
new ScalingMethodComparisonPanel(
createObjects(), d, scalingMethod, highQuality);
p.add(scalingMethodComparisonPanel);
return p;
}
// Returns a list of objects that should be drawn,
// occupying a rectangle of 100x100 in WORLD COORDINATES
private static List<Shape> createObjects()
{
List<Shape> objects = new ArrayList<Shape>();
objects.add(new Ellipse2D.Double(10,10,80,80));
objects.add(new Rectangle2D.Double(20,20,60,60));
objects.add(new Line2D.Double(30,30,70,70));
return objects;
}
}
class ScalingMethodComparisonPanel extends JPanel
{
private static final Color COLORS[] = {
Color.RED, Color.GREEN, Color.BLUE,
};
enum ScalingMethod
{
SCALING_IMAGE,
SCALING_GRAPHICS,
SCALING_SHAPES,
}
private final List<Shape> objects;
private final ScalingMethod scalingMethod;
private final boolean highQuality;
private final Dimension originalSize = new Dimension(100,100);
private final Dimension scaledSize;
private BufferedImage image;
public ScalingMethodComparisonPanel(
List<Shape> objects,
Dimension scaledSize,
ScalingMethod scalingMethod,
boolean highQuality)
{
this.objects = objects;
this.scaledSize = new Dimension(scaledSize);
this.scalingMethod = scalingMethod;
this.highQuality = highQuality;
}
@Override
public Dimension getPreferredSize()
{
return new Dimension(scaledSize);
}
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.WHITE);
g.fillRect(0,0,getWidth(), getHeight());
if (highQuality)
{
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(
RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
}
if (scalingMethod == ScalingMethod.SCALING_IMAGE)
{
paintByScalingImage(g);
}
else if (scalingMethod == ScalingMethod.SCALING_GRAPHICS)
{
paintByScalingGraphics(g);
}
else if (scalingMethod == ScalingMethod.SCALING_SHAPES)
{
paintByScalingShapes(g);
}
}
private void paintByScalingImage(Graphics2D g)
{
if (image == null)
{
image = new BufferedImage(
originalSize.width, originalSize.height,
BufferedImage.TYPE_INT_ARGB);
}
Graphics2D ig = image.createGraphics();
paintObjects(ig, null);
ig.dispose();
g.drawImage(image, 0, 0, scaledSize.width, scaledSize.height, null);
}
private void paintByScalingGraphics(Graphics2D g)
{
AffineTransform oldAT = g.getTransform();
double scaleX = (double)scaledSize.width / originalSize.width;
double scaleY = (double)scaledSize.height / originalSize.height;
g.scale(scaleX, scaleY);
paintObjects(g, null);
g.setTransform(oldAT);
}
private void paintByScalingShapes(Graphics2D g)
{
double scaleX = (double)scaledSize.width / originalSize.width;
double scaleY = (double)scaledSize.height / originalSize.height;
AffineTransform at =
AffineTransform.getScaleInstance(scaleX, scaleY);
paintObjects(g, at);
}
private void paintObjects(Graphics2D g, AffineTransform at)
{
for (int i=0; i<objects.size(); i++)
{
Shape shape = objects.get(i);
g.setColor(COLORS[i%COLORS.length]);
if (at == null)
{
g.draw(shape);
}
else
{
g.draw(at.createTransformedShape(shape));
}
}
}
}
这在 Java 中实际上很容易。 在Graphics2d
环境中,屏幕上的逻辑坐标系(您在绘图例程中使用的坐标)和物理坐标系(它们出现的坐标)是完全不相关的。 每次在Graphics2d
对象上绘制时,逻辑坐标首先由AffineTransform
对象转换为物理坐标,并且可以修改此AffineTransform
对象。 为此,您可以使用Graphics2D.scale(double,double)
、 Graphics2D.rotate(double)
、 Graphics2D.translate(double,double)
和Graphics2D.shear(double,double)
方法。
所以如果你第一次打电话
g2d.scale(2.0,2.0);
那么您随后绘制的所有图形在两个方向上都将是原来的两倍。
如果我理解正确,您想要的只是以不同的分辨率绘制图形,而无需删除或添加任何内容。
好吧,“您尝试过的事情”之一可以做到这一点。
绘制到固定大小的BufferedImage
将确保您的所有组件在该BufferedImage
中可见(假设您正确绘制它们并且相对于它的固定大小)然后您可以将图像绘制到灵活大小的屏幕上。
这是一个完整的可运行代码示例:
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Test extends Canvas implements Runnable {
// fixed size for the image
private static final int WIDTH = 640;
private static final int HEIGHT = 480;
private BufferedImage image;
private boolean running;
private Thread t;
public Test(Dimension dims) {
super();
setPreferredSize(dims); // actual screen size
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
running = false;
}
public synchronized void start() {
if (running)
return;
t = new Thread(this);
running = true;
t.start();
}
public synchronized void stop() {
if (!running)
return;
running = false;
boolean retry = true;
while (retry) {
try {
t.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void render() {
// draw to your image
Graphics2D g2d = (Graphics2D) image.getGraphics().create();
g2d.fillRect((WIDTH / 2) - 25, (HEIGHT / 2) - 25, 50, 50);
g2d.dispose();
// draw the image to your screen
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
g2d = (Graphics2D) bs.getDrawGraphics().create();
g2d.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g2d.dispose();
bs.show();
}
public void run() {
// approximately sync rendering to 60 FPS don't use it as it is.
// there are much better ways to do this.
long startTime = System.currentTimeMillis();
long frameTime = 1000 / 60;
long tick = 0;
while (running) {
while ((System.currentTimeMillis() - startTime) > tick) {
render();
tick += frameTime;
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Test test = new Test(new Dimension(800, 600));
JFrame frame = new JFrame("Fit to screen");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
test.stop();
frame.dispose();
super.windowClosing(e);
}
});
frame.getContentPane().add(test);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
test.start();
}
});
}
}
这只是一个快速的实现,有一些事情可以在该代码中做得更好,但你得到了图片。 希望这可以帮助。
也许这会有所帮助:
缩放包含基本形状的 graphics2D 有一个缺点:如果比例加倍,线条的粗细会加倍,这是实现缩放功能的应用程序中的问题......我发现的唯一方法是使容器的首选尺寸更大并且然后,绘制形状。
这是使用鼠标滚轮的缩放功能,鼠标指向的对象的像素停留在鼠标指针下方。 我花了很长时间才弄清楚如何正确地做到这一点,但我终于发现......(应用程序是一个星盘,我想放大和缩小)
graphics2D 属于一个 JPanel,它包含在 JSplitPane 的底部:
public void mouseWheelMoved(MouseWheelEvent e) {
Dimension dim = new Dimension(), oldDim = this.getPreferredSize();
double newX, newY;
Rectangle rect, oldRect;
if(this.mousewheel >= 0){
this.mousewheel += -e.getWheelRotation() * this.mousewheelSensibility;
}
else {
this.mousewheel = 0;
}
dim.setSize(this.astro.splitBottomDimension.getWidth() + this.mousewheel, this.astro.splitBottomDimension.getHeight() + this.mousewheel);
oldRect = this.getVisibleRect();
this.mouseX = e.getX();
this.mouseY = e.getY();
this.setPreferredSize(dim);
newX = this.mouseX / oldDim.getWidth() * dim.getWidth();
newY = this.mouseY / oldDim.getHeight() * dim.getHeight();
rect = new Rectangle((int)newX - (this.mouseX - oldRect.x), (int)newY - (this.mouseY - oldRect.y), oldRect.width, oldRect.height);
this.scrollRectToVisible(rect);
this.revalidate();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.