SUMMARY:
Basically I want to be able to render items based on their Image/Batch/Sprite to speed up my game, but I also have to sort the entities based on y position so that it draws in the correct order.
QUESTION:
How can I do this theoretically? (block of code is nice, but reasoning is better, thanks!)
FULL SITUATION:
I am creating a 2.5 D side-scroller in which I have found that ~500 fps is decent for the early stage of development (on my crap computer at least) but i want this to be higher as I am going to be adding more aspects later (further reducing fps) so I want to be as optimized as I can right now.
What I was wondering is if it is possible to render all entities with the same image file at the same time, to save on time loading up the image files. The struggle I have is that I can't just sort entities by their image file (or at least not that I can think of, that's why I'm asking) because I also need to render them from back to front because its a 2.5 D game.
Right now I am storing the Images in a HashMap, so it isn't completely terrible, I just want to know what I can improve on.
EDIT: Also to note I am using the Jframe and Jpanel classes with Graphics for rendering
You don't need to post a block of code if you don't want. I just want more of the theoretical reasoning.
Thanks!
-Kore
One thought that comes to mind is that if you have an image that you know will be used a lot, you can cache it and that might help.
There are ways to directly reference an image file using BufferedImage and ImageIcon (I would link directly, but i don't have enough points :/ ).
Possibly mixing caching and a direct reference with the image file could help speed up the process. Along with the caching you could possibly find a way to group all of the objects that need the same image file together (this could be done by making an arrayList of all of the objects that use this sprite.), so once one of the objects renders the file, they all render it from the cache at that time. Then the cache is cleared for the next image.
Also, if you were to have variables that pointed to your sprites, then you could have the objects load the variable instead of them all having to point to the image file.
i.e.
String path = "Image1.jpg";
File file = new File(path);
BufferedImage image = ImageIO.read(file);
and then all of your objects referenced image for the source of their image. However, I am not sure if this would be any faster than individually pointing each object to the file, this was just a thought.
Hope some of this answers what you were asking. Good luck!
---edit---
So my next thought could be implemented using a parent class and or just another class that would hold an ArrayList. So the idea is that you don't want to have to use the method images.get(imgPath) every time you want to load an image.
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.ImageIcon;
class SomeClass{
ImageIcon imgOne = new ImageIcon(new BufferedImage(images.get(imgPath)))
ImageIcon imgTwo = new ImageIcon(new BufferedImage(images.get(imgPathTwo)))
private ArrayList<ImageIcon> imagesHolder = new ArrayList<ImageIcon>(){{
add(imgOne);
add(imgTwo);
}};
public ImageIcon getImageIcon(int index){
return imagesHolder.get(index);
}
}
class test{
SomeClass imageHolder = new SomeClass();
JLabel label = new JLabel(imageHolder.getImageIcon(0));
JFrame f = new JFrame();
f.getContentPane().add(label);
}
In this, we have a class (someclass) whose object (that we only need one of) is storing every single image in an arraylist to the most processed state (imageIcon or whatever you are using can be used here) before actually putting it on the JFrame can create an object from to get all of the already rendered images.
This means that all you have to do at this point is add the image to the JFrame. you don't have to do a lot of the preprocessing of each image over and over again, because it's ready to just go on the JFrame.
While this does not particularly help a lot with rendering (cause most of this is processing of the image) it does help a lot with memory, because you are just creating 1 of each processed image and just repainting that same processed image.
hopefully this idea of processing all of the images once can help lead you to a way to have to only render the image once.
Related
I'm currently using a Canvas with an associated BufferStrategy. How can I copy the contents (image) of the Canvas and save it to a BufferedImage. So far I've only tried
BufferedImage image = ...
g = image.createGraphics();
canvas.printAll(g); //or paintAll
However, it appears that only a 'blank' (filled with the background color) screen is being drawn; I it's assume because that's the default behavior of Canvas.paint(). Is there as way for me to retrieve the image currently on screen (the last one shown with BufferStrategy.show()), or do I need to draw to a BufferedImage and copy the image to the BufferStrategy's graphics. It seems like that would/could be a big slow down; would it?
Why (because I know someone wants to ask): I already have a set of classes set up as a framework, using Canvas, for building games that can benefit from rapidly refreshing screens, and I'm looking to make an addon framework to have one of these canvases tied into a remote server, sending it's displayed image to other connected clients. (Along the lines of remotely hosting a game.) I'm sure there are better ways to do this in given particular cases (e.g. why does the server need to display the game at all?) But I've already put in a lot of work and am wondering if this method is salvageable without modifying (to much of) the underlying Game framework code I already have.
I use using WorldWind to plot data, and I also require a 2D interface as an overlay. I have been creating a new BufferedImage a few times per second to update the data, but this requires a lot of overhead. I'd like to redraw on an existing image to decrease the overall usage, both in terms of CPU and memory. I'm using this code before redrawing:
BufferedImage img = TrackingService.img.get(width).get(height);
g = img.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
g.fillRect(0,0,img.getWidth(),img.getHeight());
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
image = img;
ach element which can add to the UI returns a HashMap of BufferedImage to Point. Each BufferedImage and point is transformed into a ButtonAnnotation and added to an AnnotationLayer. Each x milliseconds, the system will:
layer.removeAllAnnotations();
layer.addAnnotations(buttonAnnotations);
redraw()
on the AWT Event queue. This works fine for markers, etc. but these images will never change from the first image I use. I've tried replacing the layer, disposing it, etc. I've tried writing the images to a debug file and noted that the BufferedImage is changing as expected. The problem is that WorldWind must be caching the images at some level. Since I am providing the same instance of BufferedImage to each new ButtonAnnotation, with a few modifications made using Graphics2D, it appears to be assuming that no changes have been made, when, in fact, there have been. This works perfectly fine if I use a new BufferedImage each time I want the data to change.
I have also tried the suggestions in This Question, and they did not work for me.
I am implementing a board game where each player has several units and each unit is shown to a user using a common image which I read from a file using the following method. I read this image at application startup and going to use it later.
private static BufferedImage readBufferedImage (String imagePath) {
try {
InputStream is = IconManager.class.getClassLoader().getResourceAsStream(imagePath);
BufferedImage bimage = ImageIO.read(is);
is.close();
return bimage;
} catch (Exception e) {
return null;
}
}
Units differ with various colorful tokens located on the top of that common image.
Before I just add several commonImages to JPanel and tokens were implemented using JLabels which were floating on the top of commonImage
//at startup
ImageIcon commonImage = new ImageIcon(readBufferedImage("image.png"));
...
JPanel panel = new JPanel();
panel.add(commonImage);
panel.add(commonImage);
//located JLabels with token on the top of each commonImage
However, now I want to use JScrollPane instead of JPanel, so I think it is a better approach to drawString() and drawImage() to each commonImage before I show it to a user.
I estimate roughly a number of units as 20. So now every turn for every unit I would need to generate on-the-fly separate BufferedImage with various tokens configuration.
The question is whether I should cache already generated BufferedImages depending on token configuration to extract from cache if the image has been generated before with same configuration?
The question you ask depends on a few factors:
How long it takes to generate the image from scratch
How long it takes to check if the image was generated before
How large each image will be
The probability of the image being reused
So without having a good knowledge of your application, no one is going to be able to give you an answer accurate for every situation.
In general, if you're only moving tokens around, it should be quick to implement an update and the paintComponent method of the panel your drawing. If you save your base image, token image and the current resulting image (basically what you proposed), I wouldn't anticipate a performance problem. The key is to do your updating of the image in your custom update method, and always draw the current resulting image in the paintComponent method.
Update
For example, you can cache the current display using code similar to this:
private BufferedImage cachedImage;
...
#Override
public void paintComponent(Graphics g){
//If the image needs to be refreshed draw it to the cache first
if(cachedImage == null){
cachedImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
super.paintComponent(cachedImage.getGraphics());
}
//Draw the image from cache
g.drawImage(cachedImage, 0, 0, this);
}
When you want to clear the cache, in your method that does the update, you'd set cachedImage = null;
But in your situation,
Any performance problems at the scale of 200x200 you experience would have to do with the number of drawings you're doing. Since I'm guessing you're not concerned about a frames/second rate, I doubt you'll have performance problems (you can collect timings to see exactly how long it takes to draw - I'd guess well under 100 ms)
The easiest thing to do would be cache the images of the tokens and their strings (I'm assuming they rarely change), but still dynamically create the layout of the tokens. (Any other performance gains in your situation aren't going to be so easy).
Caching previous rendering of the screen will essentially break the swing display model (not much point in using it - just do the layout yourself). This actually applies to pretty much any caching of a container.
I've read the java api, but I still do not understand the main difference between:
1) ImageIcon
2) BufferedImage
3) VolatileImage
4) Image
Can someone tell me when each one is used?
I wouldn't call this explanation below the absolute standard of using Java Image types, but these are the rules of thumb that I follow:
1. ImageIcon
This is typically used when you have small images that you want to add to buttons or to use as window icons. These may be created directly from anything that implements the Image interface.
2. BufferedImage
Typically used when you need to manipulate individual pixels within an image, or when you want to double-buffer a custom paint(Graphics g) method. The image resides in RAM, so it can take up a lot of space, and modifications to BufferedImage instances are not typically hardware-accelerated.
3. VolatileImage
Hardware-accelerated image, so it's fast, but you run the risk of the hardware-backed buffer being overwritten before you're done painting (although this rarely happens and according to Oracle, is only an issue for Windows-based machines). It's a little more difficult to use than BufferedImage for double-buffering custom paint(Graphics g) methods, but is well worth it if you find yourself doing a lot of pre-processing before rendering to screen.
4. Image
This is basically just an interface that defines some bare-bones functionality that every Image should have. You should use this when you don't need to modify an image's contents and/or want to make methods that handle read-only-image data most flexible.
Also, ImageIcon implements serializable so you can send it through java sockets. If you have Image objects, you have to convert them to ImageIcon and send. When the client side took the ImageIcons, it can convert them to Images again.
For a game I'm making I am storing all the sprites for the map inside one large(er) image. I want to be able to create an instance of Image for each image inside of the larger image that has all the sprites.
So how would I create an instance of Image from a set position of another Image.
The basic solution (if all tiles in your tilesheet have the same size) is to use the getSubimage(xpos, ypos, XSIZE, YSIZE) method from the class BufferedImage.
Otherwise, you'll have to store a set of position and size for all sprites in another file.
Your question is similar to this others one.
Since you are using the java.awt.Image-class i am guessing you are trying to create a reasonable game using AWT? You really should take a look at a different technology like http://en.wikipedia.org/wiki/Java_OpenGL. The problem you ran into (partitioning a sprite-sheet) is typical for a lot of other problems (rotation...) you will run into if you try to develop a game without something like Open-GL.
Considering only the problem at-hand: you can easily solve this in Open-GL by binding the whole sprite-sheet as texture (glBindTexture()) and giving for each glVertex() a glTexCoord2f(), no need to cut-out parts of the sprite-sheet.