/** * Construct a new world. The size of the world (in number of cells) and the size of each cell (in * pixels) must be specified. This constructor allows the option of creating an unbounded world, * which actors can move outside the boundaries of. * * @param worldWidth The width of the world (in cells). * @param worldHeight The height of the world (in cells). * @param cellSize Size of a cell in pixels. * @param bounded Should actors be restricted to the world boundary? */ public World(int worldWidth, int worldHeight, int cellSize, boolean bounded) { initialize(worldWidth, worldHeight, cellSize); this.isBounded = bounded; backgroundIsClassImage = true; setBackground(getClassImage()); // Now, the WorldHandler must be informed of the new world, so it can be // used immediately. This is important for actors that are created by // the world constructor, if the actors are accessing the world in their // constructors (by using getWidth/Height for instance) final WorldHandler wHandler = WorldHandler.getInstance(); if (wHandler != null) { // will be null when running unit tests. wHandler.setInitialisingWorld(this); } }
@Override public void paintComponent(Graphics g) { if (world == null) { Color c = g.getColor(); g.setColor(getParent().getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(c); WorldHandler.getInstance().repainted(); return; } // We need to sync, so that objects are not added and removed when we // traverse the list. // But, we only try to get the lock for a brief period to avoid // deadlocks. A deadlock could otherwise happen if a modal dialog is // created from the user code in one of the act() methods. // We could do the sync only on the paintObjects, but that would mean // that the background will be reset and no objects painted, resulting // in a slightly broken look, if the user code is sleeping (with // Thread.sleep). try { ReentrantReadWriteLock lock = WorldHandler.getInstance().getWorldLock(); int timeout = WorldHandler.READ_LOCK_TIMEOUT; if (lock.readLock().tryLock(timeout, TimeUnit.MILLISECONDS)) { try { Insets insets = getInsets(); Graphics2D g2 = (Graphics2D) g; g.translate(insets.left, insets.top); paintBackground(g2); paintObjects(g2); paintDraggedObject(g2); WorldVisitor.paintDebug(world, g2); g.translate(-insets.left, -insets.top); } finally { lock.readLock().unlock(); WorldHandler.getInstance().repainted(); } } else { WorldHandler.getInstance().repainted(); // we failed, but notify waiters anyway // (otherwise they keep waiting indefinitely...) } } catch (InterruptedException e) { e.printStackTrace(); } }
/** * Will save the currently selected image as the image of the class if OK is pressed. * * <p>If performed on the current world this will allow for previewing of the currently selected * image, but revert to the original background image of cancelled. * * @param e ignored */ public void actionPerformed(ActionEvent e) { final World currentWorld = WorldHandler.getInstance().getWorld(); // save the original background if possible final GreenfootImage originalBackground = ((currentWorld == null) ? null : WorldVisitor.getBackgroundImage(currentWorld)); // allow the previewing if we are setting the image of the current world. ImageSelectionWatcher watcher = null; if (currentWorld != null && currentWorld.getClass().getName().equals(classView.getGClass().getQualifiedName())) { watcher = new ImageSelectionWatcher() { @Override public void imageSelected(final File imageFile) { if (imageFile != null) { Simulation.getInstance() .runLater( new Runnable() { @Override public void run() { if (WorldHandler.getInstance().getWorld() == currentWorld) { currentWorld.setBackground(imageFile.getAbsolutePath()); } } }); } } }; } // initialise our image library frame JFrame gfFrame = GreenfootMain.getInstance().getFrame(); ImageLibFrame imageLibFrame = new ImageLibFrame(gfFrame, classView, watcher); DialogManager.centreDialog(imageLibFrame); imageLibFrame.setVisible(true); // set the image of the class to the selected file if (imageLibFrame.getResult() == ImageLibFrame.OK) { File currentImageFile = imageLibFrame.getSelectedImageFile(); setClassImage(classView, gclassRole, currentImageFile); gfFrame.repaint(); } // or if cancelled reset the world background to the original format // to avoid white screens or preview images being left there. else if (currentWorld != null && imageLibFrame.getResult() == ImageLibFrame.CANCEL) { Simulation.getInstance() .runLater( new Runnable() { @Override public void run() { currentWorld.setBackground(originalBackground); } }); } }
/** * Add an Actor to the world. * * @param object The new object to add. * @param x The x coordinate of the location where the object is added. * @param y The y coordinate of the location where the object is added. */ public void addObject(Actor object, int x, int y) { if (object.world != null) { if (object.world == this) { return; // Actor is already in the world } object.world.removeObject(object); } objectsDisordered.add(object); addInPaintOrder(object); addInActOrder(object); // Note we must call this before adding the object to the collision checker, // so that the cached bounds are cleared: object.addToWorld(x, y, this); collisionChecker.addObject(object); object.addedToWorld(this); WorldHandler whInstance = WorldHandler.getInstance(); if (whInstance != null) { WorldHandler.getInstance().objectAddedToWorld(object); } }
/** Needed for running in applets. */ public static void initialize() { MouseMotionAdapter mma = new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { LocationTracker.instance().move(e); } @Override public void mouseDragged(MouseEvent e) {} }; MouseAdapter ma = new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { LocationTracker.instance().click(e); } }; WorldCanvas canvas = WorldHandler.getInstance().getWorldCanvas(); canvas.addMouseListener(ma); canvas.addMouseMotionListener(mma); }
/** Repaints the world. */ public void repaint() { WorldHandler.getInstance().repaint(); }