The Art and Science of ERIC S. ROBERTS CHAPTER 11 Java An Introduction to Computer Science Arrays and ArrayLists Little boxes, on a hillside, little boxes made of ticky-tacky Little boxes, little boxes, little boxes, all the same There’s a green one and a pink one and a blue one and a yellow one And they're all made out of ticky-tacky and they all look just the same —Malvina Reynolds, “Little Boxes,” 1962 Lecture Slides, Part III CS101 @ Özyeğin University Slides are adapted from the originals available at http://www-cs-faculty.stanford.edu/~eroberts/books/ArtAndScienceOfJava/ Chapter 11—Arrays and ArrayLists 1 Example: SimpleSnake • In this problem we will implement a simple version of the snake game in which a snake bounces from the walls of the screen. 2 SimpleSnake • Each part of the snake body will have a size of BODY_PART_WIDTH. • There are SNAKE_LENGTH many body parts. • Each body part is a GOval object. All the body parts are kept in a GOval array. private GOval[] snakeBody = new GOval[SNAKE_LENGTH]; • Corresponding to each body part, we keep the velocity in the x direction and in the y direction. private double[] xVelocities = new double[SNAKE_LENGTH]; private double[] yVelocities = new double[SNAKE_LENGTH]; • Food for thought: Why don’t we keep a single velocity value? 3 SimpleSnake public void run() { initializeSnakeBody(); initializeVelocities(); while(true) { moveSnake(); checkBounds(); pause(20); } } 4 SimpleSnake (initializing body parts) BODY_PART_WIDTH / sqrt(2) BODY_PART_WIDTH 5 SimpleSnake public void initializeSnakeBody() { int xCenter = getWidth() / 2; int yCenter = getHeight() / 2; for(int i = 0; i < snakeBody.length; i++) { snakeBody[i] = createSnakeBodyPartCenteredAt(xCenter, yCenter); xCenter -= (BODY_PART_WIDTH/Math.sqrt(2)); yCenter -= (BODY_PART_WIDTH/Math.sqrt(2)); add(snakeBody[i]); } } public GOval createSnakeBodyPartCenteredAt(int xCenter, int yCenter) { GOval circle = new GOval(xCenter - BODY_PART_WIDTH/2, yCenter - BODY_PART_WIDTH/2, BODY_PART_WIDTH, BODY_PART_WIDTH); circle.setFilled(true); circle.setFillColor(Color.BLUE); return circle; } 6 public void initializeVelocities() { for(int i = 0; i < xVelocities.length; i++) { xVelocities[i] = 3; } for(int i = 0; i < yVelocities.length; i++) { yVelocities[i] = 3; } } SimpleSnake public void moveSnake() { for(int i = 0; i < snakeBody.length; i++) { snakeBody[i].move(xVelocities[i], yVelocities[i]); } } public void checkBounds() { for(int i = 0; i < snakeBody.length; i++) { double x = snakeBody[i].getX(); double y = snakeBody[i].getY(); if(x < 0 || x > getWidth() - BODY_PART_WIDTH) { xVelocities[i] *= -1; } if(y < 0 || y > getHeight() - BODY_PART_WIDTH) { yVelocities[i] *= -1; } } } 7 SimpleSnake v.2 • In this problem we will implement yet another simple version of the snake game. • P.S. You may need to click on the window to request focus so 8 that key events will be captured. SimpleSnake v.2 • In this version, there is a single, constant SPEED value. The head of the snake has a direction, which can be one of NORTH, EAST, SOUTH, WEST. • After each move, a body part replaces the part in the front. snake direction 9 SimpleSnake v.2 • In this version, there is a single, constant SPEED value. The head of the snake has a direction, which can be one of NORTH, EAST, SOUTH, WEST. • After each move, a body part replaces the part in the front. snake direction 10 SimpleSnake v.2 • Moving the snake parts: • Each part element gets the location of the element in front of it. • Head element is moved according to its direction. 0 1 2 3 4 5 6 7 11 public class SimpleSnakeV2 extends GraphicsProgram { private final int BODY_PART_WIDTH = 20; private final int SNAKE_LENGTH = 12; private final int SPEED = BODY_PART_WIDTH; SimpleSnake private GOval[] snakeBody = new GOval[SNAKE_LENGTH]; private final int NORTH = 1; private final int EAST = 2; private final int SOUTH = 3; private final int WEST = 4; // Direction of the head is one of {N, W, S, E} private int direction = EAST; // Cont’d on the next slide // . . . 12 public void run() { addGrid(); initializeSnakeBody(); while(true) { moveSnake(); pause(200); } } public void addGrid() { for(int i = 0; i < getWidth(); i += BODY_PART_WIDTH) { add(new GLine(i, 0, i, getHeight())); } for(int i = 0; i < getHeight(); i += BODY_PART_WIDTH) { add(new GLine(0, i, getWidth(), i)); } } 13 public void initializeSnakeBody() { int x = SNAKE_LENGTH * BODY_PART_WIDTH; int y = getHeight() / 2; for(int i = 0; i < snakeBody.length; i++) { snakeBody[i] = createSnakeBodyAt(x, y); x -= BODY_PART_WIDTH; add(snakeBody[i]); } } public GOval createSnakeBodyAt(int x, int y) { GOval circle = new GOval(x, y, BODY_PART_WIDTH, BODY_PART_WIDTH); circle.setFilled(true); circle.setFillColor(Color.BLUE); return circle; } 14 public void moveSnake() { // Each body part replaces the one in front of it for(int i = snakeBody.length - 1; i >= 1; i--) { GOval prevPart = snakeBody[i-1]; snakeBody[i].setLocation(prevPart.getX(), prevPart.getY()); } // Move the head if(direction == EAST) { snakeBody[0].move(SPEED, 0); } else if(direction == WEST) { snakeBody[0].move(-SPEED, 0); } else if(direction == NORTH) { snakeBody[0].move(0, -SPEED); } else if(direction == SOUTH) { snakeBody[0].move(0, SPEED); } } 15 // The method below is invoked whenever the user presses a key // You do not need to spend too much time understanding this part public void keyPressed(KeyEvent keyEvent) { // VK_UP: key code for up arrow key // VK_RIGHT: key code for right arrow key // VK_DOWN: key code for down arrow key // VK_LEFT: key code for left arrow key if(keyEvent.getKeyCode() == KeyEvent.VK_UP) { direction = NORTH; } else if(keyEvent.getKeyCode() == KeyEvent.VK_RIGHT) { direction = EAST; } else if(keyEvent.getKeyCode() == KeyEvent.VK_DOWN) { direction = SOUTH; } else if(keyEvent.getKeyCode() == KeyEvent.VK_LEFT) { direction = WEST; } } public void init() { setSize(600, 400); addKeyListeners(); // required for key interaction } } 16