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

2023/3/26

i want actors in my simulation to pass actors in front of them

KCee KCee

2023/3/26

#
I have a bunch of actors under a superclass called "Vehicle". All of them move from the left side of the screen to the right and then disappear when they hit the edge of the world. There are 6 lanes in the road that these actors travel on. What I want to do is when there are 2 actors in the same lane, and one actor is right behind another actor, I want it to check if the lane to its right is empty, and if so, move into that lane. How would i check if the lane is empty? and how would i make it change lanes? World code:
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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
 
/**
 * <h1>The new and vastly improved 2022 Vehicle Simulation Assignment.</h1>
 * <p> This is the first redo of the 8 year old project. Lanes are now drawn dynamically, allowing for
 *     much greater customization. Pedestrians can now move in two directions. The graphics are better
 *     and the interactions smoother.</p>
 * <p> The Pedestrians are not as dumb as before (they don't want straight into Vehicles) and the Vehicles
 *     do a somewhat better job detecting Pedestrians.</p>
 *
 * Version Notes - Feb 2023
 * --> Includes grid <--> lane conversion method
 * --> Now starts with 1-way, 5 lane setup (easier)
 */
 
/**
 * Music Used
 * No Sleep for Busy Minds – sombre, melancholic piece, piano and small guitar elements BIIANSU
 *
 */
public class VehicleWorld extends World
{
    private GreenfootImage background;
 
     
    // Color Constants
    public static Color GREY_BORDER = new Color (108, 108, 108);
    public static Color GREY_STREET = new Color (88, 88, 88);
    public static Color YELLOW_LINE = new Color (255, 216, 0);
    public static Color GREEN_STREET = new Color (114, 135, 0);
    public static Color BROWN_LINE = new Color (101, 67, 33);
    public static Color BROWN_BORDER = new Color (101, 67, 33);
     
     
 
    // Instance variables / Objects
    private boolean twoWayTraffic, splitAtCenter;
    private int laneHeight, laneCount, spaceBetweenLanes;
    private int[] lanePositionsY;
    private VehicleSpawner[] laneSpawners;
    private GreenfootSound worldSound;
    //Static Variables
    private static boolean bloodMooning;
 
    /**
     * Constructor for objects of class MyWorld.
     *
     */
    public VehicleWorld()
    {   
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(800, 600, 1, false);
 
        setPaintOrder (Villager.class, EvilWitch.class, GoodWitch.class, Bus.class, Acidmon.class, BloodDemon.class, Prowler.class);
         
        bloodMooning = false;
        worldSound = new GreenfootSound ("backgroundsound.wav");
        // set up background
        GreenfootImage background = new GreenfootImage("background.png");
        background.scale(getWidth(), getHeight());
         
        setBackground(background);
 
        // Set critical variables
        laneCount = 6;
        laneHeight = 48;
        spaceBetweenLanes = 6;
        splitAtCenter = false;
        twoWayTraffic = false;
 
        setActOrder(Pedestrian.class);
        // Init lane spawner objects
        laneSpawners = new VehicleSpawner[laneCount];
 
        // Prepare lanes method - draws the lanes
        lanePositionsY = prepareLanes (this, background, laneSpawners, 222, laneHeight, laneCount, spaceBetweenLanes, twoWayTraffic, splitAtCenter);
 
    }
 
    public void act () {
        spawn();
    }
     
    public void started()
    {
        worldSound.setVolume(80);
        worldSound.play();
    }
     
    private void spawn () {
        // Chance to spawn a vehicle
        if (Greenfoot.getRandomNumber (60) == 0){
            int lane = Greenfoot.getRandomNumber(laneCount);
            if (!laneSpawners[lane].isTouchingVehicle()){
                int vehicleType = Greenfoot.getRandomNumber(4);
                if (vehicleType == 0){
                    addObject(new Acidmon(laneSpawners[lane]), 0, 0);
                } else if (vehicleType == 1){
                    addObject(new Bus(laneSpawners[lane]), 0, 0);
                //} else if (vehicleType == 2){
                    //addObject(new BloodDemon(laneSpawners[lane]), 0, 0);
                } else if (vehicleType == 2){
                    addObject(new Prowler(laneSpawners[lane]), 0, 0);
                } else if (vehicleType == 3){
                    addObject(new RootedCorpse(laneSpawners[lane]), 0, 0);
                }
            }
        }
 
        // Chance to spawn a Pedestrian
        if (Greenfoot.getRandomNumber (80) == 0){
            int xSpawnLocation = Greenfoot.getRandomNumber (600) + 100; // random between 99 and 699, so not near edges
            boolean spawnAtTop = true;//Greenfoot.getRandomNumber(2) == 0 ? true : false;
            if (spawnAtTop){
                addObject (new Villager (1), xSpawnLocation, 50);
            } else {
                addObject (new Villager (-1), xSpawnLocation, 550);
            }
        }
        else if (Greenfoot.getRandomNumber (160) == 0){
            int xSpawnLocation = Greenfoot.getRandomNumber (600) + 100; // random between 99 and 699, so not near edges
            boolean spawnAtTop = true;//Greenfoot.getRandomNumber(2) == 0 ? true : false;
            if (spawnAtTop){
                addObject (new EvilWitch (1), xSpawnLocation, 50);
            } else {
                addObject (new EvilWitch (-1), xSpawnLocation, 550);
            }
        }
        else if (Greenfoot.getRandomNumber (160) == 0){
            int xSpawnLocation = Greenfoot.getRandomNumber (600) + 100; // random between 99 and 699, so not near edges
            boolean spawnAtTop = true;//Greenfoot.getRandomNumber(2) == 0 ? true : false;
            if (spawnAtTop){
                addObject (new GoodWitch (1), xSpawnLocation, 50);
            } else {
                addObject (new GoodWitch (-1), xSpawnLocation, 550);
            }
        }
         
        if (!bloodMooning && Greenfoot.getRandomNumber(200) == 0){
            int lane = Greenfoot.getRandomNumber(laneCount);
            addObject (new BloodMoon(240), 400, 300);
            addObject(new BloodDemon(laneSpawners[lane]), 0, 0);
            bloodMooning = true;
        }
        if (bloodMooning && getObjects(BloodMoon.class).size() == 0){
            bloodMooning = false;
        }
    }
     
    public static boolean isBloodMooning()
    {
        return bloodMooning;
    }
 
    /**
     *  Given a lane number (zero-indexed), return the y position
     *  in the centre of the lane. (doesn't factor offset, so
     *  watch your offset, i.e. with Bus).
     
     *  @param lane the lane number (zero-indexed)
     *  @return int the y position of the lane's center, or -1 if invalid
     */
    public int getLaneY (int lane){
        if (lane < lanePositionsY.length){
            return lanePositionsY[lane];
        }
        return -1;
    }
     
    /**
     * Given a y-position, return the lane number (zero-indexed).
     * Note that the y-position must be valid, and you should
     * include the offset in your calculations before calling this method.
     * For example, if a Bus is in a lane at y=100, but is offset by -20,
     * it is actually in the lane located at y=80, so you should send
     * 80 to this method, not 100.
     *
     * @param y - the y position of the lane the Vehicle is in
     * @return int the lane number, zero-indexed
     *
     */
    public int getLane (int y){
        for (int i = 0; i < lanePositionsY.length; i++){
            if (y == lanePositionsY[i]){
                return i;
            }
        }
        return -1;
    }
     
    public static int[] prepareLanes (World world, GreenfootImage target, VehicleSpawner[] spawners, int startY, int heightPerLane, int lanes, int spacing, boolean twoWay, boolean centreSplit, int centreSpacing)
    {
        // Declare an array to store the y values as I calculate them
        int[] lanePositions = new int[lanes];
        // Pre-calculate half of the lane height, as this will frequently be used for drawing.
        // To help make it clear, the heightOffset is the distance from the centre of the lane (it's y position)
        // to the outer edge of the lane.
        int heightOffset = heightPerLane / 2;
 
        // draw top border
        target.setColor (BROWN_BORDER);
        target.fillRect (0, startY, target.getWidth(), spacing);
 
        // Main Loop to Calculate Positions and draw lanes
        for (int i = 0; i < lanes; i++){
            // calculate the position for the lane
            lanePositions[i] = startY + spacing + (i * (heightPerLane+spacing)) + heightOffset ;
             
            // draw lane
            target.setColor(GREEN_STREET);
            // the lane body
            target.fillRect (0, lanePositions[i] - heightOffset, target.getWidth(), heightPerLane);
            // the lane spacing - where the white or yellow lines will get drawn
            target.fillRect(0, lanePositions[i] + heightOffset, target.getWidth(), spacing);
 
            // Place spawners and draw lines depending on whether its 2 way and centre split
            if (twoWay && centreSplit){
                // first half of the lanes go rightward (no option for left-hand drive, sorry UK students .. ?)
                if ( i < lanes / 2){
                    spawners[i] = new VehicleSpawner(false, heightPerLane);
                    world.addObject(spawners[i], target.getWidth(), lanePositions[i]);
                } else { // second half of the lanes go leftward
                    spawners[i] = new VehicleSpawner(true, heightPerLane);
                    world.addObject(spawners[i], 0, lanePositions[i]);
                }
 
                // draw yellow lines if middle
                if (i == lanes / 2){
                    target.setColor(BROWN_LINE);
                    target.fillRect(0, lanePositions[i] - heightOffset - spacing, target.getWidth(), spacing);
 
                } /*else if (i > 0){ // draw white lines if not first lane
                    for (int j = 0; j < target.getWidth(); j += 120){
                        target.setColor (Color.WHITE);
                        target.fillRect (j, lanePositions[i] - heightOffset - spacing, 60, spacing);
                    }
                }*/
 
            } else if (twoWay){ // not center split
                if ( i % 2 == 0){
                    spawners[i] = new VehicleSpawner(false, heightPerLane);
                    world.addObject(spawners[i], target.getWidth(), lanePositions[i]);
                } else {
                    spawners[i] = new VehicleSpawner(true, heightPerLane);
                    world.addObject(spawners[i], 0, lanePositions[i]);
                }
 
                // draw Grey Border if between two "Streets"
                if (i > 0){ // but not in first position
                    if (i % 2 == 0){
                        target.setColor(BROWN_BORDER);
                        target.fillRect(0, lanePositions[i] - heightOffset - spacing, target.getWidth(), spacing);
 
                    } else { // draw dotted lines
                        for (int j = 0; j < target.getWidth(); j += 120){
                            target.setColor (BROWN_LINE);
                            target.fillRect (j, lanePositions[i] - heightOffset - spacing, 60, spacing);
                        }
                    }
                }
            } else { // One way traffic
                spawners[i] = new VehicleSpawner(true, heightPerLane);
                world.addObject(spawners[i], 0, lanePositions[i]);
                /*if (i > 0){
                    for (int j = 0; j < target.getWidth(); j += 120){
                        target.setColor (Color.WHITE);
                        target.fillRect (j, lanePositions[i] - heightOffset - spacing, 60, spacing);
                    }
                }*/
            }
        }
        // draws bottom border
        target.setColor (BROWN_BORDER);
        target.fillRect (0, lanePositions[lanes-1] + heightOffset, target.getWidth(), spacing);
 
        return lanePositions;
    }
 
    /**
     * <p>The prepareLanes method is a static (standalone) method that takes a list of parameters about the desired roadway and then builds it.</p>
     *
     * <p><b>Note:</b> So far, Centre-split is the only option, regardless of what values you send for that parameters.</p>
     *
     * <p>This method does three things:</p>
     * <ul>
     *  <li> Determines the Y coordinate for each lane (each lane is centered vertically around the position)</li>
     *  <li> Draws lanes onto the GreenfootImage target that is passed in at the specified / calculated positions.
     *       (Nothing is returned, it just manipulates the object which affects the original).</li>
     *  <li> Places the VehicleSpawners (passed in via the array parameter spawners) into the World (also passed in via parameters).</li>
     * </ul>
     *
     * <p> After this method is run, there is a visual road as well as the objects needed to spawn Vehicles. Examine the table below for an
     * in-depth description of what the roadway will look like and what each parameter/component represents.</p>
     *
     * <pre>
     *                  <=== Start Y
     *  ||||||||||||||  <=== Top Border
     *  /------------\
     *  |            | 
     *  |      Y[0]  |  <=== Lane Position (Y) is the middle of the lane
     *  |            |
     *  \------------/
     *  [##] [##] [##| <== spacing ( where the lane lines or borders are )
     *  /------------\
     *  |            | 
     *  |      Y[1]  |
     *  |            |
     *  \------------/
     *  ||||||||||||||  <== Bottom Border
     * </pre>
     *
     * @param world     The World that the VehicleSpawners will be added to
     * @param target    The GreenfootImage that the lanes will be drawn on, usually but not necessarily the background of the World.
     * @param spawners  An array of VehicleSpawner to be added to the World
     * @param startY    The top Y position where lanes (drawing) should start
     * @param heightPerLane The height of the desired lanes
     * @param lanes     The total number of lanes desired
     * @param spacing   The distance, in pixels, between each lane
     * @param twoWay    Should traffic flow both ways? Leave false for a one-way street (Not Yet Implemented)
     * @param centreSplit   Should the whole road be split in the middle? Or lots of parallel two-way streets? Must also be two-way street (twoWay == true) or else NO EFFECT
     *
     */
    public static int[] prepareLanes (World world, GreenfootImage target, VehicleSpawner[] spawners, int startY, int heightPerLane, int lanes, int spacing, boolean twoWay, boolean centreSplit){
        return prepareLanes (world, target, spawners, startY, heightPerLane, lanes, spacing, twoWay, centreSplit, spacing);
    }
 
}
Vehicle Superclass code:
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
 
/**
 * This is the superclass for Vehicles.
 *
 */
public abstract class Vehicle extends SuperSmoothMover
{
    protected double maxSpeed;
    protected double speed;
    protected int direction; // 1 = right, -1 = left
    protected boolean moving;
    protected int yOffset;
    protected VehicleSpawner origin;
     
    protected abstract boolean checkHitPedestrian ();
 
    public Vehicle (VehicleSpawner origin) {
        this.origin = origin;
        moving = true;
         
        if (origin.facesRightward()){
            direction = 1;
             
        } else {
            direction = -1;
            getImage().mirrorHorizontally();
        }
    }
     
    public void addedToWorld (World w){
        setLocation (origin.getX() - (direction * 100), origin.getY() - yOffset);
    }
 
    /**
     * A method used by all Vehicles to check if they are at the edge.
     *
     * Note that this World is set to unbounded (The World's super class is (int, int, int, FALSE) which means
     * that objects should not be stopped from leaving the World. However, this introduces a challenge as there
     * is the potential for objects to disappear off-screen but still be fully acting and thus wasting resources
     * and affecting the simulation even though they are not visible.
     */
    protected boolean checkEdge() {
        if (direction == 1)
        { // if moving right, check 200 pixels to the right (above max X)
            if (getX() > getWorld().getWidth() + 200){
                return true;
            }
        }
        else
        { // if moving left, check 200 pixels to the left (negative values)
            if (getX() < -200){
                return true;
            }
        }
        return false;
    }
 
    /**
     * Method that deals with movement. Speed can be set by individual subclasses in their constructors
     */
    public void drive()
    {
        boolean bloodMooning = VehicleWorld.isBloodMooning();
        // Ahead is a generic vehicle - we don't know what type BUT
        // since every Vehicle "promises" to have a getSpeed() method,
        // we can call that on any vehicle to find out it's speed
        Vehicle ahead = (Vehicle) getOneObjectAtOffset (direction * (int)(speed + getImage().getWidth()/2 + 4), 0, Vehicle.class);
        if (ahead == null)
        {
            speed = maxSpeed;
 
        } else {
            speed = ahead.getSpeed();
        }
         
        if (bloodMooning)
        {
            speed = maxSpeed*2;
            move (speed * direction);
        }
        else
        {
            speed = maxSpeed;
            move (speed * direction);
        }
    }  
 
    /**
     * An accessor that can be used to get this Vehicle's speed. Used, for example, when a vehicle wants to see
     * if a faster vehicle is ahead in the lane.
     */
    public double getSpeed(){
        return speed;
    }
     
    public void speedUp () {
        speed = maxSpeed*3;
    }
}
Also this is the code used to make the vehicle spawners, returns if a vehicle is currently inside the spawn zone, and if so, prevents vehicles from spawning in that lane until the spawn zone is free. Not sure if this will help:
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
 
/**
 * A Spawner object is a place where a Vehicle can spawn. Each spawner is
 * able to check if there is already a Vehicle in the spot to avoid spawning
 * multiple Vehicles on top of each other.
 *
 * @author (your name)
 * @version (a version number or a date)
 */
public class VehicleSpawner extends Actor
{
    public static final Color TRANSPARENT_RED = new Color (255, 0, 0, 128);
     
    public static final int DIST_BETWEEN_CARS = 128;
     
    private GreenfootImage image;
     
    private boolean rightward;
    private boolean visible;
    private int height, width;
     
    public VehicleSpawner (boolean rightward, int laneHeight)
    {
        this.rightward = rightward;
        this.height = (int)(laneHeight * 0.75);
        width = DIST_BETWEEN_CARS;
        // set this to true to see the Spawners - might help with understanding of how this works:
        visible = false;
        image = new GreenfootImage (width, height);
        if(visible){
            image.setColor(TRANSPARENT_RED);
            image.fillRect(0, 0, width-1, height - 1);
        }
        setImage(image);
    }
     
    public boolean facesRightward (){
        return rightward;
    }
     
    /*public boolean isTouchingVehicleOld () {
        return this.isTouching (Lurker.class) || this.isTouching (Bus.class) || this.isTouching (Ambulance.class);
    }*/
     
    public boolean isTouchingVehicle () {
        return this.isTouching(Vehicle.class);
    }
}
danpost danpost

2023/3/26

#
KCee wrote...
I have a bunch of actors under a superclass called "Vehicle". All of them move from the left side of the screen to the right and then disappear when they hit the edge of the world. There are 6 lanes in the road that these actors travel on. What I want to do is when there are 2 actors in the same lane, and one actor is right behind another actor, I want it to check if the lane to its right is empty, and if so, move into that lane. How would i check if the lane is empty? and how would i make it change lanes? << Codes Omitted >>
Easiest way is to just temporarily place the vehicle into the right lane and check for collisions:
1
2
3
4
5
6
setLocation(getX(), getY()+/** lane offset value */);
boolean canLaneChange = ! isTouching(Vehicle.class);
setLocation(getX(), getY()- /** lane offset value */);
if (canLaneChange) {
    /** initiate lane change */
}
KCee KCee

2023/3/27

#
how would i make them change lanes? and would i put the code above in the world class?
danpost danpost

2023/3/27

#
KCee wrote...
how would i make them change lanes?
Best way is probably to add a vertical speed field (ySpeed: values to be (i) 0: not changing lanes; (ii) 1: moving into right lane; and (iii) -1: moving into left lane (if applicable). Then all that needs done to initial lane change is to change the value of this field from 0 to the appropriate direction value. Finally, to terminate lane changing:
1
2
// after vehicle moves
if (ySpeed != 0 && getY()%/** lane offset value */ == /** (y coordinate of any lane %/** lane offset value */) ySpeed = 0;
So if y-coordinate of lanes were at 150, 250, 350 and 450, then the lane offset value is 100 (difference between lanes), and:
1
if (ySpeed != 0 && getY()%100 == 50) ySpeed = 0; // 50 = 150%100 = 250%100 = 350%100 = 450%100
and would i put the code above in the world class?
In the Vehicle class.
KCee KCee

2023/3/28

#
how do i initialize the ySpeed variable? code:
1
2
3
4
5
6
7
8
9
10
private void changeLanes()
    {
        int ySpeed;
        setLocation(getX(), getY()+60);
        boolean canLaneChange = ! isTouching(Vehicle.class);
        setLocation(getX(), getY()- 60);
        if (canLaneChange) {
            if (ySpeed != 0 && getY()%60 == 30) ySpeed = 0;
        }
    }
Spock47 Spock47

2023/3/28

#
KCee wrote...
Thanks that worked! could you also try to answer my question in the other discussion post about the ySpeed variable?
I think it should be an attribute with default initial value 0:
1
2
3
4
private int ySpeed = 0;
 
private void changeLanes() {
    ...
KCee KCee

2023/3/28

#
nothing seems to be happening after adding the switch lanes method... vehicle code
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
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
 
/**
 * This is the superclass for Vehicles.
 *
 */
public abstract class Vehicle extends SuperSmoothMover
{
    protected double maxSpeed;
    protected double speed;
    protected int direction; // 1 = right, -1 = left
    protected boolean moving;
    protected int yOffset;
    protected VehicleSpawner origin;
    private int ySpeed = 0;
     
    protected abstract boolean checkHitPedestrian ();
     
    private void changeLanes()
    {
        setLocation(getX(), getY()+60);
        boolean canLaneChange = ! isTouching(Vehicle.class);
        setLocation(getX(), getY()- 60);
        if (canLaneChange) {
            if (ySpeed != 0 && getY()%60 == 30)
            {
                ySpeed = 0;
            }
        }
    }
     
    public Vehicle (VehicleSpawner origin) {
        this.origin = origin;
        moving = true;
         
        if (origin.facesRightward()){
            direction = 1;
             
        } else {
            direction = -1;
            getImage().mirrorHorizontally();
        }
    }
     
    public void addedToWorld (World w){
        setLocation (origin.getX() - (direction * 100), origin.getY() - yOffset);
    }
 
    /**
     * A method used by all Vehicles to check if they are at the edge.
     *
     * Note that this World is set to unbounded (The World's super class is (int, int, int, FALSE) which means
     * that objects should not be stopped from leaving the World. However, this introduces a challenge as there
     * is the potential for objects to disappear off-screen but still be fully acting and thus wasting resources
     * and affecting the simulation even though they are not visible.
     */
    protected boolean checkEdge() {
        if (direction == 1)
        { // if moving right, check 200 pixels to the right (above max X)
            if (getX() > getWorld().getWidth() + 200){
                return true;
            }
        }
        else
        { // if moving left, check 200 pixels to the left (negative values)
            if (getX() < -200){
                return true;
            }
        }
        return false;
    }
 
    /**
     * Method that deals with movement. Speed can be set by individual subclasses in their constructors
     */
    public void drive()
    {
        boolean bloodMooning = VehicleWorld.isBloodMooning();
        // Ahead is a generic vehicle - we don't know what type BUT
        // since every Vehicle "promises" to have a getSpeed() method,
        // we can call that on any vehicle to find out it's speed
        Vehicle ahead = (Vehicle) getOneObjectAtOffset (direction * (int)(speed + getImage().getWidth()/2 + 4), 0, Vehicle.class);
        if (ahead == null)
        {
            speed = maxSpeed;
 
        } else {
            speed = ahead.getSpeed();
        }
         
        if (bloodMooning)
        {
            speed = maxSpeed*2;
            move (speed * direction);
        }
        else
        {
            speed = maxSpeed;
            move (speed * direction);
        }
    }  
 
    /**
     * An accessor that can be used to get this Vehicle's speed. Used, for example, when a vehicle wants to see
     * if a faster vehicle is ahead in the lane.
     */
    public double getSpeed(){
        return speed;
    }
     
    public void speedUp () {
        speed = maxSpeed*3;
    }
}
KCee KCee

2023/3/28

#
Here is the actual worlds code where it creates the lanes maybe i have the wrong offset value? im not sure
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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
 
/**
 * <h1>The new and vastly improved 2022 Vehicle Simulation Assignment.</h1>
 * <p> This is the first redo of the 8 year old project. Lanes are now drawn dynamically, allowing for
 *     much greater customization. Pedestrians can now move in two directions. The graphics are better
 *     and the interactions smoother.</p>
 * <p> The Pedestrians are not as dumb as before (they don't want straight into Vehicles) and the Vehicles
 *     do a somewhat better job detecting Pedestrians.</p>
 *
 * Version Notes - Feb 2023
 * --> Includes grid <--> lane conversion method
 * --> Now starts with 1-way, 5 lane setup (easier)
  
 * Music Used:
 * No Sleep for Busy Minds – sombre, melancholic piece, piano and small guitar elements (Creator BIIANSU)
 *
 */
 
 
public class VehicleWorld extends World
{
    private GreenfootImage background;
 
     
    // Color Constants
    public static Color GREY_BORDER = new Color (108, 108, 108);
    public static Color GREY_STREET = new Color (88, 88, 88);
    public static Color YELLOW_LINE = new Color (255, 216, 0);
    public static Color GREEN_STREET = new Color (114, 135, 0);
    public static Color BROWN_LINE = new Color (101, 67, 33);
    public static Color BROWN_BORDER = new Color (101, 67, 33);
     
     
 
    // Instance variables / Objects
    private boolean twoWayTraffic, splitAtCenter;
    private int laneHeight, laneCount, spaceBetweenLanes;
    private int[] lanePositionsY;
    private VehicleSpawner[] laneSpawners;
    private GreenfootSound worldSound;
    //Static Variables
    private static boolean bloodMooning;
 
    /**
     * Constructor for objects of class MyWorld.
     *
     */
    public VehicleWorld()
    {   
        // Create a new world with 600x400 cells with a cell size of 1x1 pixels.
        super(800, 600, 1, false);
 
        setPaintOrder (Villager.class, EvilWitch.class, GoodWitch.class, Acidmon.class, BloodDemon.class, Prowler.class);
         
        bloodMooning = false;
        worldSound = new GreenfootSound ("backgroundsound.wav");
        // set up background
        GreenfootImage background = new GreenfootImage("background.png");
        background.scale(getWidth(), getHeight());
         
        setBackground(background);
 
        // Set critical variables
        laneCount = 6;
        laneHeight = 48;
        spaceBetweenLanes = 6;
        splitAtCenter = false;
        twoWayTraffic = false;
 
        setActOrder(Pedestrian.class);
        // Init lane spawner objects
        laneSpawners = new VehicleSpawner[laneCount];
 
        // Prepare lanes method - draws the lanes
        lanePositionsY = prepareLanes (this, background, laneSpawners, 222, laneHeight, laneCount, spaceBetweenLanes, twoWayTraffic, splitAtCenter);
 
    }
 
    public void act () {
        spawn();
    }
     
    public void started()
    {
        worldSound.setVolume(80);
        worldSound.play();
    }
     
    public boolean isGrabbed(final Villager villager)
    {
        for (final Grabber grabber : getObjects(Grabber.class)) {
            if (villager.equals(grabber.getGrabbed())) {
                return true;
            }
        }
        return false;
    }
     
    public static float getDistance (Actor a, Actor b)
    {
        double distance;
        double xLength = a.getX() - b.getX();
        double yLength = a.getY() - b.getY();
        distance = Math.sqrt(Math.pow(xLength, 2) + Math.pow(yLength, 2));
        return (float)distance;
    }
     
    private void spawn () {
        // Chance to spawn a vehicle
        if (Greenfoot.getRandomNumber (60) == 0){
            int lane = Greenfoot.getRandomNumber(laneCount);
            if (!laneSpawners[lane].isTouchingVehicle()){
                int vehicleType = Greenfoot.getRandomNumber(4);
                if (vehicleType == 0){
                    addObject(new Acidmon(laneSpawners[lane]), 0, 0);
                //} else if (vehicleType == 2){
                    //addObject(new BloodDemon(laneSpawners[lane]), 0, 0);
                } else if (vehicleType == 1){
                    addObject(new Prowler(laneSpawners[lane]), 0, 0);
                } else if (vehicleType == 2){
                    addObject(new RootedCorpse(laneSpawners[lane]), 0, 0);
                }
            }
        }
 
        // Chance to spawn a Pedestrian
        if (Greenfoot.getRandomNumber (40) == 0){
            int xSpawnLocation = Greenfoot.getRandomNumber (600) + 100; // random between 99 and 699, so not near edges
            boolean spawnAtTop = true;//Greenfoot.getRandomNumber(2) == 0 ? true : false;
            if (spawnAtTop){
                addObject (new Villager (1), xSpawnLocation, 50);
            } else {
                addObject (new Villager (-1), xSpawnLocation, 550);
            }
        }
        else if (Greenfoot.getRandomNumber (160) == 0){
            int xSpawnLocation = Greenfoot.getRandomNumber (600) + 100; // random between 99 and 699, so not near edges
            boolean spawnAtTop = true;//Greenfoot.getRandomNumber(2) == 0 ? true : false;
            if (spawnAtTop){
                addObject (new EvilWitch (1), xSpawnLocation, 50);
            } else {
                addObject (new EvilWitch (-1), xSpawnLocation, 550);
            }
        }
        else if (Greenfoot.getRandomNumber (160) == 0){
            int xSpawnLocation = Greenfoot.getRandomNumber (600) + 100; // random between 99 and 699, so not near edges
            boolean spawnAtTop = true;//Greenfoot.getRandomNumber(2) == 0 ? true : false;
            if (spawnAtTop){
                addObject (new GoodWitch (1), xSpawnLocation, 50);
            } else {
                addObject (new GoodWitch (-1), xSpawnLocation, 550);
            }
        }
         
        //Blood moon event
        if (!bloodMooning && Greenfoot.getRandomNumber(300) == 0){
            int lane = Greenfoot.getRandomNumber(laneCount);
            addObject (new BloodMoon(240), 400, 300);
            if (getObjects(BloodDemon.class).size() == 0)
            {
                addObject(new BloodDemon(laneSpawners[lane]), 0, 0);
            }
            bloodMooning = true;
        }
         
        if (bloodMooning && getObjects(BloodMoon.class).size() == 0){
            bloodMooning = false;
        }
    }
     
    public static boolean isBloodMooning()
    {
        return bloodMooning;
    }
 
    /**
     *  Given a lane number (zero-indexed), return the y position
     *  in the centre of the lane. (doesn't factor offset, so
     *  watch your offset, i.e. with Bus).
     
     *  @param lane the lane number (zero-indexed)
     *  @return int the y position of the lane's center, or -1 if invalid
     */
    public int getLaneY (int lane){
        if (lane < lanePositionsY.length){
            return lanePositionsY[lane];
        }
        return -1;
    }
     
    /**
     * Given a y-position, return the lane number (zero-indexed).
     * Note that the y-position must be valid, and you should
     * include the offset in your calculations before calling this method.
     * For example, if a Bus is in a lane at y=100, but is offset by -20,
     * it is actually in the lane located at y=80, so you should send
     * 80 to this method, not 100.
     *
     * @param y - the y position of the lane the Vehicle is in
     * @return int the lane number, zero-indexed
     *
     */
    public int getLane (int y){
        for (int i = 0; i < lanePositionsY.length; i++){
            if (y == lanePositionsY[i]){
                return i;
            }
        }
        return -1;
    }
     
    public static int[] prepareLanes (World world, GreenfootImage target, VehicleSpawner[] spawners, int startY, int heightPerLane, int lanes, int spacing, boolean twoWay, boolean centreSplit, int centreSpacing)
    {
        // Declare an array to store the y values as I calculate them
        int[] lanePositions = new int[lanes];
        // Pre-calculate half of the lane height, as this will frequently be used for drawing.
        // To help make it clear, the heightOffset is the distance from the centre of the lane (it's y position)
        // to the outer edge of the lane.
        int heightOffset = heightPerLane / 2;
 
        // draw top border
        target.setColor (BROWN_BORDER);
        target.fillRect (0, startY, target.getWidth(), spacing);
 
        // Main Loop to Calculate Positions and draw lanes
        for (int i = 0; i < lanes; i++){
            // calculate the position for the lane
            lanePositions[i] = startY + spacing + (i * (heightPerLane+spacing)) + heightOffset ;
             
            // draw lane
            target.setColor(GREEN_STREET);
            // the lane body
            target.fillRect (0, lanePositions[i] - heightOffset, target.getWidth(), heightPerLane);
            // the lane spacing - where the white or yellow lines will get drawn
            target.fillRect(0, lanePositions[i] + heightOffset, target.getWidth(), spacing);
 
            // Place spawners and draw lines depending on whether its 2 way and centre split
            if (twoWay && centreSplit){
                // first half of the lanes go rightward (no option for left-hand drive, sorry UK students .. ?)
                if ( i < lanes / 2){
                    spawners[i] = new VehicleSpawner(false, heightPerLane);
                    world.addObject(spawners[i], target.getWidth(), lanePositions[i]);
                } else { // second half of the lanes go leftward
                    spawners[i] = new VehicleSpawner(true, heightPerLane);
                    world.addObject(spawners[i], 0, lanePositions[i]);
                }
 
                // draw yellow lines if middle
                if (i == lanes / 2){
                    target.setColor(BROWN_LINE);
                    target.fillRect(0, lanePositions[i] - heightOffset - spacing, target.getWidth(), spacing);
 
                } /*else if (i > 0){ // draw white lines if not first lane
                    for (int j = 0; j < target.getWidth(); j += 120){
                        target.setColor (Color.WHITE);
                        target.fillRect (j, lanePositions[i] - heightOffset - spacing, 60, spacing);
                    }
                }*/
 
            } else if (twoWay){ // not center split
                if ( i % 2 == 0){
                    spawners[i] = new VehicleSpawner(false, heightPerLane);
                    world.addObject(spawners[i], target.getWidth(), lanePositions[i]);
                } else {
                    spawners[i] = new VehicleSpawner(true, heightPerLane);
                    world.addObject(spawners[i], 0, lanePositions[i]);
                }
 
                // draw Grey Border if between two "Streets"
                if (i > 0){ // but not in first position
                    if (i % 2 == 0){
                        target.setColor(BROWN_BORDER);
                        target.fillRect(0, lanePositions[i] - heightOffset - spacing, target.getWidth(), spacing);
 
                    } else { // draw dotted lines
                        for (int j = 0; j < target.getWidth(); j += 120){
                            target.setColor (BROWN_LINE);
                            target.fillRect (j, lanePositions[i] - heightOffset - spacing, 60, spacing);
                        }
                    }
                }
            } else { // One way traffic
                spawners[i] = new VehicleSpawner(true, heightPerLane);
                world.addObject(spawners[i], 0, lanePositions[i]);
                /*if (i > 0){
                    for (int j = 0; j < target.getWidth(); j += 120){
                        target.setColor (Color.WHITE);
                        target.fillRect (j, lanePositions[i] - heightOffset - spacing, 60, spacing);
                    }
                }*/
            }
        }
        // draws bottom border
        target.setColor (BROWN_BORDER);
        target.fillRect (0, lanePositions[lanes-1] + heightOffset, target.getWidth(), spacing);
 
        return lanePositions;
    }
 
    /**
     * <p>The prepareLanes method is a static (standalone) method that takes a list of parameters about the desired roadway and then builds it.</p>
     *
     * <p><b>Note:</b> So far, Centre-split is the only option, regardless of what values you send for that parameters.</p>
     *
     * <p>This method does three things:</p>
     * <ul>
     *  <li> Determines the Y coordinate for each lane (each lane is centered vertically around the position)</li>
     *  <li> Draws lanes onto the GreenfootImage target that is passed in at the specified / calculated positions.
     *       (Nothing is returned, it just manipulates the object which affects the original).</li>
     *  <li> Places the VehicleSpawners (passed in via the array parameter spawners) into the World (also passed in via parameters).</li>
     * </ul>
     *
     * <p> After this method is run, there is a visual road as well as the objects needed to spawn Vehicles. Examine the table below for an
     * in-depth description of what the roadway will look like and what each parameter/component represents.</p>
     *
     * <pre>
     *                  <=== Start Y
     *  ||||||||||||||  <=== Top Border
     *  /------------\
     *  |            | 
     *  |      Y[0]  |  <=== Lane Position (Y) is the middle of the lane
     *  |            |
     *  \------------/
     *  [##] [##] [##| <== spacing ( where the lane lines or borders are )
     *  /------------\
     *  |            | 
     *  |      Y[1]  |
     *  |            |
     *  \------------/
     *  ||||||||||||||  <== Bottom Border
     * </pre>
     *
     * @param world     The World that the VehicleSpawners will be added to
     * @param target    The GreenfootImage that the lanes will be drawn on, usually but not necessarily the background of the World.
     * @param spawners  An array of VehicleSpawner to be added to the World
     * @param startY    The top Y position where lanes (drawing) should start
     * @param heightPerLane The height of the desired lanes
     * @param lanes     The total number of lanes desired
     * @param spacing   The distance, in pixels, between each lane
     * @param twoWay    Should traffic flow both ways? Leave false for a one-way street (Not Yet Implemented)
     * @param centreSplit   Should the whole road be split in the middle? Or lots of parallel two-way streets? Must also be two-way street (twoWay == true) or else NO EFFECT
     *
     */
    public static int[] prepareLanes (World world, GreenfootImage target, VehicleSpawner[] spawners, int startY, int heightPerLane, int lanes, int spacing, boolean twoWay, boolean centreSplit){
        return prepareLanes (world, target, spawners, startY, heightPerLane, lanes, spacing, twoWay, centreSplit, spacing);
    }
 
}
danpost danpost

2023/3/28

#
Looks like the distance between lanes is 54 (/** lane offset value */).
You need to login to post a reply.