diff --git a/Scroller.java b/Scroller.java new file mode 100644 index 0000000000000000000000000000000000000000..f5f48748b368ba11fa226c240db7c76c21477a3f --- /dev/null +++ b/Scroller.java @@ -0,0 +1,172 @@ +import greenfoot.*; +/** + * CLASS: Scroller (extends Object) + * AUTHOR: danpost (greenfoot.org username) + * DATE: November 11, 2016 + * MODIFIED: December 22, 2016 (fixed 'scroll' method for limited no-image scrolling) + * MODIFIED: February 21, 2017 (fixed scroll offsets for unlimited no-image scrolling) + * + * DESCRIPTION: This is a support class for a scrolling world. It contains two constructors; + * one for unlimited scrolling and one for limited scrolling. Both constructors have an 'image' + * parameter. Because image manipulation can hog up CPU time, it is important to remember that + * it is better not to have a scrolling background image (having an Actor for the background is + * probably worse than having the background scroll). For unlimited scrolling using a background + * image, the smaller that background image to be tiled, the better. Making the viewport (the + * size of the world that is visible) smaller can help in CPU expense, also. Scrolling worlds + * should be unbounded, allowing actors to move beyond the visible area. Ensuring that actors + * are removed from the world if no longer needed when out of view will help to prevent lag, + * as well. + * + * It is the responsibility of the World object that creates a Scroller object to determine when + * to scroll and by how much. + */ +public class Scroller +{ + private World world; // view window world + private GreenfootImage scrollImage; // scrolling image + private boolean limited; // flag to indicate whether scrolling is limited or not + private int scrolledX, scrolledY; // current scrolled distances + private int wide, high; // if limited, dimensions of scrolling area else of image to wrap + + /** + * This constructor is for an unlimited scrolling world; + * If 'image' is null, the background will not change; else the given image is wrapped + * + * @param viewWorld the world that scrolling will be performed on + * @param image the background image that will be tiled, if needed, and wrap with scrolling + */ + public Scroller(World viewWorld, GreenfootImage image) + { + world = viewWorld; + scrollImage = image; + if (image != null) + { + wide = image.getWidth(); + high = image.getHeight(); + } + scroll(0, 0); // sets initial background image + } + + /** + * This constructor is for a limited scrolling world; + * If 'image' is smaller than the given total scrolling area, it will be tiled + * If 'image' is null, the background will not change + * + * @param viewWorld the world that scrolling will be performed on + * @param image the background image that will be tiled, if needed, to fill the scrolling area + * @param wide the width of the visible area encompassed through scrolling; + * the given value must be at least equal to the width of 'viewWorld' and + * is given in world cells (not in pixels) + * @param high the height of the visible area encompassed through scrolling; + * the given value must be at least equal to the height of 'viewWorld' and + * is given in world cells (not in pixels) + */ + public Scroller(World viewWorld, GreenfootImage image, int wide, int high) + { + this.wide = wide; + this.high = high; + limited = true; + world = viewWorld; + if (image != null) + { + // create an image as large as scrolling area; tiled, if needeed + scrollImage = new GreenfootImage(wide*world.getCellSize(), high*world.getCellSize()); + for (int x=0; x<wide*world.getCellSize(); x+= image.getWidth()) + for (int y=0; y<high*world.getCellSize(); y+=image.getHeight()) + scrollImage.drawImage(image, x, y); + // set initial background image + scroll(0, 0); + } + } + + /** + * performs scrolling on 'world' by the given distances along the horizontal and vertical; + * if 'limited' is false, requested distances are actual scrolling distances; + * if 'limited' is true, the distances may be adjusted due to the limits of scrolling + * + * @param dsx the requested distance to shift everything horizontally + * @param dsy the requested distance to shift everything vertically + */ + public void scroll(int dsx, int dsy) + { + // adjust scroll amounts and scroll background image + if (limited) + { + // calculate limits of scrolling + int maxX = wide-world.getWidth(); + int maxY = high-world.getHeight(); + // apply limits to distances to scroll + if (scrolledX+dsx < 0) dsx = -scrolledX; + if (scrolledX+dsx >= maxX) dsx = maxX-scrolledX; + if (scrolledY+dsy < 0) dsy = -scrolledY; + if (scrolledY+dsy >= maxY) dsy = maxY-scrolledY; + // update scroll positions + scrolledX += dsx; + scrolledY += dsy; + // scroll background image + if (scrollImage != null) + { + world.getBackground().drawImage + ( + scrollImage, + -scrolledX*world.getCellSize(), + -scrolledY*world.getCellSize() + ); + } + } + else // unlimited image wrapping + { + // update scroll positions + scrolledX += dsx; + scrolledY += dsy; + // scroll background image + if (scrollImage != null) + { + // create working variables of scroll positions + int imageX = scrolledX*world.getCellSize(); + int imageY = scrolledY*world.getCellSize(); + // get near-zero starting positions for drawing 'scrollImage' + imageX = imageX%wide; + imageY = imageY%high; + // adjust negative values as needed + if (imageX < 0) imageX += wide; + if (imageY < 0) imageY += high; + // create image of appropriate size and tile fill 'scrollImage' onto it + GreenfootImage hold = new GreenfootImage(scrollImage); + hold.drawImage(scrollImage, -imageX, -imageY); + if (imageX > 0) hold.drawImage(scrollImage, wide-imageX, -imageY); + if (imageY > 0) hold.drawImage(scrollImage, -imageX, high-imageY); + if (imageX > 0 && imageY > 0) + hold.drawImage(scrollImage, wide-imageX, high-imageY); + // set image to background of 'world' + world.setBackground(hold); + } + } + // adjust position of all actors (that can move with 'setLocation') + for (Object obj : world.getObjects(null)) + { + Actor actor = (Actor) obj; + actor.setLocation(actor.getX()-dsx, actor.getY()-dsy); + } + } + + /** + * getter method for the current total scrolled distance horizontally + * + * @return the current total offset of horizontal scrolling + */ + public int getScrolledX() + { + return scrolledX; + } + + /** + * getter method for the current total scrolled distance vertically + * + * @return the current total offset of vertical scrolling + */ + public int getScrolledY() + { + return scrolledY; + } +}