Hi,
i’m currently starting a new 2D game project and so far i’ve always had a fixed 1024x768 screen resolution and fixed locations of things on the screen, but this time i want to make it a bit more dynamic. The user should be able to change it to a resolution that he wants and everything should scale to the wanted size.
Is there a simple way to scale the 2D sprites to another size? Let’s say i have a few 1024x768 Jpegs that should now get scaled to a 1920x1080 resolution as soon as the level starts. What’s the best way to do that?
As i said, i’ve always had fixed starting locations like x=256, y=252. How can i adjust these values to the resolution? Should i just work with something like x=frameWidth*(1/4) y=frameHeight*(1/3) or is there some kind of factor that i could multiply my coordinates with? For example x=256widthResolutionFactor, y=252heightResolutionFactor.
If all you want to do is draw the exact same scene, just scaled to the users screen resolution, use an AffineTransform. The most direct way is just to call Graphics2D g.scale(double sx, double sy) with appropriate scaling parameters to scale your original scene dimensions to the screen dimensions. If you want to scale individual sprites you can get a Graphics2D object for a buffered image and use the same method to draw a scaled version of your original sprite to this new sprite.
The best way to do this is to draw each frame into a BufferedImage with the default width and height of a window.
Then you can just call this non-static method:
Graphics.drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer);
where dx1 and dy1 are your destination top left points, hence (0,0); dx2 and dy2 are your destination bottom right points, (1920,1080)
sx1 and sy1 are you source top left points, (0,0); sx2 and sy2 are your source bottom right points (1024,768)
And you can just ignore the observer parameter by supplying null if you do not need to use it.
The Graphics.drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer); function works perfectly :).
I had the same Problem.
I know how I scale the graphics to the screen dimension.
But what I didnt know was how I have to change the Coordinates like Player_X/Y or the Position from the NPCs?
Like the NPC “Bob” is on X=200,Y=220. I take this position on a 800x600 Dimension. But now if I change the screen resulution then the coordinates are wrong. Is there a formular to fix all Coordinates and Collisions on the Screen Resolution?
public static final int NORMAL_WIDTH = 1024;
public static final int NORMAL_HEIGHT = 768;
private float scale;
public void calculateScale()
{
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
scale = Math.min( (float)d.getWidth() / NORMAL_WIDTH, (float)d.getHeight() / NORMAL_HEIGHT );
}
public void draw(Graphics g)
{
((Graphics2D)g).scale(scale,scale);
drawAllMyCrap(g);
((Graphics2D)g).scale(1/scale,1/scale);
}
Then in drawAllMyCrap() just draw everything on a screen as if it were NORMAL_WIDTH x NORMAL_HEIGHT.
If you’re using mouse controls, your mouse coordinates will no longer match the coordinates of objects on the screen after you stretch the graphics. You’ll have to take that into account.
Ooh, there’s a way to do that? I do it in OpenGL with push and pop but I couldn’t find any equivalent functions in Java2D. Do you need to do with with getTranform() setTransform() or what?
Also @fletchergames, good point about the mouse transformation that must also be applied.
I depends on what method is used to scale, but yes it can look ugly. The faster the interpolation method is the worse it generally looks. One way you can get around this is to scale the images once when the game starts up. Performance is less of an issue then so you can use the best looking interpolation available.
Do you think it is better to set the Resolution on the Users Resolution.
AppGameContainer app = new AppGameContainer(new Spiel());
app.setDisplayMode(d.width,d.height, false);
Or should I put a fix Resolution like
AppGameContainer app = new AppGameContainer(new Spiel());
app.setDisplayMode(800,600, false);
[quote]Don’t concatenate inverse transforms or you’ll end up with rounding errors.
Store, and then restore the AffineTransform.
[/quote]
What does that mean? I used the Code from Eli Delventhal and it seem to work. ô.o
EDIT. Ah and if its important Im using Slick and LWJGL.
What does that mean? I used the Code from Eli Delventhal and it seem to work. ô.o
EDIT. Ah and if its important Im using Slick and LWJGL.
[/quote]
He’s saying don’t do the opposite transform to scale back (1.0f/scale) because it causes rounding errors. Instead, you want to store the previous transform and then restore it.
So that would be like this:
public static final int NORMAL_WIDTH = 1024;
public static final int NORMAL_HEIGHT = 768;
private float scale;
public void calculateScale()
{
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
scale = Math.min( (float)d.getWidth() / NORMAL_WIDTH, (float)d.getHeight() / NORMAL_HEIGHT );
}
public void draw(Graphics g)
{
AffineTransform transform = ((Graphics2D)g).getTransform();
((Graphics2D)g).scale(scale,scale);
drawAllMyCrap(g);
((Graphics2D)g).setTransform(transform);
}
Oh. You’re using LWJGL.
public void draw()
{
GL11.glPushMatrix();
GL11.glScalef(scale,scale,1.0f);
drawAllMyCrap();
GL11.glPopMatrix();
}
Thank you very much Eli Delventhal, that code helped me a lot.
For those who don’t want to keep the same scale for width and height, just change the code a little bit. Like this:
public static final int NORMAL_WIDTH = 1024;
public static final int NORMAL_HEIGHT = 768;
private float scaleWidth, scaleHeight;
public void calculateScale()
{
scaleWidth = (float)Toolkit.getDefaultToolkit().getScreenSize().width / NORMAL_WIDTH;
scaleHeight = (float)Toolkit.getDefaultToolkit().getScreenSize().height / NORMAL_HEIGHT;
}
public void draw(Graphics g)
{
AffineTransform transform = ((Graphics2D)g).getTransform();
((Graphics2D)g).scale(scaleWidth, scaleHeight);
drawAllMyCrap(g);
((Graphics2D)g).setTransform(transform);
}
You can also implement a HierarchyBoundsListener to the JFrame, and realize some tests changing the size of it to see what happens with all the drawing stuff. Like this:
//JFrame class implementing the HierarchyBoundsListener interface (to detect when the jframe resizes).
....
@Override
public void ancestorResized(HierarchyEvent e) {
//'game' is the object to access a class extending Canvas or JPanel (where you're drawing the stuff).
game.setSize(getWidth(), getHeight()); //just resizing a JPanel or Canvas to the same JFrame size.
game.scaleWidth = (float)this.getWidth() / NORMAL_WIDTH;
game.scaleHeight = (float)this.getHeight() / NORMAL_HEIGHT;
}
// and then the Game class extending Canvas (or Jpanel)
...
//The Game class draw method using Canvas and BufferStrategy
public void draw()
{
Graphics2D g2d = (Graphics2D) bufferStrategy.getDrawGraphics();
AffineTransform transform = g2d.getTransform();
g2d.scale(scaleWidth, scaleHeight);
g2d.clearRect(0, 0, getWidth(), getHeight());
//Draw all the stuff
...
g2d.setTransform(transform);
g2d.dispose();
bufferStrategy.show();
}
Really simple and newbie, but i hope it may help someone…
You probably want to find the minimum scale between width and height and use that in uniform, then letterbox the rest. Otherwise you can end up with weird proportions on widescreen monitors and the like.