Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (538)
Games in Android Showcase (132)
games submitted by our members
Games in WIP (600)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  loading images from a sprite sheet?  (Read 3487 times)
0 Members and 1 Guest are viewing this topic.
Offline ryuzaki

Junior Newbie





« Posted 2007-05-28 20:07:58 »

Hello.  I've only been learning java a little less than a year now.  I'm working on a 2d rpg game in which I have a sprite sheet that contains all of the images for my main characters sprite.  (the sprite sheet looks something like this:  http://sdb.drshnaps.com/sheets/Misc/Misc/Other/SBM4-Bomberman-.gif ).  However, I'm having trouble figuring out how to extract each image singularly.  I know it would involve finding the exact box of pixels that surround the desired image, cutting it out, and loading it into the program.  Is there a class in the api that can do this?  If so, can it also make the color surrounding the extracted image transparent?  On the other hand, am I taking a wrong approach to making a 2d game?  Is there a better way to load a main character with animation?  I appreciate any help on this matter.
Offline rdcarvallo

Senior Devvie


Projects: 5
Exp: 15 years


2D Java games forever!


« Reply #1 - Posted 2007-05-28 21:20:17 »

I know it would involve finding the exact box of pixels that surround the desired image, cutting it out, and loading it into the program.  Is there a class in the api that can do this?
In your paint method, you need to use something like:
1  
2  
3  
4  
5  
//g is your Graphics Object
//sw and sh are the width and height of sprite
//px and py are the position of your sprite in screen
//i and j are the coordinates of the sprite  in the sheet that you want to paint
g.drawImage(sheet, px,py, px+sw,py+sh, i*sw, j*sh, (i+1)*sw, (j+1)*sh, null);


If so, can it also make the color surrounding the extracted image transparent?

For this you need to use an image editor software to make the bg color of the prote sheet transparent.

Rafael.-
Offline ryuzaki

Junior Newbie





« Reply #2 - Posted 2007-05-28 21:28:11 »

Thank you very much!  As soon as I determine the pixel coordinates of the images I need I'll try your technique.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline ryuzaki

Junior Newbie





« Reply #3 - Posted 2007-05-29 05:59:00 »

Hmm...I'm having trouble getting it to work.  I was testing it on the sprite sheet that I posted above, the coordinates being where the sprite is facing down.  Here's some of the code (pretty much what you gave me) that I used to test it:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
import java.awt.*;
import javax.swing.*;

public class Bomberman extends JPanel {
    // Load image
    Image sheet = Toolkit.getDefaultToolkit().getImage("bomberman.gif");
    // Width and height of sprite
    int sw = 15;
    int sh = 30;
    // Position of sprite on screen
    int px = 100;
    int py = 100;
    // Coordinates of desired sprite image  
    int i = 0;
    int j = 125;
   
    public Bomberman() {
    }
   
    // Draw image
    public void paint(Graphics g) {
       super.paintComponent(g);
      g.drawImage(sheet, px,py, px+sw,py+sh, i*sw, j*sh, (i+1)*sw, (j+1)*sh, null);
    }
}


I have another class which has a main method to display the image on a panel.  Don't worry, I know the class works because I tested it with just a single image and it displayed it correctly (just making sure I rule out any possible errors).  Any suggestions as to what I'm missing?  I appreciate your help!
Offline broumbroum

Junior Devvie





« Reply #4 - Posted 2007-05-29 12:02:38 »

hello!
I think you missed the MediaTracker for your gif pic. It brings on all of the pic data within a required time you cannot know. Plus it is recommended to impl the paintComponent meth for all swing component in place of std paint. And moreover the heivyweight oomp like the JPanel don't require to call the super.paint, but mainly to redraw entirely in the paintComponent meth as you must clear the clipping rect.

Thus I recommend you to add the MediaTracker and to clearRect th graphics before to draw the GIf.  Tongue

::::... :..... :::::: ;;;:::™ b23:production 2006 GNU/GPL @ http://b23prodtm.webhop.info
on sf.net: /projects/sf3jswing
Java (1.6u10 plz) Web Start pool
dev' VODcast[/ur
Offline rdcarvallo

Senior Devvie


Projects: 5
Exp: 15 years


2D Java games forever!


« Reply #5 - Posted 2007-05-29 13:36:56 »

The problem is in the coordinates of the sprite, maybe I didn't explain it well.

If you have the next sheet:

ABCDEFG
HIJKLMN
OPQRSTU

And you want to display the sprite in the 'F' position, your coordinates are i = 5, j = 0.
For the 'K' sprite i = 3 and j = 1; and 'A' is in (0,0).

Also, as said by  broumbroum(entish??), you are using the Toolkit to get your image but aren't waiting for it to load. There is a new way to load your images using ImageIO.
1  
2  
3  
4  
//Change this
    Image sheet = Toolkit.getDefaultToolkit().getImage("bomberman.gif");
    //With this
    Image sheet = ImageIO.read(new File("bomberman.gif"));

You need to import the ImageIO and File classes and catch some Exceptions, but this is better to get your image loaded into your app.
 
Rafael.-

 
Offline ryuzaki

Junior Newbie





« Reply #6 - Posted 2007-05-29 21:59:53 »

Thank you for your input.  My program now reads images using ImageIO.  I also realized that my x and y coordinates were switched (i should equal 125 and j should equal 0).  Anyway, I'm using photoshop and found the exact pixels needed for the sprite, so the new coordinates are more accurate.  However, the drawImage line of code rdcarvallo gave me still didn't work as expected.  When I made i = 0 and j = 0 with the height = 100 and width =  100, it would display what one would expect: the first 100 x 100 pixels of the image.  But when I tried it with the sprite's exact pixels it wouldn't display anything.  After searching around the internet I came across the setClip method which seems pretty useful:

setClip(int x, int y, int width, int height)
      Sets the current clip to the rectangle specified by the given coordinates.

Using this method I was able to display the specific sprite that I wanted.  Here is the code so far:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
import java.awt.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;

class Bomberman extends JPanel {
    // Load image
    Image sheet = ImageIO.read(new File("bomberman.gif"));
    // Width and height of sprite
    int sw = 15;
    int sh = 23;
    // Position of sprite on screen
    int px = 100;
    int py = 100;
    // Coordinates of desired sprite image  
    int i = 124;
    int j = 2;
   
    public Bomberman() throws Exception {
    }
   
    // Draw image
    public void paint(Graphics g) {
       super.paintComponent(g);
       g.setClip(i, j, sw, sh);
   g.drawImage(sheet, i - 124,j - 2, null);
    }
}


It cuts out everything around the clipped part of the image, leaving just the desired sprite to be displayed.  Now the part that I'm not sure with now is how to set the location of the image.  The sprite is positioned where it would be as if the rest of the image was there.  So, it's located 124 pixels to the right and 2 pixels down.  The problem is, how would I set it to a different position on the screen?  I think once this problem is solved I'll be able to figure out the rest for my program.  Any suggestions?
Offline oNyx

JGO Coder


Medals: 2


pixels! :x


« Reply #7 - Posted 2007-05-30 03:12:31 »

There are half a zillion ways to do that and setClip isn't really one of the fastest, I'm afraid.

IIRC the last time we benched it over here... copying individual frames over to their own BufferedImages results in the fastest rendering. Especially if the sprite sheet exceeds 2^16 (eg 256x256) pixels. How you copy it over doesn't really matter since it's only done once.

Btw using an equally spaced sheet (each cell has the same size) is a lot easier to handle. If the sizes vary a lot you can use sprite packing tools to create a sheet and some text file (=atlas), which keeps track which sprite is stored where.

弾幕 ☆ @mahonnaiseblog
Offline ryuzaki

Junior Newbie





« Reply #8 - Posted 2007-05-30 04:32:56 »

How you copy it over doesn't really matter since it's only done once.

Hmm.  I understand what you are saying.  I don't know of any way to copy the sprites location and store it as its own image though...can you give an example of how you would do it?  Or the kind of process it would take?
Offline oNyx

JGO Coder


Medals: 2


pixels! :x


« Reply #9 - Posted 2007-05-30 07:16:08 »

For the copy process...  you create a compatible BufferedImage in the desired size, get the Graphics object, set the composite mode to AlphaComposite.Src (this allows you to overwrite it with the alpha, too - instead of using the alpha to blend), draw the image, and finally you dispose the Graphics object. Now you have a freshly created image which only contains this single frame.

Here is a simple demo:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;
import javax.swing.*;

public class SpriteSlice extends Canvas{
   GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
   BufferStrategy strat;
   static int []keys = new int[0xFF];
   static int []prevKeys = new int[0xFF];
   BufferedImage [][]cards;
   int W=300;
   int H=200;
   int cardNum=11; //11=queen
   int cardType=1; //1=hearts
   public SpriteSlice(){
      BufferedImage img=null;
      try{
         img=ImageIO.read(new BufferedInputStream(getClass().getResourceAsStream("/cards.gif")));
      }catch(IOException e){
         e.printStackTrace();
         System.exit(1);
      }
      cards=slice(img,13,4);//13 rows, 4 columns

      setSize(W,H);
      JFrame f=new JFrame("SpriteSlice");
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      f.add(this);
      f.pack();
      f.setResizable(false);
      f.setVisible(true);

      createBufferStrategy(2);
      strat=getBufferStrategy();

      enableEvents(AWTEvent.KEY_EVENT_MASK);

      requestFocusInWindow();

      gameLoop();
   }
   public void gameLoop(){
      while(true){
         //### tick
         if(down(KeyEvent.VK_RIGHT))
            cardNum++;
         if(down(KeyEvent.VK_LEFT))
            cardNum--;
         if(down(KeyEvent.VK_DOWN))
            cardType++;
         if(down(KeyEvent.VK_UP))
            cardType--;
         cardNum%=cards.length;
         if(cardNum<0)
            cardNum+=cards.length;
         cardType%=cards[0].length;
         if(cardType<0)
            cardType+=cards[0].length;

         //### draw
         Graphics g=strat.getDrawGraphics();
         g.setColor(new Color(0x778899));
         g.fillRect(0,0,W,H);
         g.setColor(new Color(0xFFFFFF));
         g.drawString("cards["+cardNum+"]["+cardType+"]",10,10);
         g.drawString("(use the cursor keys to change)",10,25);
         /*
         for(int i=0;i<4;i++){
            g.drawString(""+keys[KeyEvent.VK_LEFT+i],10+10*i,180);
            g.drawString(""+prevKeys[KeyEvent.VK_LEFT+i],10+10*i,195);
         }*/

         drawCentered(g,cards[cardNum][cardType]);
         g.dispose();
         strat.show();
      }
   }
   private void drawCentered(Graphics g, BufferedImage img){
      g.drawImage(img,W/2-img.getWidth()/2,H/2-img.getHeight()/2,null);
   }
   private BufferedImage[][] slice(BufferedImage source, int cols, int rows){
      int w=source.getWidth();
      int h=source.getHeight();
      int spritew=w/cols;
      int spriteh=h/rows;
      BufferedImage [][]slices=new BufferedImage[cols][rows];
      for(int x=0;x<cols;x++){
         for(int y=0;y<rows;y++){
            slices[x][y]=gc.createCompatibleImage(spritew,spriteh,source.getTransparency());
            Graphics2D g=(Graphics2D)slices[x][y].getGraphics();
            g.setComposite(AlphaComposite.Src);
            g.drawImage(
               source,
               0,
               0,
               spritew,
               spriteh,
               w/cols*x,
               h/rows*y,
               w/cols*x+spritew,
               h/rows*y+spriteh,
               null
            );
            g.dispose();
         }
      }
      return slices;
   }
   private boolean down(int key){
      boolean ret=false;
      if(keys[key]==1&&prevKeys[key]==0) //currently down, previously up
         ret=true;
      prevKeys[key]=keys[key];
      return ret;
   }
   public void processKeyEvent(KeyEvent ke){
      keys[ke.getKeyCode()&0xFF]=(ke.getID()==KeyEvent.KEY_RELEASED)?0:1;
   }
   public static void main(String[]args){
      new SpriteSlice();
   }
}


The used image:
http://kaioa.com/k/cards.gif

弾幕 ☆ @mahonnaiseblog
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline lhkbob

JGO Knight


Medals: 32



« Reply #10 - Posted 2007-05-30 13:16:08 »

I think I noticed one problem with ryuzaki's implementation of the original way he was told to get the sprite images from the sprite sheet.  Unless I'm mistaken, the sprite coordinates to access the sheet shouldn't be measured in pixels but function more like indices into a 2d array.  The sprite width/height will be in pixels.  This would explain why having (0,0) as your sprite coordinates work, since (0,0) in pixels is still the same as (0,0) as array indices.  If want the next image over, just increment whichever coordinate by 1, instead of the actual amount of pixels that it's over by.

Offline ryuzaki

Junior Newbie





« Reply #11 - Posted 2007-05-31 03:08:02 »

lhkbob, I believe you are right!  Using the
g.drawImage(sheet, px,py, px+sw,py+sh, i*sw, j*sh, (i+1)*sw, (j+1)*sh, null);
implementation, when I set i = 1 and j = 0, it displays the first sprite properly!  One problem with this method though is that the sprites aren't lined up evenly with the 2d array...for example when i = 8 and j = 0, it shows half of one sprite and half of another.  This could probably be solved if I knew exactly where the array lined up and redid the sheet accordingly...
oNyx, thank you for the elaborate example!  I think I understand what kind of method you used.  When I get more time I'll be able to look at it more thoroughly.  I think the technique of loading the images separately prior to the painting process is the most practical method, so I will probably end up using this one.  Thanks everyone for the help!  Cool
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

rwatson462 (29 views)
2014-12-15 09:26:44

Mr.CodeIt (20 views)
2014-12-14 19:50:38

BurntPizza (40 views)
2014-12-09 22:41:13

BurntPizza (76 views)
2014-12-08 04:46:31

JscottyBieshaar (37 views)
2014-12-05 12:39:02

SHC (50 views)
2014-12-03 16:27:13

CopyableCougar4 (47 views)
2014-11-29 21:32:03

toopeicgaming1999 (114 views)
2014-11-26 15:22:04

toopeicgaming1999 (102 views)
2014-11-26 15:20:36

toopeicgaming1999 (30 views)
2014-11-26 15:20:08
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!