Chapter 11 Scrolling with Greenfooot What Is Scrolling

  • Slides: 43
Download presentation
Chapter 11: Scrolling with Greenfooot

Chapter 11: Scrolling with Greenfooot

What Is Scrolling? Scrolling is an important and often necessary concept in games. It

What Is Scrolling? Scrolling is an important and often necessary concept in games. It can be used to: a) Convey a sense of movement to the player. b) Implement a world that extends beyond the immediate borders of the visible screen.

What Is Scrolling? Generally speaking, we cannot see what is not on the screen

What Is Scrolling? Generally speaking, we cannot see what is not on the screen until we scroll in the direction of what is off-screen. To emphasize the scrolling movement, it is necessary to have a background that changes as we scroll. This gives the player visual cues, telling them that they are moving to a new position in the world.

What Is Scrolling? For example, lets assume the following is the entire world of

What Is Scrolling? For example, lets assume the following is the entire world of a game: However, at any one time you can only see half of this world (due to screen restrictions):

What Is Scrolling? When we scroll to the right, a different portion of the

What Is Scrolling? When we scroll to the right, a different portion of the world comes into view:

Using Scrolling Whenever we want to have a level that exceeds or appears to

Using Scrolling Whenever we want to have a level that exceeds or appears to exceed a single “frame”, we have to use scrolling. The scrolling can either be automatic, or depend on the movement of a playercontroller actor. If the player moves to the right, we want to scroll our level accordingly, etc.

How To Implement Scrolling We can manage the scrolling of the World’s background by

How To Implement Scrolling We can manage the scrolling of the World’s background by creating a method that shifts the background-image itself. The game might require movement in more then 1 dimension, so a general scrolling method should be able to handle both an x and y offset. For simplicities sake, we will start with scrolling in only dimension.

How To Implement Scrolling It is certainly possible to implement scrolling using fixed and

How To Implement Scrolling It is certainly possible to implement scrolling using fixed and complex images (as shown in earlier slides with out “panorama”). However, it is usually preferable to scroll a pattern created by repeating simple cells.

Pattern Scrolling A pattern that is scrolled as a background image should be a

Pattern Scrolling A pattern that is scrolled as a background image should be a pattern that repeats seamlessly. This means that if we put the image side by side next to itself, there would be no visible breaks or gaps:

Pattern Scrolling When we use a repeatable pattern (of size less than our World)

Pattern Scrolling When we use a repeatable pattern (of size less than our World) as our World-image, the pattern will be automatically tiled. This means it is placed end to end until it fills up the whole screen. Due to tiling, we can scroll the screen by continually shifting where the drawing of the pattern starts. To do this, we will be using the draw. Image() method of the Greenfoot. Image class.

Pattern Scrolling An example: If we want to scroll 10 pixels to the left,

Pattern Scrolling An example: If we want to scroll 10 pixels to the left, we will do so by starting the drawing-process 10 pixels to the right. However, this leaves empty space at the left.

Pattern Scrolling We can fill this empty space by going one screen width to

Pattern Scrolling We can fill this empty space by going one screen width to the left from our initial position, and drawing an entire screen length of background starting from there:

Pattern Scrolling Only the part within the Greenfoot window is actually seen by the

Pattern Scrolling Only the part within the Greenfoot window is actually seen by the user, so it appears that the background has scrolled by whatever amount we shifted it:

Pattern Scrolling Note that this process can be applied to scrolling both left and

Pattern Scrolling Note that this process can be applied to scrolling both left and right. When scrolling to the right, we will simply shift our image to the left. The resulting empty space can be filled in by going one screen length to the right and drawing another full screen.

Pattern Scrolling Thus, to implement a scroll-effect we will draw our background-image at least

Pattern Scrolling Thus, to implement a scroll-effect we will draw our background-image at least twice: • once offset to the left (i. e. by a negative value) • once offset to the right (i. e. by a positive value) Scrolling left and scrolling right differ only in how far we offset left/right and how they are perceived by the user. The process itself stays the same.

Pattern Scrolling This means that we can implement a normalized shifting process by always

Pattern Scrolling This means that we can implement a normalized shifting process by always shifting in one direction first. For example: if dx is the shift amount and WIDTH is the screen width, then the following code ensures that the shift always starts from the left: dx = dx % WIDTH; if (dx > 0) dx = dx - WIDTH; // keep shift amount within one width // always draw the left-offset image first

The World act() method So far, we have only used Actor based act() methods.

The World act() method So far, we have only used Actor based act() methods. However, it is also possible to create World-based act() methods. Generally speaking, an act() method of a World object functions just like the act() method of an Actor object: It gets called continuously and can be used to implement tasks that require constant repetition (such as scrolling).

Pattern Scrolling Below is code that implements a continual shift for a generic World

Pattern Scrolling Below is code that implements a continual shift for a generic World of size 400*300: private private static final int WIDTH = 400; static final int HEIGHT = 300; Greenfoot. Image bg. Image; int scroll. Speed = 2; int scroll. Position = 0; public Scroll. World() { super(WIDTH, HEIGHT, 1); bg. Image = new Greenfoot. Image(get. Background()); } public void act() { scroll. Background(-scroll. Speed); }

Pattern Scrolling private void scroll. Background(int dx) { dx = dx % WIDTH; if

Pattern Scrolling private void scroll. Background(int dx) { dx = dx % WIDTH; if (dx > 0) dx = dx - WIDTH; scroll. Position = (scroll. Position + dx) % WIDTH; Greenfoot. Image bg = get. Background(); bg. draw. Image(bg. Image, scroll. Position, 0); bg. draw. Image(bg. Image, scroll. Position + WIDTH, 0); } Because of the act() method in this World class, this code immediately starts shifting the background left. A positive dx value would shift it to the right.

Using Pattern Scrolling The horizontal. Scrolling scenario is a modification of our Asteroids scenario.

Using Pattern Scrolling The horizontal. Scrolling scenario is a modification of our Asteroids scenario. It uses the exact code we just discussed and random enemy spawning to create the impression of our rocket actually moving through space.

Pattern Scrolling in the vertical direction is very similar to scrolling in the horizontal

Pattern Scrolling in the vertical direction is very similar to scrolling in the horizontal direction. We substitute the WIDTH constant with HEIGHT throughout our code, and use the y-coordinate argument for the draw. Image commands: bg. draw. Image(bg. Image, scroll. Position, 0); bg. draw. Image(bg. Image, scroll. Position + WIDTH, 0); bg. draw. Image(bg. Image, 0, scroll. Position); bg. draw. Image(bg. Image, 0, scroll. Position + HEIGHT);

Pattern Scrolling both horizontally and vertically functions very similar to just scrolling horizontally. However,

Pattern Scrolling both horizontally and vertically functions very similar to just scrolling horizontally. However, it requires some additional code, as we need to take 2 dimensions into account: private private static final int WIDTH = 600; static final int HEIGHT = 600; Greenfoot. Image bg. Image; int scroll. Speed = 2; int scroll. HPosition = 0; int scroll. VPosition = 0; public Scroll. World() { super(WIDTH, HEIGHT, 1); bg. Image = new Greenfoot. Image(get. Background()); }

Pattern Scrolling public void act() { scroll. Background(-scroll. Speed, scroll. Speed); } private void

Pattern Scrolling public void act() { scroll. Background(-scroll. Speed, scroll. Speed); } private void scroll. Background(int dx, int dy) { dx = dx % WIDTH; if (dx > 0) dx = dx - WIDTH; scroll. HPosition = (scroll. HPosition + dx) % WIDTH; dy = dy % HEIGHT; if (dy > 0) dy = dy - HEIGHT; scroll. VPosition = (scroll. VPosition + dy) % HEIGHT; Greenfoot. Image bg = get. Background();

Pattern Scrolling bg. draw. Image(bg. Image, scroll. HPosition, scroll. VPosition); bg. draw. Image(bg. Image,

Pattern Scrolling bg. draw. Image(bg. Image, scroll. HPosition, scroll. VPosition); bg. draw. Image(bg. Image, scroll. HPosition + WIDTH, scroll. VPosition); bg. draw. Image(bg. Image, scroll. HPosition , scroll. VPosition + HEIGHT); bg. draw. Image(bg. Image, scroll. HPosition + WIDTH , scroll. VPosition + HEIGHT); } Because of the act() method, this code will immediately start shifting the background down and to the left.

Using Pattern Scrolling The horizontaland. Vertical. Scrolling scenario is a modification of our Asteroids

Using Pattern Scrolling The horizontaland. Vertical. Scrolling scenario is a modification of our Asteroids scenario. In this scenario, the Rocket seems to move diagonally. It is also unable to fire Bullets and thus has to dodge incoming Asteroids.

Input-based Scrolling So far, we have only implemented automatic scrolling. While this works well

Input-based Scrolling So far, we have only implemented automatic scrolling. While this works well for some types of game, it is not always a sufficient solution. On the following slides, we will discuss how to implement Input and Actor based scrolling.

Connecting Input and Scrolling Luckily, we can use the same tools to implement manual

Connecting Input and Scrolling Luckily, we can use the same tools to implement manual scrolling as we have used previously to implement manual movement: public void act() { if (Greenfoot. is. Key. Down("left")) scroll. Background(-scroll. Speed, 0); if (Greenfoot. is. Key. Down("right")) scroll. Background(scroll. Speed, 0); if (Greenfoot. is. Key. Down("up")) scroll. Background(0, -scroll. Speed); if (Greenfoot. is. Key. Down("down")) scroll. Background(0, scroll. Speed); }

Bounded and Unbounded Worlds In Greenfoot, a World can be bounded or unbounded. We

Bounded and Unbounded Worlds In Greenfoot, a World can be bounded or unbounded. We cannot place an object in a coordinate that is off of the World when a world is bounded. The object will simply be placed on the nearest edge, and no farther. When a world is unbounded, we can place objects at coordinates outside of the world. In combination with scrolling, this will allow us to build Worlds that expand beyond a single screen.

Bounded and Unbounded Worlds Imagine a 400 x 300 world with a wombat in

Bounded and Unbounded Worlds Imagine a 400 x 300 world with a wombat in it, and you try to move the wombat to position (500, 150). The following would be the result: In the bounded case it gets stuck on the nearest edge (400, 150). In the unbounded case it goes off the screen and we can no longer see it. However, the wombat still exists in our game.

Bounded and Unbounded Worlds By default, Worlds in Greenfoot are bounded. If we want

Bounded and Unbounded Worlds By default, Worlds in Greenfoot are bounded. If we want an unbounded world, we must pass the value of false as the fourth value to the super() call in the constructor of the world: super(400, 300, 1, false); // 400 x 300 unbounded world

Scrolling Actors While we are able to scroll manually, this is a purely aesthetic

Scrolling Actors While we are able to scroll manually, this is a purely aesthetic effect so far: only the background moves. If we want to get to our lost wombat back, we have to move our Actor objects, too. We will use a new method for this: public void scroll. Actors(int dx, int dy)

Scrolling Actors Conceptually, the scroll. Actors method functions similar to the scroll. Background method.

Scrolling Actors Conceptually, the scroll. Actors method functions similar to the scroll. Background method. We supply two parameters and the method shifts the objects in that direction. However, we apply the changes not to our background, but to specific Actor objects. public void scroll. Actors(int dx, int dy) { List<Actor> actors = get. Objects(Wombat. class); for(Actor a : actors) { a. set. Location(a. get. X()+dx, a. get. Y()+dy); } }

Using Input-based Scrolling The scrolling. Pengu scenario implements user-controlled scrolling and Actor scrolling. Specifically,

Using Input-based Scrolling The scrolling. Pengu scenario implements user-controlled scrolling and Actor scrolling. Specifically, the background and the Ground objects scroll left/right, depending on what key is being pressed. The only part of the scenario that is NOT moving is the Pengu object, as the Pengu class it is not included in the scroll. Actors method.

Picture Image Scrolling Instead of scrolling patterns, we can also use fixed images as

Picture Image Scrolling Instead of scrolling patterns, we can also use fixed images as scrollable backgrounds. However, we have to take care that the image connects to itself more or less seamlessly in the direction we want to scroll:

Picture Image Scrolling A Panorama Image:

Picture Image Scrolling A Panorama Image:

Picture Image Scrolling Note: Complex images rarely connect seamlessly to themselves in both dimensions.

Picture Image Scrolling Note: Complex images rarely connect seamlessly to themselves in both dimensions.

Picture Image Scrolling While panorama images look nice, they are usually only repeatable horizontally.

Picture Image Scrolling While panorama images look nice, they are usually only repeatable horizontally. This means they cannot be properly vertically tiled by Greenfoot. There are two ways to handle this issue: 1) Scale (stretch or shrink) the image to exactly fit the window size horizontally and vertically 2) Scale (stretch or shrink) the image to make the height match the window height and let the image exceed the window horizontally

Scaling to Exactly fit the Window When you scale to exactly fit the window,

Scaling to Exactly fit the Window When you scale to exactly fit the window, your image may be contorted, and that may or may not present a problem: The above example shows how when then whole image is scaled to fit the screen the mountains look thinner. This change in the look and feel may or may not be a problem.

Picture Image Scrolling w/ Contortion If the contortion is not a problem, then we

Picture Image Scrolling w/ Contortion If the contortion is not a problem, then we can use the following code to scroll the background. In this case, the image is named "landscape. jpg": private private static final int WIDTH = 400; static final int HEIGHT = 300; Greenfoot. Image bg. Image; int scroll. Speed = 2; int scroll. Position = 0; public Scroll. World() { super(WIDTH, HEIGHT, 1); bg. Image = new Greenfoot. Image("landscape. jpg"); bg. Image. scale(WIDTH, HEIGHT) ; get. Background(). draw. Image(bg. Image, 0, 0); }

Picture Image Scrolling w/ Contortion public void act() { scroll. Background(-scroll. Speed); } private

Picture Image Scrolling w/ Contortion public void act() { scroll. Background(-scroll. Speed); } private void scroll. Background(int dx) { dx = dx % WIDTH; if (dx > 0) dx = dx- WIDTH; scroll. Position = (scroll. Position + dx) % WIDTH; Greenfoot. Image bg = get. Background(); bg. draw. Image(bg. Image, scroll. Position, 0); bg. draw. Image(bg. Image, scroll. Position + WIDTH, 0); } Once again, because of the act() method in this World class, this code immediately starts shifting the background left. A positive dx value would shift it to the right.

Picture Image Scrolling w/o Contortion If the contortion is a problem, then we need

Picture Image Scrolling w/o Contortion If the contortion is a problem, then we need to use a different approach. We will set the height of the image to be the same as the height of the screen. However, we will re-calculate the width of the image to keep the original aspect ratio: WIDTH 2 = ( bg. Image. get. Width() / bg. Image. get. Height() ) * HEIGHT

Picture Image Scrolling w/o Contortion private private static final int WIDTH = 400; static

Picture Image Scrolling w/o Contortion private private static final int WIDTH = 400; static final int HEIGHT = 300; final int picture. Width; Greenfoot. Image bg. Image; int scroll. Speed = 2; int scroll. Position = 0; public Scroll. World() { super(WIDTH, HEIGHT, 1); bg. Image = new Greenfoot. Image("landscape. jpg"); picture. Width = (int) ((double) bg. Image. get. Width() / bg. Image. get. Height() * HEIGHT); bg. Image. scale(picture. Width, HEIGHT) ; get. Background(). draw. Image(bg. Image, 0, 0); }

Picture Image Scrolling w/o Contortion public void act() { scroll. Background(-scroll. Speed); } private

Picture Image Scrolling w/o Contortion public void act() { scroll. Background(-scroll. Speed); } private void scroll. Background(int dx) { dx = dx %picture. Width; if (dx > 0) dx = dx - picture. Width; scroll. Position = (scroll. Position + dx) % picture. Width; Greenfoot. Image bg = get. Background(); bg. draw. Image(bg. Image, scroll. Position, 0); bg. draw. Image(bg. Image, scroll. Position + picture. Width, 0); }