I want to send a Image object over a network in java.
Im getting this error.
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: sun.awt.image.OffScreenImage
The java Image object doesn't implement Serializable. Is there a way to get around this?
Ive already tried making a subclass of image and implement it but Then I got errors when using the createImage method. Thanks for any help.
EDIT*
Ok here is the code but there is kinda a lot. The idea of the program is for it to be a pictionary game. Someone can draw using basic tools and it will send it over a network and draw that image on other clients screen.
This is my basic draw area where the user and draw using a line tool. On mouse Released it will try and send the Image object over to the server.
class PadDraw extends JComponent {
Image image;
Graphics2D graphics2D;
int currentX, currentY, oldX, oldY;
int lineSize = 1;
public PadDraw() {
setDoubleBuffered(false);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
oldX = e.getX();
oldY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
currentX = e.getX();
currentY = e.getY();
//graphics2D.drawLine(oldX, oldY, currentX, currentY); //this is where it does the drawing
//It seems to draw a line between the old coordinate point and the new coordinate point rather than drawing it as points
//Test to see if I can get a drawoval to work rather than a line
//graphics2D.fillOval(currentX-1, currentY-1, 2, 2);
//if this works it should draw an oval at the cursor position rather than drawing a line
//technically it works, but without a line it causes gaps
//I may have found it. Testing the setStroke method
graphics2D.setStroke(new BasicStroke(lineSize));
graphics2D.drawLine(oldX, oldY, currentX, currentY);
repaint();
oldX = currentX;
oldY = currentY;
}
});
addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
try {
clientOutputStream.writeObject(image);
} catch (IOException ex) {
Logger.getLogger(ChatClient.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
#Override
public void paintComponent(Graphics g) {
if (image == null) {
image = (Image) createImage(getSize().width, getSize().height);
graphics2D = (Graphics2D) image.getGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
clear();
}
g.drawImage(image, 0, 0, null);
}
public void updateImage(Image image){
this.image = image;
repaint();
}
public void clear() {
graphics2D.setPaint(Color.white);
graphics2D.fillRect(0, 0, getSize().width, getSize().height);
//graphics2D.setPaint(Color.BLACK);
lineSize = 1;
repaint();
}
public void fill(){
Color c = findColor();
graphics2D.setPaint(c);
graphics2D.fillRect(0, 0, getSize().width, getSize().height);
repaint();
}
public void changeColor(Color theColor) {
graphics2D.setPaint(theColor);
repaint();
}
public Color findColor() {
return graphics2D.getColor();
}
public void changeSize(int size) {
lineSize = size;
}
}
This is my threaded class for the image on the server.
private static class Handler2 extends Thread {
private Socket socket1;
private ObjectInputStream serverInputStream;
private ObjectOutputStream serverOutputStream;
public Handler2(Socket sock1) {
socket1 = sock1;
}
#Override
public void run() {
Image image = null;
try {
serverInputStream = new ObjectInputStream(socket1.getInputStream());
serverOutputStream = new ObjectOutputStream(socket1.getOutputStream());
oos.add(serverOutputStream);
while (true) {
image = (Image)serverInputStream.readObject();
for (ObjectOutputStream ooss : oos) {
ooss.writeObject(image);
}
}
} catch (IOException e) {
System.out.println(e);
} catch (ClassNotFoundException ex) {
Logger.getLogger(ChatServer.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if (serverOutputStream != null) {
oos.remove(serverOutputStream);
}
try {
socket1.close();
} catch (IOException e) {
System.out.println(e);
}
}
}
}
And back in the client I have a method for getting the image back from the server.
public void run2() throws IOException, ClassNotFoundException, InterruptedException {
// Make connection and initialize streams
serverAddress = getServerAddress();
Socket socket2 = new Socket(serverAddress, 9999);
//String theIP = getIP();
//Socket socket2 = new Socket(theIP, 9999);
// Process all messages from server, according to the protocol.
clientOutputStream = new ObjectOutputStream(socket2.getOutputStream());
clientInputStream = new ObjectInputStream(socket2.getInputStream());
while (true) {
Image ni = (Image)clientInputStream.readObject();
drawPad.updateImage(ni);
}
}
I know my code is kinda bad. I split thinks up a lot to test individual parts. As fare as the network code. It should work. The only problem I believe is that its not serializable.
The java Image object doesn't implement Serializable. Is there a way to get around this?
You can Serialize it yourself. You can wrap it with a class which is Externalizable, or you can write the data in the image without using writeObject.
Simple answer: no. sun.awt.image.OffScreenImage is not meant to be directly serialized so you can't just add the Serializable interface on a child class to make it serializable.
You have to find a work around, I'm not sure what is this image about but, for example, if it's from a set of known images then you can just send a key to it over the network so that you can recover it on the otherside. If you need to pass directly the image data then you'll have to encapsulate it in another object and rebuild the OffScreenImage on the other side.
You can extend the class and make it serializable with some work but since it seems to be a class tighly coupled with the operating system it will need some thinking.
Related
I'm trying to animate the sprite in my game when a button is pressed, but when I press the button, it skips the animation. Its supposed to go one pixel, change sprites, and then go one more pixel and change back. Here is the code
//for all
import java.nio.file.*;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.awt.image.*;
import java.net.*;
import java.awt.*;
import javax.swing.*;
import static java.lang.invoke.MethodHandles.*;
import java.awt.event.*;
//my Mario class (cut down a lot)
class Mario {
// all numbers multiplied by 2 from OG game
protected Direction dir;
protected int x, y;
protected BufferedImage sprite;
protected String currentSpriteName;
public Mario() {
this.x = 54;
this.y = 808;
dir = Direction.RIGHT;
setSprite(MVCE.SMALLSTANDFACERIGHT);
currentSpriteName = MVCE.SMALLSTANDFACERIGHT;
}
public void moveRight(){
if(this.dir == Direction.LEFT){
this.dir = Direction.RIGHT;
}
else if(this.dir == Direction.RIGHT){
this.x+=1;
}
}
public void animateMoveRight(){
if (currentSpriteName.equals(MVCE.SMALLSTANDFACERIGHT)){
setSprite(MVCE.SMALLWALKFACERIGHT);
}
else if (currentSpriteName.equals(MVCE.SMALLWALKFACERIGHT)){
setSprite(MVCE.SMALLSTANDFACERIGHT);
}
}
public void jump() {
this.y -= 46;
}
public void setSprite(String spriteName) {
URL spriteAtLoc = MVCE.urlGenerator(spriteName);
this.sprite = MVCE.generateAndFilter(sprite, spriteAtLoc);
}
public void getSprite(){
System.out.println(this.currentSpriteName);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(sprite, 0, 0, null); // DO NOT SET x and y TO ANYTHING,
// this sets 0,0 to top left!!
}
}
// my MarioRender class:
class MarioRender extends JLabel {
protected Mario marioSprite;
public MarioRender() {
marioSprite = new Mario();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
marioSprite.paint(g2);
setBounds(marioSprite.x, marioSprite.y, marioSprite.sprite.getWidth(), marioSprite.sprite.getHeight());
}
public void moveMarioRight(){
marioSprite.moveRight();
marioSprite.animateMoveRight();
setLocation(this.marioSprite.getX(), this.marioSprite.getY());
repaint();
//this is my attempt to make it animate
marioSprite.moveRight();
marioSprite.animateMoveRight();
setLocation(this.marioSprite.getX(), this.marioSprite.getY());
repaint();
}
public void jumpMario() {
marioSprite.jump();
setLocation(this.marioSprite.x, this.marioSprite.y);
repaint();
}
}
// direction class, solely for moving
enum Direction {
LEFT, RIGHT
}
// my calling class, which I called MVCE where I make the frame
public class MVCE extends JFrame {
MarioRender m = new MarioRender();
JLabel bg;
public MVCE() {
bg = new JLabel();
this.setSize(868, 915);
this.setVisible(true);
this.add(bg, BorderLayout.CENTER);
bg.setLayout(null);
bg.add(m);
m.setBounds(m.marioSprite.x, m.marioSprite.y, m.marioSprite.sprite.getWidth(),
m.marioSprite.sprite.getHeight());
KeyListener kl = new MoveListener();
this.addKeyListener(kl);
this.setFocusable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static final String SMALLSTANDFACERIGHT = "SmallStandFaceRight.bmp"; // 30
// x
// 32
public static final String SMALLJUMPFACERIGHT = "SmallJumpFaceRight.bmp"; // 32
// x
// 32
// generate URL
public static URL urlGenerator(String name) {
URL u = lookup().lookupClass().getResource(name);
return u;
}
// return image with filtered color
public static BufferedImage generateAndFilter(BufferedImage b, URL u) {
try {
b = ImageIO.read(u);
int width = b.getWidth();
int height = b.getHeight();
int[] pixels = new int[width * height];
b.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
// System.out.println(pixels[i]);
if (pixels[i] == 0xFFff00fe) {
pixels[i] = 0x00ff00fe;
}
}
BufferedImage newSprite = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
newSprite.setRGB(0, 0, width, height, pixels, 0, width);
b = newSprite;
} catch (IOException e) {
System.out.println("sprite not found");
e.printStackTrace();
}
return b;
}
// key listener
class MoveListener implements KeyListener {
public void keyPressed(KeyEvent k) {
if ((k.getKeyCode() == 39)) {
m.moveMarioRight();
///THIS IS SUPPOSED TO MOVE HIM 1, change sprite, and automatically move him back, it moves 2 pixels but no animation
}
if (k.getKeyCode() == 83) { // S key
m.marioSprite.setSprite(SMALLJUMPFACERIGHT);
m.jumpMario();
}
}
public void keyReleased(KeyEvent k) {
}
public void keyTyped(KeyEvent k) {
}
}
public static void main(String[] args) {
MVCE m = new MVCE();
}
}
I tried putting this between the calls to marioMoveRight():
try {
Thread.sleep(200);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
but it just delays the whole thing. I had also tried using an ActionListener, but I don't know how to make it react only when the key is pushed. as I had it,
I had this class inside of MVCE:
class TickListener implements ActionListener{
public void actionPerformed(ActionEvent a){
m.marioSprite.setSprite(Constants.SMALLWALKFACERIGHT);
repaint();
}
}
and this at the end of the MVCE constructor:
ActionListener ac = new TickListener();
final int DELAY = 1000;
Timer t = new Timer(DELAY, ac);
t.start();
but then, the Mario just moves automatically. I do not want to use a sprite sheet for this project, I am trying to do it as this guy did for SMB1.
Many problems, don't know which one or if any will fix the problem:
Don't use a KeyListener. If a component doesn't have focus the component won't receive the event. Instead use Key Bindings.
Don't use "==" to compare Objects. Instead you should be using the equals(...) method.
Don't override paintComponent. A painting method is for painting only. You should not be changing the bounds of the component in the painting method.
Do basic debugging (problem solving) before asking a question. A simple System.out.println(...) added to various methods will determine if the code is executing as you expect. Then when you ask a question you can ask a specific question telling us which block of code does not execute as you expect.
You never actually call the method animateMoveRight(), and if I understand correcly, that's what's changing the sprite. Also, I doubt that you see the sprite change when calling the same method twice in a row without any delay.
Try putting the animateMoveRight() method into the moveRight() or the moveMarioRight() method and, if neccessary because the animation is too fast, add your delay code back where you had it. Be careful not to let the main thread sleep, as this causes everything to freeze, so start another one or use a timer etc.
EDIT: Good timers
I'm not too familiar with the Timer class, so I end up using the Thread variant. There are many tutorials for that out there, just search for "java threads" or "java multithreading". This is IMO a solid tutorial you can check out.
I used some of the information around this site to find out to use URLs to get images into a jar file; which I want to be able to be used alone. But when I make the jar file with BlueJ, only some images show up.
Its a blackjack game, and only the table canvas shows up, while no cards ever do. Here's the code:
THIS WORKS (the table):
public class TableComponent extends JLabel{
BufferedImage table;
public TableComponent(){
URL finalTable = getClass().getResource("blackjackTableCanvas.jpg");
try {
table = ImageIO.read(finalTable);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g2);
g2.drawImage(table, 0, 0, null);
}...}
but this does not (the cards):
public class CardRender2 extends JComponent{
BufferedImage image;
String val;
String suit;
String filename;
public CardRender2(Card card) {
this.val = card.value.face;
this.suit = card.suit.toString();
filename = this.fetchCardFileLabel();
URL cardview = getClass().getResource("\\card deck\\" + filename + ".png");
try {
image = ImageIO.read(cardview);
} catch (IOException e) {
e.printStackTrace();
}
}
public CardRender2(){
this.val = null;
this.suit = null;
filename = "DEALER_FIRST_CARD";
URL cardview = getClass().getResource("\\card deck\\DEALER_FIRST_CARD.png");
try {
image = ImageIO.read(cardview);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g2);
g2.drawImage(image, 0, 0, null);
}...}
my cards are in a folder in the directory I try to import into BlueJ, whereas the table is in the directory root. There are 53 cards in there (incl dealer hidden card) and I'd rather not put all of then in the root. I tried to implement them similarly. How can I do this?
looks like changing from
URL cardview = getClass().getResource("\\card deck\\DEALER_FIRST_CARD.png");
to
URL cardview = getClass().getResource("card deck/DEALER_FIRST_CARD.png");
did the trick.
So i'm trying to create this java game about aircraft shooting aliens and stuff. The aircraft shoot a bullet every time mouse click. That mean the aircraft can shoot 10 or 20 or more bullets at a time. To demonstrate the bullet movement i tried Thread and Timer but the real problem is if i 1 bullet shot out that mean i created a new Thread(or Timer) and that make the game run very slow. Is there any way i can fix this problem?
Here a my code for bullet moving
public class Bullet extends JComponent implements Runnable {
int x;//coordinates
int y;
BufferedImage img = null;
Thread thr;
public Bullet(int a, int b) {
x = a;
y = b;
thr = new Thread(this);
thr.start();
}
protected void paintComponent(Graphics g) {
// TODO Auto-generated method stub
try {
img = ImageIO.read(new File("bullet.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// g.drawImage(resizeImage(img, 2), x, y, this);
g.drawImage(Plane.scale(img, 2, img.getWidth(), img.getHeight(), 0.125, 0.125), x, y, this);
width = img.getWidth() / 8;
height = img.getHeight() / 8;
super.paintComponent(g);
}
public void run() {
while(true)
{
if(y<-50)break;//if the bullet isnt out of the frame yet
y-=5;//move up
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
A bullet should NOT be on its own thread. There are several reasons for this, one of which is the one you mentioned - it is going to make your game very slow.
Try using one master thread which updates all bullets. You will need an update function in your bullet:
public class Bullet extends JComponent {
public void update() {
if(y<-50)return; //if the bullet isnt out of the frame yet
y-=5; //move up
}
//all your other code for your bullet
}
Then in your master thread have a list of bullets:
LinkedList<Bullet> bullets = new LinkedList<>();
In the run method of that thread, you can continuously update ALL bullets:
public void run() {
while(true)
{
for (Bullet b : bullets) {
b.update();
}
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
You will need to have a method in your master thread that lets you add a new bullet:
public void addBullet(Bullet b) {
bullets.add(b);
}
Then you can call that to add a new bullet and the master thread will update that bullet along with all the others.
so I am trying to make a simple program where you click on the screen and it creates a block that falls and collides with a larger block beneath and sticks to it. Kind of like a simple collision program. The problem is when I create one block it deletes the block previously. I made an array, but it still does this. Do any of you know what Im doing wrong? Im sure its a simple fix.
public class Screen extends JPanel implements Runnable {
public static JLabel statusbar; //displays a status bar showing what mouse movements are taking place
private Image cat; //image of the cat
public int xCoord ; //get the coordinates of the mouse pressed
public int yCoord ;
public int xCreate;
public int yCreate;
public Rectangle Ground;
public Rectangle Block;
public boolean isClicked = false;
public int clickCount = 0;
Rectangle blocks[] = new Rectangle[10];
int blocknum = 0;
public Screen(Frame frame) {
loadPic(); //calls the loadPic method above
Handlerclass handler = new Handlerclass(); //creates a new class to use the mouse motion listener
System.out.println("mouse works!");
addMouseListener(handler);
addMouseMotionListener(handler);
statusbar = new JLabel("default");
add(statusbar);
}
public void run(){ //this is the game run loop
System.out.println("this is running");
try{
} catch(Exception e) {} //exception handling
}
public void loadPic(){ //loads the picture from the other project but its the same pic
cat = new ImageIcon("C:\\Users\\Camtronius\\Documents\\NetBeansProjects\\Moving Block Proj\\src\\MovingBlock\\catIcon1.png").getImage(); //gets the image
System.out.println("Image Loaded!");
}
#Override public void paintComponent(Graphics g){
super.paintComponent(g); //paints the component, the picture, on top
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(cat, xCoord, yCoord, null);
g2d.setColor(Color.BLUE);
Ground = new Rectangle(0,450,550,50);
g2d.fillRect(0,450, 550, 50);
for(Rectangle blocknum : blocks){
if (blocks != null) {
g2d.setColor(Color.RED);
g2d.fillRect(xCreate,yCreate,50,50);
System.out.println(blocknum);
}
}
//move();
}
public void move(){
if(yCreate<400){
yCreate+=1;
}else{
}
if(Ground.intersects(blocks[blocknum])){
yCreate=400;
System.out.println("contains!");
}
}
private class Handlerclass implements MouseListener, MouseMotionListener{
public void mouseClicked(MouseEvent event){
}
public void mousePressed(MouseEvent event){
}
public void mouseReleased(MouseEvent event){
if(blocknum<blocks.length){
xCreate=event.getX();
yCreate=event.getY();
blocks[blocknum] = new Rectangle(50,50, xCreate, yCreate);
repaint();
}
blocknum=blocknum+1;
}
public void mouseEntered(MouseEvent event){
}
public void mouseExited(MouseEvent event){
}
public void mouseDragged(MouseEvent event){
}
public void mouseMoved(MouseEvent event){
statusbar.setText(String.format("Coordinates are: %d, %d", event.getX(),event.getY()));
xCoord=event.getX();
yCoord=event.getY();
}
}
}
Painting is a destructive process. That is, when a new paint cycle runs, the previous contents of the Graphics context should be cleared...
So, in you paintComponent method you are only painting the last block...
if(isClicked = true){
blocks[blocknum] = new Rectangle(50,50, xCreate, yCreate);
g2d.setColor(Color.RED);
g2d.fillRect(xCreate,yCreate,50,50);
System.out.println(blocknum);
repaint(); // THIS IS A BAD IDEA
}
DO NOT call any method that might cause repaint to be called. This will put you in a potential cycle of death that will consume your CPU.
Instead, you should loop through the blocks array and paint each one...
for (Rectangle block : blocks) {
if (block != null) {
g2d.setColor(Color.RED);
g2d.fill(block);
}
}
And in you mouseReleased method, you should be adding the new rectangles...
public void mouseReleased(MouseEvent event){
blocknum=blocknum+1;
if (blocknum < blocks.length) {
xCreate=event.getX();
yCreate=event.getY();
blocks[blocknum] = new Rectangle(xCreate, yCreate, 50, 50);
}
}
I'd suggest you take a look at Custom Painting, Painting in AWT and Swing and Concurrency in Swing for more details
I'm trying to draw over an image (with the mouse) in a JPanel, this is working, but when I try to take an screenshot of the panel and generate an image of this, I only can see the image background without drawn with the mouse.
This is my code to generate the background Panel.java
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(this.createImage("/imagenes/cuerpoHumano.png").getImage(), 0, 0, null);
}
This is my code to draw as a pencil over the image: Panel.java
private void formMouseDragged(java.awt.event.MouseEvent evt) {
x = evt.getX();
y = evt.getY();
this.getGraphics().setColor(Color.RED);
this.getGraphics().fillOval(x, y, 4, 4);
}
This is the code to generate an screenshot
Dimension size = panel.getSize();
BufferedImage image = (BufferedImage) panel.createImage(size.width, size.height);
Graphics g = image.getGraphics();
panel.paint(g);
g.dispose();
try {
String fileName = UUID.randomUUID().toString().substring(0, 18);
ImageIO.write(image, "jpg", new File(path, fileName + ".jpg"));
} catch (IOException e) {
e.printStackTrace();
}
When you are taking the screenshot, the paintComponent() method is called. This means it will only paint you the image. You have to store the mouse move inside some model and paint the contents of the model in the paintComponent() method. This method is triggered by calling repaint() on the panel during the mouse move.
I think this is code that works .
public class PanelImagenCuerpoHumano extends JPanel {
private int x = -1;
private int y = -1;
private Image image = null;
private ArrayList<Point> puntos = new ArrayList<Point>();
public PanelImagenCuerpoHumano() {
image = new ImageIcon(getClass()
.getResource("/imagenes/cuerpoHumano.png")).getImage();
this.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
x = e.getX();
y = e.getY();
puntos.add(new Point(x, y));
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
});
}
#Override
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null);
for (Point p : puntos) {
g.setColor(Color.red);
g.fillOval(p.x, p.y, 3, 3);
}
}
}