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

15 hours ago

VERY COMPLICATED - Loading Zones somehow losing references to final variables?!

Bennylit Bennylit

15 hours ago

#
So I am making an overly complicated that uses "LoadingZone" and "Room"objects. Room objects are a series of actors that it adds in, and sets the background image to something new. LoadingZone objects sit in rooms, and hold other room objects. (So a LoadingZone in Room1 might hold the Room object for Room2. Whenever the player collides with a loading zone, it loads the Room2 object by clearing the world and adding all of the actors in Room2's storage.) In order to prevent memory heap errors (for example, creating a Room2 object requires creating all the actors within Room2, which includes a LoadingZone to Room1 -> we need to create a Room1 object -> creating a Room1 object requires creating all the actors within Room1, which includes a LoadingZone to Room2 -> we need to create a Room2 object -> creating a Room2 object requires creating all the actors within Room2, which includes a LoadingZone to Room1 -> we need to create a Room1 object... you get the point), I implemented that every room is static and final, and only created once in the room class. It reads as this:
public class Room extends Actor
{
    public ArrayList<ActorPoint> actors=new ArrayList<>();
    public GreenfootImage background;
    final static Room roboticsRoom = new RoboticsRoom();
    final static Room roboticsBackroom = new RoboticsBackroom();
    static Room soudRoom = new SoudRoom();
    static Room roboticsHall = new RoboticsHall();
    public Room(GreenfootImage b, ActorPoint[] a) {
        background=b;
        java.util.Collections.addAll(actors,a);
    }
    public Room(){}
}
(SoudRoom and JohnRoom are unused right now, that's why they're not final.) The constructor of a LoadingZone reads as follows:
public LoadingZone(int w, int h,int ro, Room r) {
        GreenfootImage g = new GreenfootImage(w,h);
        if (Player.DEBUG_MODE) {
            g.setColor(new Color(0,255,255,100));
            g.fill();
        }
        setImage(g);
        loaded=r;
        setRotation(ro);
    }
As you can see, it takes the Room object when it is created, ensuring there is no way to accidentally not initialize it. Creating the RoboticsRoom (the first static final object) looks like this:
public RoboticsRoom() {
        super(new GreenfootImage("RoboticsRoom2.png"),new ActorPoint[]{new ActorPoint(new CollisionBox(575,70),548,569),new ActorPoint(new CollisionBox(131,30),1035,364),new ActorPoint(new CollisionBox(16,41),975,397),new ActorPoint(new CollisionBox(14,35),1092,394),new ActorPoint(new CollisionBox(14,34),967,165),new ActorPoint(new CollisionBox(141,29),1030,134),new ActorPoint(new CollisionBox(10,32),1094,162),new ActorPoint(new CollisionBox(250,59),737,221),new ActorPoint(new CollisionBox(15,30),853,261),new ActorPoint(new CollisionBox(15,36),615,262),new ActorPoint(new CollisionBox(230,66),734,291),new ActorPoint(new CollisionBox(92,16),709,252),new ActorPoint(new CollisionBox(34,33),648,332),new ActorPoint(new CollisionBox(36,35),826,330),new ActorPoint(new CollisionBox(215,60),380,225),new ActorPoint(new CollisionBox(18,38),482,271),new ActorPoint(new CollisionBox(15,30),278,270),new ActorPoint(new CollisionBox(20,299),10,287),new ActorPoint(new CollisionBox(1097,19),548,9),new ActorPoint(new LoadingZone(40,170,90,Room.roboticsHall),981,679),new ActorPoint(new LoadingZone(40,170,0,Room.roboticsBackroom,777,583),21,564)});
    }
As you can see, it fills the room with various collision boxes, but those are not important. The important part is at the end:
new LoadingZone(40,170,0,Room.roboticsBackroom)
You can see here is makes a new LoadingZone, and for what room to load should the player collide with it, I enter "Room.roboticsBackroom"(an adjoining room). The first time this room is loaded, everything is tootsy dandy, and walking through it sets the room to the roboticsBackroom. The creation of the robotics back room looks something like this:
public RoboticsBackroom() {
        super(new GreenfootImage("RoboticsBackroom.png"),new ActorPoint[]{new ActorPoint(new CollisionBox(419,533),889,266),new ActorPoint(new CollisionBox(389,528),194,264),new ActorPoint(new CollisionBox(142,40),470,48),new ActorPoint(new CollisionBox(15,27),532,78),new ActorPoint(new CollisionBox(12,24),405,80),new ActorPoint(new CollisionBox(67,129),634,88),new ActorPoint(new CollisionBox(20,110),598,97),new ActorPoint(new CollisionBox(73,21),623,161),new ActorPoint(new LoadingZone(40,170,0,Room.soudRoom),198,615),new ActorPoint(new LoadingZone(40,170,0,Room.roboticsRoom,105,541),872,620)});
        
    }
Once again, it just creates a bunch of collision boxes, but also it makes a new loading zone with the robotics room as its location to send you to (because the player can walk between these rooms):
LoadingZone(40,170,0,Room.roboticsRoom,105,541)
So, the player walks in the roboticsBackroom, and walks back successfully. However, despite being the exact same room and loaded using the exact same code, the original loading zone which should hold "Room.roboticsBackroom" now has a null variable. I am very confused, because it has no constructors that allow the room object to be null, and the only variable ever put into it is a static and final variable, meaning the JVM should literally stop it from trying to modify the object to make it null. So in short, I am very confused, and would love to hear anything about why this might be happening. Also, here are some images that show the problem a little better (the loadingZones are light blue and semitransparent): If anyone has any idea why this happens, or how to fix it, or you need more information to fix it, please let me know. I am desperate.
Bennylit Bennylit

15 hours ago

#
Attempt 2 on those images: <iframe src="https://drive.google.com/file/d/1_eorxznInYs5V3MIcwAjc1KIIAfJKOb7/preview" width="640" height="480" allow="autoplay"></iframe> <iframe src="https://drive.google.com/file/d/1Ld_XLKHM4WhGfV-m_W3Tqeuq1VcDD42g/preview" width="640" height="480" allow="autoplay"></iframe> <iframe src="https://drive.google.com/file/d/1Bm5UlRb1F-EhnX1AgvNTLMehEy3v7Opr/preview" width="640" height="480" allow="autoplay"></iframe>
Bennylit Bennylit

15 hours ago

#
here's the links to those images since they can't seem to display: Image 1 Image 2 Image 3
danpost danpost

13 hours ago

#
Bennylit wrote...
the original loading zone which should hold "Room.roboticsBackroom" now has a null variable.
Make line 8 in the Room class snippet above final and see what happens. I do not think it could then be changed to a null value.
Bennylit Bennylit

12 hours ago

#
I tried that, and the loading zone still somehow gets a null value in its "loaded" object location.
Bennylit Bennylit

12 hours ago

#
I did a little more testing, and it seems the final values never become null, which... I guess I expected. I have found something unusual in my extended testing, though. When you walk back into an area, all of the loadingzones held room objects become null, not just the one you came from. Also, this error does still occur if you call Player.deployRoom(Room r) from clicking on it while Greenfoot is paused, in any room, whenever the same room is loaded twice.
Bennylit Bennylit

12 hours ago

#
One very notable find: if you deploy a room while already in it, and turn off the part of the code that deletes old LoadingZones, a new loadingzone spawns on top of the old one. (You probably figured this out in a second, but the significance of that is that is means it's not referencing the same final static LoadingZones, it's creating new ones.) I'm not sure why this happens, but I feel it is significant to this mystery.
danpost danpost

12 hours ago

#
Bennylit wrote...
the significance of that is that is means it's not referencing the same final static LoadingZones, it's creating new ones.)
Show codes for classes where LoadingZone objects are created.
Bennylit Bennylit

5 hours ago

#
I already did - currently the RoboticsBackroom and the RoboticsRoom constructors are the only place where LoadingZones are ever instantized. The place that actually adds them to the world is here:
public static void deployRoom(Room r) {
        MyWorld.w.removeObjects(MyWorld.w.getObjects(CollisionStuff.class));//gets rid of all the objects in the current room
        //MyWorld.w.removeObjects(MyWorld.w.getObjects(LoadingZone.class)); //gets rid of all the load objects
        System.out.println("Room.backroom=null: "+(Room.roboticsBackroom==null));
        System.out.println("r=null: "+(r==null));
        System.out.println("r.background=null: "+(r.background==null));
        MyWorld.w.setBackground(r.background);
        for (ActorPoint ap: r.actors) {
            ActorPoint.deploy(ap);//essentially World.addObject(ap)
        }
        if (sx!=0) {//setting the player's location based on where they should be in the new room
            if (Player.player.rY!=-1) Player.player.rY=-1;//ignore this, this is for a seperate animation that wouldn't affect this
            Player.player.setLocation(sx,sy);
            sx=0;
            sy=0;
        }
        System.out.println("//Deployment finished.");
    }
(You can ignore all the printLns, they're just there for testing purposes. If my knowledge of Java is correct, if I try to add everything to the world twice, using the exact same arraylist of actors, it should recognize that they're already in the world, and nothing should happen. But it... doesn't.
danpost danpost

2 hours ago

#
Sorry about my first post, I seemed to have looked at the wrong line there. Instead of passing the Room object to the constructor of the LoadingZone objects, try assigning an int value to represent each room and pass it. Then you can use a switch statement to "get" the room depending on the value of the int:
Room room = null;
switch(roomNumber) {
    case 1:  room = Room.roboticsRoom; break;
    case 2:  room = Room.roboticsBackroom; break;
    // etc.
}
Bennylit Bennylit

2 hours ago

#
Alright, I added this to the code:
public LoadingZone(int w, int h, int ro, int r, int rx, int ry) {
        this(w,h,ro,null,rx,ry);
        if (r==0) {
            loaded=Room.roboticsRoom;
        }
        if (r==1) {
            loaded=Room.roboticsBackroom;
        }
        if (r==2) {
            loaded=Room.roboticsHall;
        }
        if (r==3) {
            loaded=Room.soudRoom;
        }
    }
    
    public LoadingZone(int w, int h,int ro, Room r,int rx, int ry) {
        GreenfootImage g = new GreenfootImage(w,h);
        if (Player.DEBUG_MODE) {
            g.setColor(new Color(0,255,255,100));
            g.fill();
        }
        setImage(g);
        loaded=r;
        setRotation(ro);
        y=ry;
        x=rx;
    }
and replaced all instances of creation of a loadingzone with an int... and it still doesn't change anything.
Bennylit Bennylit

1 hour ago

#
I tried something else, it all it did was make me very mad. I added print to see if rooms are ever loaded as null.
public LoadingZone(int w, int h, int ro, int r, int rx, int ry) {
        this(w,h,ro,null,rx,ry);
        if (r==0) {
            loaded=Room.roboticsRoom;
        }
        if (r==1) {
            loaded=Room.roboticsBackroom;
        }
        if (r==2) {
            loaded=Room.roboticsHall;
        }
        if (r==3) {
            loaded=Room.soudRoom;
        }
        System.out.println("Room=null: "+(loaded==null));
        System.out.println("Int r: "+r);
    }
And two of the rooms, it printed out:
Room=null: true
Int r: 2
Room=null: true
Int r: 1
So now I'm mad because that means it had the int, and just... didn't assign the room. So now I'm upset.
danpost danpost

1 hour ago

#
Alright, how about the collision codes where the player goes from one room to the other -- please show that code, plus any other codes not already shown run by that segment of code.
Bennylit Bennylit

37 minutes ago

#
Yes, of course. The code occurs in the act() cycle of the LoadingZone's code.
public void act() {
        if (isTouching(CollisionPoint.class)) {
            Player.sx=x;
            Player.sy=y;
            Player.deployRoom(loaded);
        }
    }
It checks for CollisionPoints, which are the players points of collision, so it essentially checks if it is touching a player. If so, it assigns the player's setX and setY to whatever location it needs send the player to, then gives the player its room object (loaded) to deploy.
You need to login to post a reply.