This site requires JavaScript, please enable it in your browser!
Greenfoot back
DaRafster
DaRafster wrote ...

2020/6/19

Help with java.lang.ClassCastException

DaRafster DaRafster

2020/6/19

#
I've seen the threads which have discussed this but I still have trouble identifying the solution to my problem. So I've had this problem in my past projects but I've found other ways around it, but it messed up other code in different classes. So I've made a pipe in "myWorld" which sends Mario to "CoinWorld" when Mario is on it. I've added a clump of coins in the middle of "CoinWorld", as I was testing it, Mario touched the coin and this error popped up. It led me to the class coin, where the error lies in the checkTouch() method:
public class Coin extends Items
{
    /**
     * Act - do whatever the Coin wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act() 
    {
        checkTouch();
    }    

    public void checkTouch()
    {
        MyWorld world = (MyWorld)getWorld();
        ScoreBoard getScoreBoard = world.getScoreBoard();
        
        if(isTouching(Mario.class))
        {
            getScoreBoard.addScore(5);
            world.removeObject(this);
        }   
    }
}
So how can I be able to access the same scoreboard without casting? Or is there some other solution? Here is the myWorld class which carries the scoreboard I need to access:
public class MyWorld extends greenfoot.World
{
    GreenfootImage bg = new GreenfootImage("MarioBackground.png");
    private ScoreBoard theScoreboard;
    private Mario mario;
    private int level = 1;
    /**
     * Constructor for objects of class MyWorld.
     * 
     */
    public MyWorld()
    {    
        super(1500, 500, 1); 
        setBackground(bg);
        theScoreboard = new ScoreBoard();
        mario = new Mario();
        prepare();
    }

    /**
     * Prepare the world for the start of the program.
     * That is: create the initial objects and add them to the world.
     */
    private void prepare()
    {
        // switches sections everytime mario hits the edge of the world, must complete three sections each time to go to the next level
        switch(level)
        {
            case 2:
            // entities
            addObject(mario,95,460);
            Koopa koopa = new Koopa();
            addObject(koopa,616,172);
            Koopa koopa2 = new Koopa();
            addObject(koopa2,1356,326);
            // flooring
            addFloor();
            removeFloor(800, 20);
            // other
            addObject(theScoreboard,100, 50);
            addObject(new Timer(), getWidth()/2, 50);
            // platforms
            regularBlocks(175,350,8);
            regularBlocks(406,202,10);
            regularBlocks(445,95,3);
            regularBlocks(541,95,3);
            regularBlocks(700,350,8);
            regularBlocks(925,450,8);
            regularBlocks(1200,358,8);
            regularBlocks(1237,258,2);
            regularBlocks(1309,258,2);
            // coinblocks
            coinBlocks(517,95,1);
            coinBlocks(1285,258,1);
            // walls
            addWalls(480,226,4,4);
            addWalls(480,394,4,4);
            addWalls(973,258,4,4);
            addWalls(973,0,4,8);
            // coins
            Coin coin = new Coin();
            addObject(coin,514,352);
            Coin coin2 = new Coin();
            addObject(coin2,1005,212);
            Coin coin3 = new Coin();
            addObject(coin3,490,25);
            Coin coin4 = new Coin();
            addObject(coin4,550,24);
            Coin coin5 = new Coin();
            addObject(coin5,210,304);
            Coin coin6 = new Coin();
            addObject(coin6,306,302);
            Coin coin7 = new Coin();
            addObject(coin7,749,314);
            Coin coin8 = new Coin();
            addObject(coin8,827,314);
            Coin coin9 = new Coin();
            addObject(coin9,1284,317);
            Coin coin10 = new Coin();
            addObject(coin10,1007,411);

            break;

            case 1:
            // entities
            addObject(mario, 95, 460);

            // objects
            addObject(theScoreboard,100, 50);
            addObject(new Timer(), getWidth()/2, 50);

            // walls
            addWalls(192,392,4,4);
            addWalls(360,272,4,9);
            addWalls(528,392,4,4);

            // platforms
            regularBlocks(695,416,6);
            regularBlocks(12,300,5);
            // flooring
            addFloor();
            removeFloor(288,3);
            removeFloor(456,3);
            // pipes
            VerticalPipe verticalPipe = new VerticalPipe();
            addObject(verticalPipe,26,253);
            break;

            case 3:
            addFloor();
            removeFloor(getRandomNumber(500,750), 8);
            removeFloor(getRandomNumber(1000, 1250), 9);
            addObject(new Timer(), getWidth()/2, 50);
            addObject(mario, 95, 460);
            addingKoopas(level);            
            Flag flag = new Flag();
            addObject(flag,1450,335);
            EmptyBlock emptyBlock = new EmptyBlock();
            emptyBlock.getImage().scale(47, 50);
            addObject(emptyBlock,1450, 455);
        }
    }
    
    public void nextSection()
    {
        removeObjects(getObjects(null)); //removes all objects
        level++; //moves to next section
        prepare(); // adds all objects for section 2
    }

    // This method auto-generated a floor for the entire level, no matter how long it is! Wohoo generalization :)
    public void addFloor()
    {
        Block block = new Block();
        int blockWidth = block.getImage().getWidth();
        int blockHeight = block.getImage().getHeight();
        int numBlocksFloor = getWidth() / blockWidth;

        for (int i = 0; i <= numBlocksFloor; ++i)
        {
            addObject(new Block(), i*blockWidth, getHeight() - blockHeight/2);
        }
    }

    // This method auto-generates a singular gap in the floor whenever requested; able to control the distance of the gap and where it will be located
    public void removeFloor(int xValue, int gapDistance)
    {
        Block block = new Block();
        int blockHeight = block.getImage().getHeight();
        int blockWidth = block.getImage().getWidth();
        
        for(int i = 0; i < gapDistance; i++)
        {
            removeObjects(getObjectsAt(xValue + i*blockWidth, getHeight() - blockHeight/2, Block.class));
        }
    }
    
    // A modular method designed to add coin blocks in clumps of [amount of blocks] whenever requested
    public void coinBlocks(int xStart, int yStart, int blocks)
    {
        CoinBlock cb = new CoinBlock();
        int blockWidth = cb.getImage().getWidth();
        
        for(int a = 0; a < blocks; a++)
        {
            addObject(new CoinBlock(), xStart+(a*blockWidth), yStart);
        }
    }
    
    // A modular method designed to add regular blocks as platforms
    public void regularBlocks(int xStart, int yStart, int blocks)
    {
        Block block = new Block();
        int blockWidth = block.getImage().getWidth();
        
        for(int i = 0; i < blocks; i++)
        {
            addObject(new Block(), xStart+(i*blockWidth), yStart);
        }
    }
    
    // a method designed to create a huge clump of blocks and make a brick wall
    public void addWalls(int xStart, int yStart, int blocks, int rows)
    {
        Block block = new Block();
        int blockHeight = block.getImage().getHeight();
        
        for(int i = 0; i < rows; i++)
        {
            regularBlocks(xStart,yStart+(i*blockHeight),blocks);
        }
    }
    
    // This method adds and controls the amount of koopas that are added into the world
    public void addingKoopas(int koopas)
    {
        for(int i = 0; i < koopas; i++)
        {
            addObject(new Koopa(), getRandomNumber(500,1300), getHeight()/2);          
        }
    }
    
    public void addingParas(int paras)
    {
        for(int i = 0; i < paras; i++)
        {
            addObject(new ParaTroopa(), getRandomNumber(500, 1300), 80);
        }
    }
    
    // A method which generates a number between the desired values
    public int getRandomNumber(int start,int end)
    {
       int normal = Greenfoot.getRandomNumber(end-start+1);
       return normal+start;
    }
    
    public Mario getMario()
    {
        return mario;
    }
    
    public ScoreBoard getScoreBoard()
    {
        return theScoreboard;
    }
}
And here's the pipe class where I changed the world into "CoinWorld":
public class VerticalPipe extends Platform
{
    /**
     * Act - do whatever the VerticalPipe wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act() 
    {
        checkMario();
    }    
    
    // This method checks if mario is on top or not
    public void checkMario()
    {
        int blockHeight = getImage().getHeight();
        int yDistance = blockHeight/-2;
        Actor mario = getOneObjectAtOffset(0,yDistance - 5, Mario.class);
        
        if(mario != null)
        {
            Greenfoot.setWorld(new CoinWorld());
        }
    }
}
One of the errors I come across the most, so finding a solution around this will be a life-saver!
danpost danpost

2020/6/19

#
As is, once line 21 in the VerticalPipe class is executed , your MyWorld object and its ScoreBoard object, with the score it holds, no longer exist. Well, you can still work with them up to line 23, where the method ends. You can probably move the ScoreBoard object to the CoinWorld world so the score within is retained. Or, if a new ScoreBoard is already in the world, set its score to the MyWorld ScoreBoard score. Either way, the checkTouch method in the Coin class will need to determine which type world it is in before casting the world returned by getWorld:
private void checkTouch()
{
    if (isTouching(Mario.class))
    {
        ScoreBoard scoreboard = null;
        if (getWorld() instanceof MyWorld) scoreboard = ((MyWorld)getWorld()).getScoreBoard();
        if (getWorld() instanceof CoinWorld) scoreboard = ((CoinWorld)getWorld()).getScoreBoard();
        scoreboard.add(5);
        getWorld().removeObject(this);
    }
}
DaRafster DaRafster

2020/6/19

#
Thanks, it worked! I passed my scoreboard object to the CoinWorld world while using the method you provided above! Just one question, what does this line of code mean:
ScoreBoard scoreboard = null;
Is this equivalent to saying:
ScoreBoard scoreboard  = new ScoreBoard();
That's my only question to the solution you provided but thank you, this will be a life-saver for future projects!
danpost danpost

2020/6/19

#
DaRafster wrote...
Just one question, what does this line of code mean:
ScoreBoard scoreboard = null;
Is this equivalent to saying:
ScoreBoard scoreboard  = new ScoreBoard();
Definitely NOT equivalent. This:
ScoreBoard scoreboard = new ScoreBoard();
is equivalent to:
ScoreBoard scoreboard;
scoreboard = new ScoreBoard();
In the second code set, line 1 declares a new (object reference) variable and line 2 initializes the variable by assigning an object (reference) to it. In the first code set, both are done in one statement. This line:
ScoreBoard scoreboard = null;
declares the variable and also initializes it. But initializing it to null value means that it will not initially be assigned any actual object to reference. I believe it needs to be initialized as above in line 5 because the compiler might otherwise complain about it as the assignment (whichever is done by line 6 or 7) is in a if block (with "scoreboard may not have been initialized").
You need to login to post a reply.