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

2016/7/30

Greenfoot Freezes Altogether

1
2
elektrikpulse61 elektrikpulse61

2016/7/30

#
First, to clear up any confusion. My scenario does not crash. There is no error log printed out to the console window. Greenfoot itself crashes (sort of). I have a scenario, and in said scenario, there are times when Greenfoot will simply stop working. All of the act() methods stop running, and everything is ground to a halt. Here's the real kicker: recompiling doesn't resolve the issue, and neither does closing and reopening the scenario. I have to physically quit Greenfoot and reopen it (with the whole loading screen and whatnot). I believe I have an idea of what the problem is, but I don't know for sure, and I haven't been able to find anyone else with this problem on the forums. If need be, I can provide source code, and I can also post my scenario for download if it gets to that point. Thanks in advance! Edit: a quick test shows that Java does indeed still run. It seems to be an infinite loop of some sort that is clogging up the program.
danpost danpost

2016/7/30

#
elektrikpulse61 wrote...
I can provide source code... It seems to be an infinite loop of some sort that is clogging up the program.
Posting the code to your initial world class would be a start. Btw, there are ways to avoid closing out totally and restarting greenfoot. Commenting out the code in your initial world class (except for the constructor 'super' call') and using the debugger to 'Terminate' the project will usually do the trick.
elektrikpulse61 elektrikpulse61

2016/7/30

#
I'll put in two bits of code. I'll put in the initial world class as you requested, and I'll also include the class that has the bit that I suspect is clogging the software. Here's the initial world class:
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
public class Levels extends World
{
    private GreenfootSound music = new GreenfootSound("Battle Theme 1.wav");
     
    private boolean[] idTaken = new boolean[500];
     
    private List<CharTop> chars;
    private List<CharBody> chars_;
    private List<OrangeTop> oranges;
    private List<GrayTop> grays;
    private List<CyanTop> cyans;
     
    private List<Wall> walls;
     
    private List<Projectile> projs;
     
    private List<Flag> flags;
     
    private int level = 1;
     
    public Levels()
    {
        super(833, 641, 1);
         
        if (!music.isPlaying())
        {
            music.setVolume(50);
            music.playLoop();
        }
         
        addEdgeWalls();
        loadLevel1();
    }
     
    public void stopped()
    {
        music.pause();
    }
     
    public void started()
    {
        music.playLoop();
    }
     
    public void stopMusic()
    {
        music.stop();
    }
     
    public void startMusic()
    {
        music.playLoop();
    }
     
    private void addEdgeWalls()
    {
        for (int x = 0; x < getWidth(); x += 64)
            for (int y = 0; y < getHeight(); y += 64)
                if (x == 0)
                    addObject(new EdgeWall(x, y, (byte)1), x, y);
                else if (y == 0)
                    addObject(new EdgeWall(x, y, (byte)2), x, y);
                else if (x >= getWidth() - 32)
                    addObject(new EdgeWall(x, y, (byte)3), x, y);
                else if (y >= getHeight() - 32)
                    addObject(new EdgeWall(x, y, (byte)0), x, y);
    }
     
    protected int getNextId()
    {
        for (int i = 0; i < idTaken.length; i++)
        {
            if (!idTaken[i])
            {
                idTaken[i] = true;
                return i;
            }
        }
                 
        return -1;
    }
     
    public void act()
    {
        chars = getObjects(CharTop.class);
        chars_ = getObjects(CharBody.class);
        oranges = getObjects(OrangeTop.class);
        grays = getObjects(GrayTop.class);
        cyans = getObjects(CyanTop.class);
         
        walls = getObjects(Wall.class);
         
        projs = getObjects(Projectile.class);
         
        flags = getObjects(Flag.class);
         
        if (chars.size() == 0)
        {
            stopMusic();
             
            Menu menu = new Menu();
             
            Greenfoot.delay(200);
             
            menu.startMusic();
            Greenfoot.setWorld(menu);
        }
         
        switch(level)
        {
            case 1: if (oranges.size() == 0) {Greenfoot.delay(60); loadLevel2(); level = 2;} break;
            case 2: if (grays.size() == 0) {Greenfoot.delay(60); loadLevel3(); level = 3;} break;
            case 3: if (oranges.size() == 0 && grays.size() == 0) {Greenfoot.delay(60); loadLevel4(); level = 4;} break;
        }
    }
     
    private void loadLevel1()
    {
        CharBody cb = new CharBody(150, getHeight() / 2, 0, getNextId());
        addObject(cb, 0, 0);
         
        OrangeBody ob = new OrangeBody(getWidth() - 150, getHeight() / 2, 180, getNextId());
        addObject(ob, 0, 0);
         
        for (int i = -2; i < 3; i++)
        {
            if (i != 0)
                addObject(new Wall(getWidth() / 2, getHeight() / 2 + (64 * i), false),
                                   getWidth() / 2, getHeight() / 2 + (64 * i));
            else
                addObject(new Wall(getWidth() / 2, getHeight() / 2 + (64 * i), true),
                                   getWidth() / 2, getHeight() / 2 + (64 * i));
        }
    }
     
    private void prepareLevel()
    {
        removeObjects(walls);
        removeObjects(projs);
        removeObjects(chars_);
        removeObjects(flags);
         
        addEdgeWalls();
    }
     
    private void loadLevel2()
    {
        prepareLevel();
         
        CharBody cb = new CharBody(125, 2 * (getHeight() / 3), 0, getNextId());
        addObject(cb, 0, 0);
         
        GrayBody gb = new GrayBody(getWidth() - 125, getHeight() / 3, 180, getNextId());
        addObject(gb, 0, 0);
         
        for (int i = -3; i < 4; i++)
        {
            addObject(new Wall(getWidth() / 2 + (64 * i), getHeight() / 3, false),
                               getWidth() / 2 + (64 * i), getHeight() / 3);
             
            addObject(new Wall(getWidth() / 2 + (64 * i), 2 * (getHeight() / 3), false),
                               getWidth() / 2 + (64 * i), 2 * (getHeight() / 3));
        }
    }
     
    private void loadLevel3()
    {
        prepareLevel();
         
        CharBody cb = new CharBody(150, 3 * (getHeight() / 4), 0, getNextId());
        addObject(cb, 0, 0);
         
        GrayBody gb1 = new GrayBody(getWidth() - 150, getHeight() - 100, 270, getNextId());
        addObject(gb1, 0, 0);
         
        GrayBody gb2 = new GrayBody(150, 100, 90, getNextId());
        addObject(gb2, 0, 0);
         
        OrangeBody ob = new OrangeBody(3 * (getWidth() / 4), getHeight() / 2, 180, getNextId());
        addObject(ob, 0, 0);
         
        for (int i = -2; i < 3; i++)
        {
            addObject(new Wall(getWidth() / 2, getHeight() / 2  + (64 * i), false),
                               getWidth() / 2, getHeight() / 2  + (64 * i));
             
            if (i == 2)
                for (int j = 1; j < 5; j++)
                {
                    if (j / 2.0 == j / 2)
                        addObject(new Wall(getWidth() / 2 + (64 * j), getHeight() / 2  + (64 * i), false),
                                           getWidth() / 2 + (64 * j), getHeight() / 2  + (64 * i));
                    else
                        addObject(new Wall(getWidth() / 2 + (64 * j), getHeight() / 2  + (64 * i), true),
                                           getWidth() / 2 + (64 * j), getHeight() / 2  + (64 * i));
                }
            if (i == -2)
                for (int j = 1; j < 5; j++)
                {
                    addObject(new Wall(getWidth() / 2 - (64 * j), getHeight() / 2  + (64 * i), false),
                                       getWidth() / 2 - (64 * j), getHeight() / 2  + (64 * i));
                }
        }
    }
     
    private void loadLevel4()
    {
        prepareLevel();
         
        CharBody cb = new CharBody(125, 3 * (getHeight() / 4), 0, getNextId());
        addObject(cb, 0, 0);
         
        OrangeBody ob = new OrangeBody(125, getHeight() / 4, 0, getNextId());
        addObject(ob, 0, 0);
         
        GrayBody gb = new GrayBody(getWidth() - 125, 3 * (getHeight() / 4), 180, getNextId());
        addObject(gb, 0, 0);
         
        CyanBody cyb = new CyanBody(getWidth() - 125, getHeight() / 4, 90, getNextId());
        addObject(cyb, 0, 0);
         
        for (int i = 0; i < 3; i++)
        {
            addObject(new Wall(192 + (64 * i), 224, false), 192 + (64 * i), 224);
             
            addObject(new Wall(getWidth() - (192 + (64 * i)), 224, false),
                               getWidth() - (192 + (64 * i)), 224);
             
            addObject(new Wall(192 + (64 * i), getHeight() - 224, false),
                               192 + (64 * i), getHeight() - 224);
             
            addObject(new Wall(getWidth() - (192 + (64 * i)), getHeight() - 224, false),
                               getWidth() - (192 + (64 * i)), getHeight() - 224);
                                
            if (i == 2)
                for (int j = 1; j < 2; j++)
                {
                    addObject(new Wall(192 + (64 * i), 224 - (64 * j), false),
                                       192 + (64 * i), 224 - (64 * j));
             
                    addObject(new Wall(getWidth() - (192 + (64 * i)), 224 - (64 * j), false),
                                       getWidth() - (192 + (64 * i)), 224 - (64 * j));
                     
                    addObject(new Wall(192 + (64 * i), getHeight() - 224 + (64 * j), false),
                                       192 + (64 * i), getHeight() - 224 + (64 * j));
                     
                    addObject(new Wall(getWidth() - (192 + (64 * i)), getHeight() - 224 + (64 * j), false),
                                       getWidth() - (192 + (64 * i)), getHeight() - 224 + (64 * j));
                }
        }
    }
}
(first time posting code. I hope I followed all of the guidelines :3) And here's the class containing the part I find suspicious:
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
public class Trajectory extends Projectile
{
    private Top parent;
     
    public Trajectory(int x, int y, int rot, int id, int ric, Top parent)
    {
        super (x, y, rot, id);
         
        this.parent = parent;
         
        speed = 3;
        this.ric = ric;
    }
     
    @Override
    public void act()
    {
        while (getWorld() != null)
            super.act();
    }
     
    @Override
    protected void updateCollisions()
    {
        Tank tank = (Tank)getOneIntersectingObject(Tank.class);
        CharBody charBody = (CharBody)getOneIntersectingObject(CharBody.class);
        if (tank != null && tank != charBody)
        {
            parent.setOnTarget(-1);
            destroy();
            return;
        }
         
        if (tank != null && tank == charBody)
        {
            parent.setOnTarget(1);
            destroy();
            return;
        }
         
        Wall wall = (Wall)getOneIntersectingObject(Wall.class);
        if (wall != null)
            if (ric <= 0)
            {
                destroy();
                return;
            }
         
        parent.setOnTarget(0);
    }
}
If need be, I can also put in the superclass of the Trajectory object, but to save space, I won't include it unless requested.
elektrikpulse61 elektrikpulse61

2016/7/30

#
I'm kind of just throwing ideas here, but would the fact that an act() method is being called from within a while loop (and thus faster than it was intended to be called) have any sort of contribution to the problem? I mean, if so, I can just have it call a different method that virtually does the same thing as super.act(), but without actually calling another act() method.
danpost danpost

2016/7/30

#
elektrikpulse61 wrote...
I'm kind of just throwing ideas here, but would the fact that an act() method is being called from within a while loop (and thus faster than it was intended to be called) have any sort of contribution to the problem? I mean, if so, I can just have it call a different method that virtually does the same thing as super.act(), but without actually calling another act() method.
Indeed! Calling 'super.act()' from within a while loop will have the one actor acting (without any other actions taking place) until that actor (the Trajectory object) is removed from the world. If it is never removed from the world, your project will appear to freeze. What is your reasoning for attempting to place the 'super.act' call in a while loop? What were you attempting to accomplish with that?
elektrikpulse61 elektrikpulse61

2016/7/30

#
Well, in my scenario, I have an abstract Projectile class that handles all flying objects (including their precise movement, ricochets, etc.), and I made this Trajectory object a subclass of that Projectile superclass. The reason for this is because I want the enemies to know where their projectiles are going to end up going BEFORE they shoot, otherwise they end up being stupid and often shooting themselves or their fellow enemies. My solution to this would be to have a no-texture object "scout out" the trajectory that a hypothetical projectile would follow if fired along that path (if that makes any sense). This object (the Trajectory object) would have to move basically instantly in order to quickly feed the enemies the trajectory information they need. So I figured that I'd have it follow its whole path in one act-cycle as opposed to over several act-cycles. In order to save myself having to copy and paste the movement and ricochet methods from the superclass to the subclass, I figured I'd let the super.act() method handle all of that. Apparently this causes major problems when the conditions you described roll out. Sorry for long post :3. I wanted to thoroughly answer your question.
danpost danpost

2016/7/30

#
elektrikpulse61 wrote...
In order to save myself having to copy and paste the movement and ricochet methods from the superclass to the subclass, I figured I'd let the super.act() method handle all of that.
Two things you should know: (1) methods in a superclass, unless declared 'private', are automatically available for use in a subclass through inheritance; (2) the 'act' method of a superclass (which is also inherited) will run for a subclass if that subclass does not override it; In other words, by removing lines 15 through 20 in the Trajectory class, all objects of that class will be controlled by the act method in the Projectile class (its superclass).
danpost danpost

2016/7/30

#
There are two things that are puzzling me. The first is the use of all the List object for the different type actors and the boolean array seem to be quite extravagant. There is no need for the List fields as fields are for thing that need to be retained between act cycles where you are continuously re-assigning the contents of the lists immediately upon entry into the act method (their previous references are ignored entirely). With the array, a simple int field can be used to assign an 'id' and its value bumped upon assignment; thereby assigning a unique value to each of whatever is being identified. Finally, I am not sure about the 'prepareLevel' method. If it is removing ALL actors from the world (which is difficult to determine with the currently given code), then you could just use:
1
removeObjects(getObjects(null));
elektrikpulse61 elektrikpulse61

2016/7/30

#
I see. I have some responses to your statements / questions. I'll try to go in order. 1. / 2. The reason for me putting super.act() within a while loop that is within the act() method of the Trajectory class is because I want the Trajectory to basically spawn, do its thing in one act cycle, and then disappear. If I remove lines 15-20 altogether, and let the Projectile act() method run its course, the Trajectory objects will simply move at the speed of regular projectiles, which defeats the whole purpose of me adding them. As for the lists, is there perhaps a better way to keep track of how many actors there are of each type? If so, I'd rather use that, as I understand that what I'm doing is horribly inefficient (basically re-initializing the entire set of Lists every act cycle). As for your alternative method to assigning unique IDs to each actor, I don't know why I never thought of your method myself. I suppose it was because, at the time when I first made the boolean list, I was thinking I could improve and develop the system into a more grand and overall-more-useful system. It seems that such a system is not necessary. Finally, perhaps using removeObjects(getObjects(null)) is a far more efficient way of removing (just about) everything. I will change my prepareLevel to include that instead of removing each List of objects individually. -end comments- I think I have an idea as to how I can condense the entire lifespan so-to-speak of the Trajectory objects into one act cycle: instead of calling another act cycle, I just call a series of private methods that will virtually do the same thing without having to go through the Greenfoot act() method (if that makes any sense whatsoever - probably doesn't). Do you have any thoughts / improvements for this idea?
danpost danpost

2016/7/30

#
Without the List fields, you can just replace the field names with the method calls used to assign the objects to the lists. For example, when removing line 85 (and line 7), you would then change line 97 to this:
1
if (getObjects(CharTop.class).size() == 0)
or
1
if (getObjects(CharTop.class).isEmpty())
which checks for the same thing, but is more efficient. The thing is that a List of all actors in a world is retained by that World instance and the 'getObjects' method can create a sublist containing one type of Actor or all types of actors. This suggests that what you were doing was creating a redundancy by adding all those List fields in your subclass of World. As far as the trajectory of a projectile, you can only form a "perfect world" case as to the path. How would you account for ricochets and such without having other actors moving at the same time? Maybe you can explain the aiming process that initially determines where a projectile is to go.
elektrikpulse61 elektrikpulse61

2016/7/30

#
I have read your reply, and I now understand your first point. I will be sure to update that as soon as I can. Thank you for the clarification on that, by the way. For your second point (the question), instead of typing a ridiculously long explanation of it, I will demonstrate with a brief video (not in Greenfoot) of my idea. I'm more of a visual person, so I can probably explain it better and with fewer ambiguities and loopholes this way. I will add (or edit in) a second post to an unlisted YouTube video that will contain that video.
elektrikpulse61 elektrikpulse61

2016/7/30

#
Here's the unlisted video. https://youtu.be/ZnlKHWdvOQA
danpost danpost

2016/7/31

#
You probably do not want any projectile to be continuously ricocheting off of the edges of the world and want to set a limit as to how far they will travel before being removed (if missing the target tank). You could set this limit in number of act cycles in the world or you could limit the number of edges bouncing off of to one or two, for example. By setting a limit, you can then use the 'super.act' in a while loop provided the projectile is positively removed from the world at some point. I think, however, that if the projectile is going to get any information from the Tragectory object, something more needs to be done after the while loop. One issue might then be that when not in the world, you cannot get the last location in that world. So, unless you have a way to determine why the Trajectory object was removed from the world (which is what I believe the parent object would need in order to determine whether to fire a projectile or not), I do not see how this setup would work.
elektrikpulse61 elektrikpulse61

2016/7/31

#
I forgot to explain the first point of your reply. In the constructor of the Trajectory object, there is a parameter that will limit the number of ricochets the object will have. Also, using System.currentTimeMillis(), I also limited the lifespan on a Trajectory object to 250 milliseconds, which is way more than enough time for one to complete its journey, considering the relatively small size of the Greenfoot scenario world. (This was actually how I fixed the problem). As for your second point, since the movement is all done within one act cycle, the parent tank will not have moved any since it "fired" its last Trajectory object. Therefore, if the parent is returned a character as the "bumped into" object (the parent is also a parameter, allowing the Trajectory object to send information to the original tank), it will just fire wherever it is currently.
danpost danpost

2016/7/31

#
elektrikpulse61 wrote...
As for your second point, since the movement is all done within one act cycle, the parent tank will not have moved any since it "fired" its last Trajectory object. Therefore, if the parent is returned a character as the "bumped into" object (the parent is also a parameter, allowing the Trajectory object to send information to the original tank), it will just fire wherever it is currently.
It is not the location of firing that is the issue -- it is the returning of the "bumped into" object that is of concern. There is nothing in the Trajectory class to retain that information and no call to the parent object to return it. At any rate, the way you are going about it seems a bit clumsy. I would have the parent create and add a Trajectory object into the world (at its location; giving the proper rotation) and then call a non-act method in the Trajectory class to perform the nasties and return the "bumped into" object. Something like this in the class of the enemy tank act method:
1
2
3
4
5
6
7
8
9
10
11
if (< time to target tank c >) // whatever conditions you require for this
{
    Trajectory trajectory = new Trajectory(< whatever parameters are needed >);
    getWorld().addObject(trajectory , getX(), getY()); // adding any adjustment in location
    Actor bumped = trajectory.isAimedAt();
    if (bumped != null && bumped instanceof TankC) // whatever the class is named
    {
        Projectile projectile = new Projectile(< same parameters as Trajectory object >);
        getWorld().addObject(projectile, getX(), getY()); // adding same adjustments in location
    }
}
Then in the Trajectory class, you would (instead of an act method) have something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Actor isAimedAt()
{
    Actor bumped = null;
    while (true)
    {
        if (timeExpired()) break;
        move();
        if (atWorldEdge())
        {
            ricochet();
            if (ricochetCount > ricochetLimit) break;
        }
        bumped = getOneIntersectingObject(null);
        if (bumped != null) break;
    }
    getWorld().removeObject(this);
    return bumped;
}
There are more replies on the next page.
1
2