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:
Vehicle Superclass code:
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 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); } } |
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 ; } } |
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 ); } } |