Search code examples
javaswingrunnable

ClassCastException when I try to load gameplay for Space Invaders clone


Edit: Solved by Mark Rotteveel! During my save, I was using oos.writeObject(this);rather than oos.writeObject(GamePlay.this);

so I am having a lot of trouble getting my load function to work. When I applied this logic to simple programs I never had a problem, and this is the first time I make something that involves serializing and Threads. So I have a hunch the problem is due to Threads.

So the way I made this is I have a Gameplay class that saves itself using this. Saving this file gets no errors and it successfully appears in my folder.:

                try {

                FileOutputStream fos = new FileOutputStream("C:\\temp\\gameplay.dat");
                try (ObjectOutputStream oos = new ObjectOutputStream(fos)) {
                    oos.writeObject(this);
                }
                System.out.println("Gameplay Saved");
            } catch (IOException e) {
                System.out.println("Error Saving");
            }

When I am playing the game, the console tells me it saved successfully. However when I restart the game and try to load it, I get the ClassCastException. Here is the load game code

  public void actionPerformed(ActionEvent arg0) {
            try {
                FileInputStream fi = new FileInputStream("C:\\temp\\gameplay.dat");
                ObjectInputStream oi = new ObjectInputStream(fi);
                try {
                    Gameplay game = new Gameplay();
                    try {
                        game = (Gameplay) oi.readObject();
                    } catch (ClassCastException e) {
                        System.out.println("ClassCastException ");
                    }
                    oi.close();
                    fi.close();
                    game.addKeyBindings(panelCont);
                    frame.add(game);
                    Thread t1 = new Thread(game);
                    t1.start();
                } catch (ClassNotFoundException ex) {
                    Logger.getLogger(SpaceRaiders.class.getName()).log(Level.SEVERE, null, ex);
                }

            } catch (FileNotFoundException e) {
                System.out.println("File not found");
            } catch (IOException e) {
                System.out.println("Error initializing stream");

            }

I really want to understand what is going on, and I appreciate any help if someone could simply point me in the right direction!

Note if you need to look at the Gameplay class, I don't mind posting it, but it is quite long and I don't want to overwhelm.

Here is the Gameplay class

public class Gameplay extends JPanel implements ActionListener, Runnable, Serializable {

private Ship player = new Ship(new Point(200, 555));
private Timer t = new Timer(5, this);
private int score;

//Laser and shot variables ----------
private ArrayList<Laser> lasers = new ArrayList<Laser>();
private boolean readytofire;
private boolean shot = false;

//Invader variables -----------
private ArrayList<Invader> invaders = new ArrayList<Invader>();

public Gameplay() {
    super();
    t.start();
    setFocusable(true);
    requestFocus();
    setFocusTraversalKeysEnabled(false);

    for (int j = 0; j < 80; j += 20) {
        for (int i = 0; i < 20; i++) {
            invaders.add(new Invader(5 + i * 30, j));
        }
    }
}

public boolean addLaser(Laser a) {
    lasers.add(a);
    return true;
}

public boolean addPlayer(Ship p) {

    this.player = p;

    return true;
}

// Les évenements claviers --------------------
@Override
public void actionPerformed(ActionEvent ae) {
    repaint();
}

public void addKeyBindings(JComponent jc) {
    jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "moveRight");
    jc.getActionMap().put("moveRight", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            moveRight();
        }
    });

    jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "moveLeft");
    jc.getActionMap().put("moveLeft", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            moveLeft();
        }
    });
    jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "shoot");
    jc.getActionMap().put("shoot", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            shoot();
        }
    });

    // FONCTIONS DE SAUVEGARDE------------------------
    jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "save");
    jc.getActionMap().put("save", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            try {

                FileOutputStream fos = new FileOutputStream("C:\\temp\\gameplay.dat");
                try (ObjectOutputStream oos = new ObjectOutputStream(fos)) {
                    oos.writeObject(this);
                }
                System.out.println("Gameplay Saved");
            } catch (IOException e) {
                System.out.println("Error Saving");
            }

        }
    });
}

// Le mouvement du Ship --------------------
public void moveRight() {
    if (player.getCentre().getX() >= 580) {
        player.setX(580);
    } else {
        double movement = player.getCentre().getX();
        movement += 10;
        player.setX(movement);
    }
    this.repaint();
}

public void moveLeft() {
    if (player.getCentre().getX() <= 20) {
        player.setX(20);
    } else {
        double movement = player.getCentre().getX();
        movement -= 10;
        player.setX(movement);
    }
    this.repaint();
}

// Création du laser --------------------
public void shoot() {

    shot = true;
    if (readytofire) {
        Point top = new Point(player.getTopX(), player.getTopY());
        Laser laser = new Laser(top);
        addLaser(laser);

    }
}

// Mouvement du laser --------------------    
public void moveShot() {
    if (shot) {
        for (Laser l : lasers) {
            l.setY(l.getTopLeft().getY() - 2);

        }
    }
}

// Collision Detection
public boolean checkCollision(Invader i, Laser l) {
    double x = i.getlocX();
    double y = i.getlocY();
    if (l.getX() > x && l.getX() < x + 15 && l.getY() > y && l.getY() < y + 15) {
        return true;
    } else {
        return false;
    }
}

public boolean outBounds(Laser l) {

    if (l.getY() == 0) {
        return true;
    } else {
        return false;
    }
}

// Dessiner les graphiques --------------------
@Override
public void paint(Graphics g) {
    setBackground(Color.black);
    super.paint(g);
    player.draw(g);
    try {
        for (Laser l : lasers) {
            l.draw(g);
        }
        for (Invader i : invaders) {
            i.draw(g);
        }

    } catch (Exception e) {

    }
    g.drawString("Score: " + score, 0, 10);

}

// public void paintComponent (Graphics g){
// Controle Thread
public void run() {

    while (true) {
        moveShot();

        //Verifie Collision-------------------------------------------------
        List<Invader> invaderRemove = new ArrayList<Invader>();
        List<Laser> laserRemove = new ArrayList<Laser>();
        for (Invader i : invaders) {
            for (Laser l : lasers) {

                if (checkCollision(i, l) == true) {

                    laserRemove.add(l);
                    invaderRemove.add(i);
                    score += 1;
                }
            }
        }

        lasers.removeAll(laserRemove);
        invaders.removeAll(invaderRemove);

        //Enleve les lasers si ils arrivent a la fin du frame---------------
        for (Laser l : lasers) {
            if (outBounds(l) == true) {
                laserRemove.add(l);

            }

        }
        lasers.removeAll(laserRemove);

        //Mouvement des invaders
        for (Invader i : invaders) {
            i.moveAndUpdate();

        }

        try {
            Thread.sleep(10);
            readytofire = true;

        } catch (InterruptedException ex) {
            Logger.getLogger(Gameplay.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

}

And the stacktrace

java.lang.ClassCastException: spaceraiders.vue.Gameplay$4 cannot be cast to spaceraiders.vue.Gameplay
at spaceraiders.Controlleur.SpaceRaiders$2.actionPerformed(SpaceRaiders.java:106)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.Component.processMouseEvent(Component.java:6533)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
at java.awt.Component.processEvent(Component.java:6298)
at java.awt.Container.processEvent(Container.java:2236)
at java.awt.Component.dispatchEventImpl(Component.java:4889)
at java.awt.Container.dispatchEventImpl(Container.java:2294)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
at java.awt.Container.dispatchEventImpl(Container.java:2280)
at java.awt.Window.dispatchEventImpl(Window.java:2746)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Solution

  • This is your code:

    jc.getActionMap().put("save", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            try {
                FileOutputStream fos = new FileOutputStream("C:\\temp\\gameplay.dat");
                try (ObjectOutputStream oos = new ObjectOutputStream(fos)) {
                    oos.writeObject(this);
                }
                System.out.println("Gameplay Saved");
            } catch (IOException e) {
                System.out.println("Error Saving");
            }
        }
    });
    

    This creates an anonymous class, and the this in oos.writeObject(this) refers to this anonymous class. It does not refer to your GamePlay instance. So you are serializing that Action, and when deserializing and casting to GamePlay, the cast fails.

    You either need to make the action call a method in the GamePlay class that contains the actual serialization code, or you need to reference the enclosing class using GamePlay.this. See for details: Getting hold of the outer class object from the inner class object