Repository: l0y/learnjava5e Branch: master Commit: 8dd1bb833643 Files: 92 Total size: 231.9 KB Directory structure: gitextract_wyoxb4rf/ ├── .gitignore ├── LICENSE ├── README.md ├── ch02/ │ ├── HelloJava.java │ └── HelloJava2.java ├── ch04/ │ └── EuclidGCD.java ├── ch05/ │ ├── Apple.java │ ├── AppleToss.java │ ├── Field.java │ ├── GamePiece.java │ ├── HelloJava3.java │ ├── Physicist.java │ ├── PrintAppleDetails.java │ ├── PrintAppleDetails2.java │ ├── PrintAppleDetails3.java │ ├── PrintAppleDetails4.java │ └── Tree.java ├── ch06/ │ ├── Euclid2.java │ └── LogTest.java ├── ch07/ │ ├── Apple.java │ ├── AppleToss.java │ ├── Field.java │ ├── GamePiece.java │ ├── GameUtilities.java │ ├── Physicist.java │ └── Tree.java ├── ch08/ │ ├── Apple.java │ ├── AppleToss.java │ ├── Field.java │ ├── GamePiece.java │ ├── GameUtilities.java │ ├── Physicist.java │ └── Tree.java ├── ch09/ │ ├── URLConsumer.java │ ├── URLDemo.java │ ├── URLProducer.java │ ├── URLQueue.java │ └── game/ │ ├── Apple.java │ ├── AppleToss.java │ ├── Field.java │ ├── GamePiece.java │ ├── GameUtilities.java │ ├── Physicist.java │ └── Tree.java ├── ch10/ │ ├── ActionDemo1.java │ ├── ActionDemo2.java │ ├── BorderLayoutDemo.java │ ├── Buttons.java │ ├── HelloJavaAgain.java │ ├── HelloMouse.java │ ├── HelloMouseHelper.java │ ├── Labels.java │ ├── MenuDemo.java │ ├── ModalDemo.java │ ├── NestedPanelDemo.java │ ├── PhoneGridDemo.java │ ├── ProgressDemo.java │ ├── TextInputs.java │ ├── Widget.java │ └── game/ │ ├── Apple.java │ ├── AppleToss.java │ ├── Field.java │ ├── GamePiece.java │ ├── GameUtilities.java │ ├── Physicist.java │ └── Tree.java ├── ch11/ │ ├── CopyChannels.java │ ├── CopyChannels2.java │ ├── CopyChannels3.java │ ├── CopyFile.java │ ├── CopyStreams.java │ ├── DateAtHost.java │ ├── ListIt.java │ └── game/ │ ├── Apple.java │ ├── AppleToss.java │ ├── Field.java │ ├── GamePiece.java │ ├── GameUtilities.java │ ├── Physicist.java │ └── Tree.java ├── ch12/ │ ├── Post.java │ └── Read.java ├── ch13/ │ ├── ActionDemoLambda.java │ └── ListItLambda.java └── game/ ├── Apple.java ├── AppleToss.java ├── Field.java ├── GamePiece.java ├── GameUtilities.java ├── Multiplayer.java ├── Physicist.java └── Tree.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Compiled class file *.class # Log file *.log # BlueJ files *.ctxt # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.nar *.ear *.zip *.tar.gz *.rar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 l0y Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # learnjava5e Code examples for Learning Java, 5e by Marc Loy, Patrick Niemeyer, and Daniel Leuck ================================================ FILE: ch02/HelloJava.java ================================================ package ch02; import javax.swing.*; /** * A bare bones graphical version of Hello World. */ public class HelloJava { public static void main( String[] args ) { JFrame frame = new JFrame( "Hello, Java!" ); JLabel label = new JLabel("Hello, Java!", JLabel.CENTER ); frame.add(label); frame.setSize( 300, 300 ); frame.setVisible( true ); } } ================================================ FILE: ch02/HelloJava2.java ================================================ package ch02; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * An upgraded graphical application with interactivity! */ public class HelloJava2 { public static void main( String[] args ) { JFrame frame = new JFrame( "HelloJava2" ); frame.add( new HelloComponent2("Hello, Java!") ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); frame.setSize( 300, 300 ); frame.setVisible( true ); } } /* * Inheritence (the "extends" keyword below) and interfaces * (the "implements MouseMotionListener" portion) are covered in * Chapter 5. */ class HelloComponent2 extends JComponent implements MouseMotionListener { String theMessage; int messageX = 125, messageY = 95; // Coordinates of the message /** * Create a new component that can draw its message at an arbitrary position. * That position can be changed by dragging the mouse; we attach a listener * to pick up those drag events. */ public HelloComponent2( String message ) { theMessage = message; addMouseMotionListener(this); } public void paintComponent( Graphics g ) { g.drawString( theMessage, messageX, messageY ); } public void mouseDragged(MouseEvent e) { // Save the mouse coordinates and paint the message. messageX = e.getX(); messageY = e.getY(); repaint(); } public void mouseMoved(MouseEvent e) { // Ignore simple movements } } ================================================ FILE: ch04/EuclidGCD.java ================================================ package ch04; /** * A basic implementation of Euclid's greatest common denominator * algorithm. * * https://en.wikipedia.org/wiki/Algorithm */ public class EuclidGCD { public static void main(String args[]) { // For now, just "hard code" the two numbers to compare int a = 2701; int b = 222; while (b != 0) { if (a > b) { a = a - b; } else { b = b - a; } } System.out.println("GCD is " + a); } } ================================================ FILE: ch05/Apple.java ================================================ package ch05; import java.awt.*; /** * Apple * * This class sums up everything we know about the apples our physicists will be lobbing. * We keep the size and weight details. We'll expand this class as we cover more topics. */ public class Apple implements GamePiece { float mass; float diameter = 1.0f; int x, y; int size; // Setup some size constants public static final int SMALL = 0; public static final int MEDIUM = 1; public static final int LARGE = 2; // Some helpers for optimizing the draw() method that can be called many, many times int centerX, centerY; int scaledLength; // Boundary helper for optimizing collision detection with physicists and trees Rectangle boundingBox; // If we bumped into something, keep a reference to that thing around for cleanup and removal GamePiece collided; /** * Create a default, Medium apple */ public Apple() { this(MEDIUM); } /** * Create an Apple of the given size */ public Apple(int size) { setSize(size); } /** * Sets the size (and dependent properties) of the apple based on the * supplied value which must be one of the size constants. * * @param size one of SMALL, MEDIUM, or LARGE, other values are bounded to SMALL or LARGE */ public void setSize(int size) { if (size < SMALL) { size = SMALL; } if (size > LARGE) { size = LARGE; } this.size = size; switch (size) { case SMALL: diameter = 0.9f; mass = 0.5f; break; case MEDIUM: diameter = 1.0f; mass = 1.0f; break; case LARGE: diameter = 1.1f; mass = 1.8f; break; } // fillOval() used below draws an oval bounded by a box, so figure out the length of the sides. // Since we want a circle, we simply make our box a square so we only need one length. scaledLength = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS + 0.5); boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } public double getDiameter() { return diameter; } @Override public void setPosition(int x, int y) { // Our position is based on the center of the apple, but it will be drawn from the // upper left corner, so figure out the distance of that gap int offset = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS / 2); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { // Make sure our apple will be red, then paint it! g.setColor(Color.RED); g.fillOval(x, y, scaledLength, scaledLength); } /** * Determine whether or not this apple is touching another piece. */ public boolean isTouching(GamePiece other) { double xdiff = x - other.getPositionX(); double ydiff = y - other.getPositionY(); double distance = Math.sqrt(xdiff * xdiff + ydiff * ydiff); // For now, we can only really collide with other apples. // Just cheat a little and assume the other piece is in fact // an apple, and an apple with the same diameter. We'll fix // this as we fill out the other game pieces in later chapters. if (distance < diameter) { return true; } else { return false; } } public void printDetails() { System.out.println(" mass: " + mass); // Print the exact diameter: //System.out.println(" diameter: " + diameter); // Or a nice, human-friendly approximate: String niceNames[] = getAppleSizes(); if (diameter < 5.0f) { System.out.println(niceNames[SMALL]); } else if (diameter < 10.0f) { System.out.println(niceNames[MEDIUM]); } else { System.out.println(niceNames[LARGE]); } System.out.println(" position: (" + x + ", " + y +")"); } public static String[] getAppleSizes() { // Return names for our constants // The index of the name should match the value of the constant return new String[] { "SMALL", "MEDIUM", "LARGE" }; } } ================================================ FILE: ch05/AppleToss.java ================================================ package ch05; import javax.swing.*; /** * Our apple tossing game. This class extends JFrame to create our * main application window. We'll be filling this out along the way, * but for now we can setup a field with some trees and our player. */ public class AppleToss extends JFrame { Field field = new Field(); Physicist player1 = new Physicist(); public AppleToss() { // Create our frame super("Apple Toss Game"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(800,600); setResizable(false); // Build the field with our player and some trees setupFieldForOnePlayer(); } /** * A helper method to populate a one player field with target trees. */ private void setupFieldForOnePlayer() { // Place our (new) physicist in the lower left corner and connect them to the field player1.setPosition(100,500); field.setPlayer(player1); player1.setField(field); field.setupApples(); field.setupTree(); add(field); } public static void main(String args[]) { AppleToss game = new AppleToss(); game.setVisible(true); } } ================================================ FILE: ch05/Field.java ================================================ package ch05; import javax.swing.*; import java.awt.*; /** * The playing field for our game. This class will be undergoing quite a few * changes as we learn about more of Java's features in coming chapters. * For now, we can demonstrate how to work with member variables like a1 and a2 * as well as how to create new methods like setupApples() and detectCollisions(). */ public class Field extends JComponent { public static final float GRAVITY = 9.8f; // feet per second per second public static final int STEP = 40; // duration of an animation frame in milliseconds public static final int APPLE_SIZE_IN_PIXELS = 30; public static final int TREE_WIDTH_IN_PIXELS = 60; public static final int TREE_HEIGHT_IN_PIXELS = 2 * TREE_WIDTH_IN_PIXELS; public static final int PHYSICIST_SIZE_IN_PIXELS = 75; public static final int MAX_TREES = 12; Color fieldColor = Color.GRAY; Apple a1 = new Apple(); Apple a2 = new Apple(); Tree tree = new Tree(); Physicist physicist; public void setupApples() { // For now, just play with our apple attributes directly a1.diameter = 3.0f; a1.mass = 5.0f; a1.x = 20; a1.y = 40; a2.diameter = 8.0f; a2.mass = 10.0f; a2.x = 70; a2.y = 200; } public void setupTree() { // Unlike apples, we'll use the setPosition() method from our // GamePiece interface to setup our lonely tree tree.setPosition(500,200); } public void setPlayer(Physicist p) { physicist = p; } protected void paintComponent(Graphics g) { g.setColor(fieldColor); g.fillRect(0,0, getWidth(), getHeight()); tree.draw(g); physicist.draw(g); a1.draw(g); a2.draw(g); } public void detectCollisions() { if (a1.isTouching(a2)) { System.out.println("Collision detected!"); } else { System.out.println("Apples are not touching."); } } } ================================================ FILE: ch05/GamePiece.java ================================================ package ch05; import java.awt.*; /** * Interface to hold common elements for our apples, trees, and physicists * - methods for positioning on a Field * - methods for Drawing * - methods for detecting a collision with other GamePieces */ public interface GamePiece { /** * Sets the position of the piece on the playing field. * (0,0) is the upper left per standard Java drawing methods. * * @param x * @param y */ void setPosition(int x, int y); /** * Gets the current horizontal position of the piece on the field. * * @return current X position of the piece */ int getPositionX(); /** * Gets the current vertical position of the piece on the field. * * @return current Y position of the piece */ int getPositionY(); /** * Gets the bounding box of this piece. * * @return a Rectangle encompassing the visual elements of the piece */ Rectangle getBoundingBox(); /** * Draws the piece inside the given graphics context. * Do not assume anything about the state of the context. * * @param g */ void draw(Graphics g); /** * Detect a collision with another piece on the field. * By definition, a piece does NOT touch itself (i.e. it won't collide with itself). * * @param otherPiece * @return */ boolean isTouching(GamePiece otherPiece); } ================================================ FILE: ch05/HelloJava3.java ================================================ package ch05; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * Another (minor) upgrade to our graphical Hello, World example. * This variation introduces an inner class for the message component * and an anonymous inner class for the listener handling the mouse * drag events. (More on events and listeners in Chapter 10.) */ public class HelloJava3 extends JFrame { public static void main( String[] args ) { HelloJava3 demo = new HelloJava3(); demo.setVisible( true ); } public HelloJava3() { super( "HelloJava3" ); add( new HelloComponent3("Hello, Inner Java!") ); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setSize( 300, 300 ); } class HelloComponent3 extends JComponent { String theMessage; int messageX = 125, messageY = 95; // Coordinates of the message public HelloComponent3( String message ) { theMessage = message; addMouseMotionListener(new MouseMotionListener() { public void mouseDragged(MouseEvent e) { messageX = e.getX(); messageY = e.getY(); repaint(); } public void mouseMoved(MouseEvent e) { } }); } public void paintComponent( Graphics g ) { g.drawString( theMessage, messageX, messageY ); } } } ================================================ FILE: ch05/Physicist.java ================================================ package ch05; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import static java.awt.Color.*; /** * Our player class. A physicist can aim where to toss an apple in addition * to implementing the basic methods for GamePiece. We can also set a custom * color in case you build on the game and allow multiple physicists to be * on the screen at the same time. */ public class Physicist implements GamePiece { Color color; int centerX, centerY; float aimingAngle; float aimingForce; Field field; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; // Boundary helpers private final int physicistHeight = (int)(1.5 * Field.PHYSICIST_SIZE_IN_PIXELS); private Rectangle boundingBox; /** * Create a default, blue physicist */ public Physicist() { this(BLUE); } /** * Create a Physicist of the given color */ public Physicist(Color color) { setColor(color); aimingAngle = 90.0f; aimingForce = 50.0f; } public void setAimingAngle(Float angle) { aimingAngle = angle; } public void setAimingForce(Float force) { if (force < 0) { force = 0.0f; } aimingForce = force; } /** * Sets the color for the physicist. * * @param color */ public void setColor(Color color) { this.color = color; } @Override public void setPosition(int x, int y) { // Our position is based on the center of our dome, // but it will be drawn from the upper left corner. // Figure out the distance of that gap int offset = (int)(Field.PHYSICIST_SIZE_IN_PIXELS / 2.0f); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, physicistHeight); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } /** * Sets the active field once our physicist is being displayed. * * @param field Active game field */ public void setField(Field field) { this.field = field; } @Override public void draw(Graphics g) { // Make sure our physicist is the right color, then draw the semi-circle and box g.setColor(color); g.fillArc(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS, 0, 180); g.fillRect(x, centerY, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS); } @Override public boolean isTouching(GamePiece otherPiece) { // We don't really have any collisions to detect yet, so just return "no". // As we fill out all of the game pieces, we'll come back to this method // and provide a more useful response. return false; } } ================================================ FILE: ch05/PrintAppleDetails.java ================================================ package ch05; /** * A simple demonstration of accessing the contents of an object. We create * a new apple and print out some of its details. */ public class PrintAppleDetails { public static void main(String args[]) { String niceNames[] = Apple.getAppleSizes(); Apple a1 = new Apple(); System.out.println("Apple a1:"); System.out.println(" mass: " + a1.mass); System.out.println(" diameter: " + a1.diameter); System.out.println(" position: (" + a1.x + ", " + a1.y +")"); if (a1.diameter < 5.0f) { System.out.println("This is a " + niceNames[Apple.SMALL] + " apple."); } else if (a1.diameter < 10.0f) { System.out.println("This is a " + niceNames[Apple.MEDIUM] + " apple."); } else { System.out.println("This is a " + niceNames[Apple.LARGE] + " apple."); } } } ================================================ FILE: ch05/PrintAppleDetails2.java ================================================ package ch05; /** * Another quick example of working with an object. This time we print * the initial details of an apple, change some of those details, and * then do the same printing to provide a comparison of the results. */ public class PrintAppleDetails2 { public static void main(String args[]) { Apple a1 = new Apple(); System.out.println("Apple a1:"); System.out.println(" mass: " + a1.mass); System.out.println(" diameter: " + a1.diameter); System.out.println(" position: (" + a1.x + ", " + a1.y +")"); // fill in some information on a1 a1.mass = 10.0f; a1.x = 20; a1.y = 42; System.out.println("Updated a1:"); System.out.println(" mass: " + a1.mass); System.out.println(" diameter: " + a1.diameter); System.out.println(" position: (" + a1.x + ", " + a1.y +")"); } } ================================================ FILE: ch05/PrintAppleDetails3.java ================================================ package ch05; /** * A variation on PrintAppleDetails2 where we have moved the printing code * to the Apple class. Notice that this file is smaller than PrintAppleDetails2 * meaning fewer lines to make mistakes! */ public class PrintAppleDetails3 { public static void main(String args[]) { Apple a1 = new Apple(); System.out.println("Apple a1:"); a1.printDetails(); // fill in some information on a1 a1.mass = 10.0f; a1.x = 20; a1.y = 42; System.out.println("Updated a1:"); a1.printDetails(); } } ================================================ FILE: ch05/PrintAppleDetails4.java ================================================ package ch05; /** * One final iteration of manipulating and printing apple details. * Now the Field class understands apples so we access those apples * through the field object. Notice the double dot-notation. */ public class PrintAppleDetails4 { public static void main(String args[]) { Field f = new Field(); f.setupApples(); System.out.println("Apple a1:"); f.a1.printDetails(); System.out.println("Apple a2:"); f.a2.printDetails(); f.detectCollisions(); } } ================================================ FILE: ch05/Tree.java ================================================ package ch05; import java.awt.*; /** * An obstacle for our game. Trees includ a simple circle and rectangle shape. * They are built using sizes determined by the constants in the Field class. */ public class Tree implements GamePiece { int x, y; // Drawing helpers private Color leafColor = Color.GREEN.darker(); private Color trunkColor = new Color(101, 67, 33); private int trunkWidth = (int)(Field.TREE_WIDTH_IN_PIXELS * 0.2); private int trunkHeight = (int)(Field.TREE_WIDTH_IN_PIXELS * 1.1); private int trunkX, trunkY; // Boundary helpers private Rectangle boundingBox; @Override public void setPosition(int x, int y) { this.x = x; this.y = y; trunkX = x + (Field.TREE_WIDTH_IN_PIXELS - trunkWidth) / 2; trunkY = y + 2 * Field.TREE_WIDTH_IN_PIXELS - trunkHeight; boundingBox = new Rectangle(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_HEIGHT_IN_PIXELS); } @Override public int getPositionX() { return x; } @Override public int getPositionY() { return y; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { g.setColor(trunkColor); g.fillRect(trunkX, trunkY, trunkWidth, trunkHeight); g.setColor(leafColor); g.fillOval(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_WIDTH_IN_PIXELS); } @Override public boolean isTouching(GamePiece otherPiece) { // We don't really have any collisions to detect yet, so just return "no". // As we fill out all of the game pieces, we'll come back to this method // and provide a more useful response. return false; } } ================================================ FILE: ch06/Euclid2.java ================================================ package ch06; /** * A basic implementation of Euclid's greatest common denominator * algorithm. This version build on ch04 allowing you to pass the * numbers to use as command line arguments. */ public class Euclid2 { public static void main(String args[]) { int a = 2701; int b = 222; // Only try to parse arguments if we have exactly 2 if (args.length == 2) { try { a = Integer.parseInt(args[0]); b = Integer.parseInt(args[1]); } catch (NumberFormatException nfe) { System.err.println("Arguments were not both numbers. Using defaults."); } } else { System.err.println("Wrong number of arguments (expected 2). Using defaults."); } System.out.print("The GCD of " + a + " and " + b + " is "); while (b != 0) { if (a > b) { a = a - b; } else { b = b - a; } } System.out.println(a); } } ================================================ FILE: ch06/LogTest.java ================================================ package ch06; import java.util.logging.*; /** * A simple example of creating a logger and then using some of its methods. * For homework, try adjusting the log level where the comment is and see * which lines of output you still get on the console. */ public class LogTest { public static void main(String argv[]) { Logger logger = Logger.getLogger("com.oreilly.LogTest"); // try setting the log level here logger.severe("Power lost - running on backup!"); logger.warning("Database connection lost, retrying..."); logger.info("Startup complete."); logger.config("Server configuration: standalone, JVM version 1.5"); logger.fine("Loading graphing package."); logger.finer("Doing pie chart"); logger.finest("Starting bubble sort: value =" + 42); } } ================================================ FILE: ch07/Apple.java ================================================ /** * Apple * * This class sums up everything we know about the apples our physicists will be lobbing. * We keep the size and weight details and provide a few simple methods for lobbing. */ package ch07; import java.awt.*; public class Apple implements GamePiece { public static final int SMALL = 0; public static final int MEDIUM = 1; public static final int LARGE = 2; int size; double diameter; double mass; int centerX, centerY; Physicist myPhysicist; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; int scaledLength; // Boundary helper for optimizing collision detection with physicists and trees Rectangle boundingBox; // If we bumped into something, keep a reference to that thing around for cleanup and removal GamePiece collided; /** * Create a default, Medium apple */ public Apple(Physicist owner) { this(owner, MEDIUM); } /** * Create an Apple of the given size */ public Apple(Physicist owner, int size) { myPhysicist = owner; setSize(size); } /** * Sets the size (and dependent properties) of the apple based on the * supplied value which must be one of the size constants. * * @param size one of SMALL, MEDIUM, or LARGE, other values are bounded to SMALL or LARGE */ public void setSize(int size) { if (size < SMALL) { size = SMALL; } if (size > LARGE) { size = LARGE; } this.size = size; switch (size) { case SMALL: diameter = 0.9; mass = 0.5; break; case MEDIUM: diameter = 1.0; mass = 1.0; break; case LARGE: diameter = 1.1; mass = 1.8; break; } // fillOval() used below draws an oval bounded by a box, so figure out the length of the sides. // Since we want a circle, we simply make our box a square so we only need one length. scaledLength = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS + 0.5); boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } public double getDiameter() { return diameter; } @Override public void setPosition(int x, int y) { // Our position is based on the center of the apple, but it will be drawn from the // upper left corner, so figure out the distance of that gap int offset = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS / 2); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { // Make sure our apple will be red, then paint it! g.setColor(Color.RED); g.fillOval(x, y, scaledLength, scaledLength); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece || myPhysicist == otherPiece || collided != null) { // By definition we don't collide with ourselves, our physicist, or with more than one other piece return false; } if (otherPiece instanceof Apple) { // The other piece is an apple, so we can do a simple distance calculation using // the diameters of both apples. Apple otherApple = (Apple) otherPiece; int v = this.y - otherPiece.getPositionY(); // vertical difference int h = this.x - otherPiece.getPositionX(); // horizontal difference double distance = Math.sqrt(v * v + h * h); double myRadius = diameter * Field.APPLE_SIZE_IN_PIXELS / 2; double otherRadius = otherApple.getDiameter() * Field.APPLE_SIZE_IN_PIXELS / 2; if (distance < (myRadius + otherRadius)) { // Since apples track collisions, we'll update the other apple to keep everyone in sync setCollided(otherPiece); otherApple.setCollided(this); return true; } return false; } if (GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox())) { setCollided(otherPiece); return true; } return false; } public GamePiece getCollidedPiece() { return collided; } public void setCollided(GamePiece otherPiece) { this.collided = otherPiece; } } ================================================ FILE: ch07/AppleToss.java ================================================ package ch07; import javax.swing.*; /** * Our apple tossing game. This class extends JFrame to create our * main application window. We'll be filling this out along the way, * but for now we can setup a field with some trees and our player. */ public class AppleToss extends JFrame { Field field = new Field(); Physicist player1 = new Physicist(); public AppleToss() { // Create our frame super("Apple Toss Game"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(800,600); setResizable(false); // Build the field with our player and some trees setupFieldForOnePlayer(); } /** * A helper method to populate a one player field with target trees. */ private void setupFieldForOnePlayer() { // Place our (new) physicist in the lower left corner and connect them to the field player1.setPosition(100,500); field.setPlayer(player1); player1.setField(field); // And now make a few trees for target practice for (int row = 1; row <= 2; row++) { for (int col = 1; col <=3; col++) { field.addTree(col * 100, row * 100); } } add(field); } public static void main(String args[]) { AppleToss game = new AppleToss(); game.setVisible(true); } } ================================================ FILE: ch07/Field.java ================================================ package ch07; import javax.swing.*; import java.awt.Color; import java.awt.Graphics; import java.util.*; /** * The playing field for our game. Now we can setup some constants for * other game classes to use and create member variables for our player and some trees. */ public class Field extends JComponent { public static final float GRAVITY = 9.8f; // feet per second per second public static final int STEP = 40; // duration of an animation frame in milliseconds public static final int APPLE_SIZE_IN_PIXELS = 30; public static final int TREE_WIDTH_IN_PIXELS = 60; public static final int TREE_HEIGHT_IN_PIXELS = 2 * TREE_WIDTH_IN_PIXELS; public static final int PHYSICIST_SIZE_IN_PIXELS = 75; public static final int MAX_TREES = 12; Color fieldColor = Color.GRAY; Random random = new Random(); // List and ArrayList are covered in chapter 8 on Generics // synchronizedArrayList is covered in chapter 9 on Threads Physicist physicist; List trees = Collections.synchronizedList(new ArrayList<>()); protected void paintComponent(Graphics g) { g.setColor(fieldColor); g.fillRect(0,0, getWidth(), getHeight()); for (Tree t : trees) { t.draw(g); } physicist.draw(g); } public void setPlayer(Physicist p) { physicist = p; } public void addTree(int x, int y) { Tree tree = new Tree(); tree.setPosition(x,y); trees.add(tree); } } ================================================ FILE: ch07/GamePiece.java ================================================ package ch07; import java.awt.*; /** * Interface to hold common elements for our apples, trees, and physicists * From the README: * GamePiece * - methods for positioning on a PlayingField * - methods for Drawing * - methods for detecting a collision with other GamePieces */ public interface GamePiece { /** * Sets the position of the piece on the playing field. * (0,0) is the upper left per standard Java drawing methods. * * @param x * @param y */ void setPosition(int x, int y); /** * Gets the current horizontal position of the piece on the field. * * @return current X position of the piece */ int getPositionX(); /** * Gets the current vertical position of the piece on the field. * * @return current Y position of the piece */ int getPositionY(); /** * Gets the bounding box of this piece. * * @return a Rectangle encompassing the visual elements of the piece */ Rectangle getBoundingBox(); /** * Draws the piece inside the given graphics context. * Do not assume anything about the state of the context. * * @param g */ void draw(Graphics g); /** * Detect a collision with another piece on the field. * By definition, a piece does NOT touch itself (i.e. it won't collide with itself). * * @param otherPiece * @return */ boolean isTouching(GamePiece otherPiece); } ================================================ FILE: ch07/GameUtilities.java ================================================ package ch07; // collision detection between a circle and a rectangle // https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection import java.awt.*; /** * Utility class with a few helper methods for calculating various collisions and * intersections. */ public class GameUtilities { static boolean isPointInsideBox(int x, int y, Rectangle box) { // Our own custom test. We could of course use box.contains(), // but we can practice some interesting conditional checking here. // Let's test left and right first if (x >= box.x && x <= (box.x + box.width)) { // Our x coordinate is ok, so check our y if (y >= box.y && y <= (box.y + box.height)) { return true; } } // x or y was outside the box, so return false return false; } static boolean doesBoxIntersect(Rectangle box, Rectangle other) { // If any of the four corners of box are inside other, we intersect, so // let's check each one. Happily, that answer doesn't change if more // than one corner is contained in other, so we can return as soon as // we find the first contained corner. // Let's get some local copies of the corner coordinates // to make the call arguments easier to read. int x1 = box.x; int y1 = box.y; int x2 = x1 + box.width; int y2 = y1 + box.height; if (isPointInsideBox(x1, y1, other)) { // upper left return true; } else if (isPointInsideBox(x1, y2, other)) { // lower left return true; } else if (isPointInsideBox(x2, y1, other)) { // upper right return true; } else if (isPointInsideBox(x2, y2, other)) { // lower right return true; } // No box points in other so no intersection return false; } /** * Given two rectangles, do the overlap at all? This includes one box being * completely contained by the other box. * * @param box1 a box to test, order does not matter * @param box2 the other box * @return true if the boxes overlap, false otherwise */ public static boolean doBoxesIntersect(Rectangle box1, Rectangle box2) { // Another custom test. We could of course use box1.intersects(box2) // but we can practice method calls and some boolean logic here. if (doesBoxIntersect(box1, box2)) { // At least one of box1's points must be inside box2 return true; } else if (doesBoxIntersect(box2, box1)) { // None of box1's points were in box2, but at least one of box2's points are inside box1 return true; } // No intersections in either direction return false; } } ================================================ FILE: ch07/Physicist.java ================================================ package ch07; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import static java.awt.Color.*; /** * Our player class. A physicist has an apple that they can throw in addition * to implementing the basic methods for GamePiece. We can also set a custom * color in case you build on the game and allow multiple physicists to be * on the screen at the same time. */ public class Physicist implements GamePiece { Color color; int centerX, centerY; Apple aimingApple; float aimingAngle; float aimingForce; Field field; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; // Boundary helpers private final int physicistHeight = (int)(1.5 * Field.PHYSICIST_SIZE_IN_PIXELS); private Rectangle boundingBox; /** * Create a default, blue physicist */ public Physicist() { this(BLUE); } /** * Create a Physicist of the given color */ public Physicist(Color color) { setColor(color); aimingAngle = 90.0f; aimingForce = 50.0f; getNewApple(); } public void setAimingAngle(Float angle) { aimingAngle = angle; } public void setAimingForce(Float force) { if (force < 0) { force = 0.0f; } aimingForce = force; } /** * Sets the color for the physicist. * * @param color */ public void setColor(Color color) { this.color = color; } @Override public void setPosition(int x, int y) { // Our position is based on the center of our dome, // but it will be drawn from the upper left corner. // Figure out the distance of that gap int offset = (int)(Field.PHYSICIST_SIZE_IN_PIXELS / 2.0f); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, physicistHeight); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } /** * Sets the active field once our physicist is being displayed. * * @param field Active game field */ public void setField(Field field) { this.field = field; } /** * Take the current apple away from the physicist. Returns the apple for * use in animating on the field. Note that if there is no current apple being * aimed, null is returned. * * @return the current apple (if any) being aimed */ public Apple takeApple() { Apple myApple = aimingApple; aimingApple = null; return myApple; } /** * Get a new apple ready if we need one. Do not discard an existing apple. */ public void getNewApple() { // Don't drop the current apple if we already have one! if (aimingApple == null) { aimingApple = new Apple(this); } } @Override public void draw(Graphics g) { // Make sure our physicist is the right color, then draw the semi-circle and box g.setColor(color); g.fillArc(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS, 0, 180); g.fillRect(x, centerY, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS); // Do we have an apple to draw as well? if (aimingApple != null) { // Yes. Position the center of the apple on the edge of our semi-circle. // Use the current aimingAngle to determine where on the edge. double angleInRadians = Math.toRadians(aimingAngle); double radius = Field.PHYSICIST_SIZE_IN_PIXELS / 2.0; int aimingX = centerX + (int)(Math.cos(angleInRadians) * radius); int aimingY = centerY - (int)(Math.sin(angleInRadians) * radius); aimingApple.setPosition(aimingX, aimingY); aimingApple.draw(g); } } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } } ================================================ FILE: ch07/Tree.java ================================================ package ch07; import java.awt.*; /** * An obstacle for our game. Trees includ a simple circle and rectangle shape. * They are built using sizes determined by the constants in the Field class. */ public class Tree implements GamePiece { int x, y; // Drawing helpers private Color leafColor = Color.GREEN.darker(); private Color trunkColor = new Color(101, 67, 33); private int trunkWidth = (int)(Field.TREE_WIDTH_IN_PIXELS * 0.2); private int trunkHeight = (int)(Field.TREE_WIDTH_IN_PIXELS * 1.1); private int trunkX, trunkY; // Boundary helpers private Rectangle boundingBox; @Override public void setPosition(int x, int y) { this.x = x; this.y = y; trunkX = x + (Field.TREE_WIDTH_IN_PIXELS - trunkWidth) / 2; trunkY = y + 2 * Field.TREE_WIDTH_IN_PIXELS - trunkHeight; boundingBox = new Rectangle(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_HEIGHT_IN_PIXELS); } @Override public int getPositionX() { return x; } @Override public int getPositionY() { return y; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { g.setColor(trunkColor); g.fillRect(trunkX, trunkY, trunkWidth, trunkHeight); g.setColor(leafColor); g.fillOval(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_WIDTH_IN_PIXELS); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } } ================================================ FILE: ch08/Apple.java ================================================ /** * Apple * * This class sums up everything we know about the apples our physicists will be lobbing. * We keep the size and weight details and provide a few simple methods for lobbing. We * can now detect collisions as well. */ package ch08; import java.awt.*; public class Apple implements GamePiece { public static final int SMALL = 0; public static final int MEDIUM = 1; public static final int LARGE = 2; int size; double diameter; double mass; int centerX, centerY; Physicist myPhysicist; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; int scaledLength; // Boundary helper for optimizing collision detection with physicists and trees Rectangle boundingBox; // If we bumped into something, keep a reference to that thing around for cleanup and removal GamePiece collided; /** * Create a default, Medium apple */ public Apple(Physicist owner) { this(owner, MEDIUM); } /** * Create an Apple of the given size */ public Apple(Physicist owner, int size) { myPhysicist = owner; setSize(size); } /** * Sets the size (and dependent properties) of the apple based on the * supplied value which must be one of the size constants. * * @param size one of SMALL, MEDIUM, or LARGE, other values are bounded to SMALL or LARGE */ public void setSize(int size) { if (size < SMALL) { size = SMALL; } if (size > LARGE) { size = LARGE; } this.size = size; switch (size) { case SMALL: diameter = 0.9; mass = 0.5; break; case MEDIUM: diameter = 1.0; mass = 1.0; break; case LARGE: diameter = 1.1; mass = 1.8; break; } // fillOval() used below draws an oval bounded by a box, so figure out the length of the sides. // Since we want a circle, we simply make our box a square so we only need one length. scaledLength = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS + 0.5); boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } public double getDiameter() { return diameter; } @Override public void setPosition(int x, int y) { // Our position is based on the center of the apple, but it will be drawn from the // upper left corner, so figure out the distance of that gap int offset = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS / 2); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { // Make sure our apple will be red, then paint it! g.setColor(Color.RED); g.fillOval(x, y, scaledLength, scaledLength); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece || myPhysicist == otherPiece || collided != null) { // By definition we don't collide with ourselves, our physicist, or with more than one other piece return false; } if (otherPiece instanceof Apple) { // The other piece is an apple, so we can do a simple distance calculation using // the diameters of both apples. Apple otherApple = (Apple) otherPiece; int v = this.y - otherPiece.getPositionY(); // vertical difference int h = this.x - otherPiece.getPositionX(); // horizontal difference double distance = Math.sqrt(v * v + h * h); double myRadius = diameter * Field.APPLE_SIZE_IN_PIXELS / 2; double otherRadius = otherApple.getDiameter() * Field.APPLE_SIZE_IN_PIXELS / 2; if (distance < (myRadius + otherRadius)) { // Since apples track collisions, we'll update the other apple to keep everyone in sync setCollided(otherPiece); otherApple.setCollided(this); return true; } return false; } if (GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox())) { setCollided(otherPiece); return true; } return false; } public GamePiece getCollidedPiece() { return collided; } public void setCollided(GamePiece otherPiece) { this.collided = otherPiece; } } ================================================ FILE: ch08/AppleToss.java ================================================ package ch08; import javax.swing.*; import java.util.Random; /** * Our apple tossing game. This class extends JFrame to create our * main application window. We can now setup several trees for * target practice. (The ability for the player to aim and throw * will be covered in Chapter 10.) */ public class AppleToss extends JFrame { public static final int FIELD_WIDTH = 800; public static final int FIELD_HEIGHT = 600; Field field = new Field(); Physicist player1 = new Physicist(); // Helper class Random random = new Random(); public AppleToss() { // Create our frame super("Apple Toss Game"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(FIELD_WIDTH,FIELD_HEIGHT); setResizable(false); // Build the field with our player and some trees setupFieldForOnePlayer(); } /** * Helper method to return a good x value for a tree so it's not off the left or right edge. * * @return x value within the bounds of the playing field width */ private int goodX() { // at least half the width of the tree plus a few pixels int leftMargin = Field.TREE_WIDTH_IN_PIXELS / 2 + 5; // now find a random number between a left and right margin int rightMargin = FIELD_WIDTH - leftMargin; // And return a random number starting at the left margin return leftMargin + random.nextInt(rightMargin - leftMargin); } /** * Helper method to return a good y value for a tree so it's * not off the top or bottom of the screen. * * @return y value within the bounds of the playing field height */ private int goodY() { // at least half the height of the "leaves" plus a few pixels int topMargin = Field.TREE_WIDTH_IN_PIXELS / 2 + 5; // a little higher off the bottom int bottomMargin = FIELD_HEIGHT - Field.TREE_HEIGHT_IN_PIXELS; // And return a random number starting at the top margin but not past the bottom return topMargin + random.nextInt(bottomMargin - topMargin); } /** * A helper method to populate a one player field with target trees. */ private void setupFieldForOnePlayer() { // Place our (new) physicist in the lower left corner and connect them to the field player1.setPosition(100,500); field.setPlayer(player1); player1.setField(field); // And now make a few trees for target practice for (int i = field.trees.size(); i < Field.MAX_TREES; i++) { Tree t = new Tree(); t.setPosition(goodX(), goodY()); // Trees can be close to each other and overlap, // but they shouldn't intersect our physicist while(player1.isTouching(t)) { // We do intersect this tree, so let's try again t.setPosition(goodX(), goodY()); System.err.println("Repositioning an intersecting tree..."); } field.addTree(t); } add(field); } public static void main(String args[]) { AppleToss game = new AppleToss(); game.setVisible(true); } } ================================================ FILE: ch08/Field.java ================================================ package ch08; import javax.swing.*; import javax.swing.Timer; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.util.*; import java.util.List; /** * The playing field for our game. Now we can setup some constants for * other game classes to use and create member variables for our player and some trees. */ public class Field extends JComponent { public static final float GRAVITY = 9.8f; // feet per second per second public static final int STEP = 40; // duration of an animation frame in milliseconds public static final int APPLE_SIZE_IN_PIXELS = 30; public static final int TREE_WIDTH_IN_PIXELS = 60; public static final int TREE_HEIGHT_IN_PIXELS = 2 * TREE_WIDTH_IN_PIXELS; public static final int PHYSICIST_SIZE_IN_PIXELS = 75; public static final int MAX_TREES = 12; Color fieldColor = Color.GRAY; Random random = new Random(); // ArrayList covered in Generics chapter // synchronizedArrayList covered in Threads chapter Physicist physicist; List trees = Collections.synchronizedList(new ArrayList<>()); protected void paintComponent(Graphics g) { g.setColor(fieldColor); g.fillRect(0,0, getWidth(), getHeight()); for (Tree t : trees) { t.draw(g); } physicist.draw(g); } public void setPlayer(Physicist p) { physicist = p; } /** * Create and add a new tree to the field. * * @param x The X-coordinate for the tree * @param y The Y-coordinate for the tree */ public void addTree(int x, int y) { Tree tree = new Tree(); tree.setPosition(x,y); trees.add(tree); } /** * Add an existing tree to our field. Useful if the position * of the tree has already been set. * * @param t The existing tree to add */ public void addTree(Tree t) { trees.add(t); } } ================================================ FILE: ch08/GamePiece.java ================================================ package ch08; import java.awt.*; /** * Interface to hold common elements for our apples, trees, and physicists * From the README: * GamePiece * - methods for positioning on a PlayingField * - methods for Drawing * - methods for detecting a collision with other GamePieces */ public interface GamePiece { /** * Sets the position of the piece on the playing field. * (0,0) is the upper left per standard Java drawing methods. * * @param x * @param y */ void setPosition(int x, int y); /** * Gets the current horizontal position of the piece on the field. * * @return current X position of the piece */ int getPositionX(); /** * Gets the current vertical position of the piece on the field. * * @return current Y position of the piece */ int getPositionY(); /** * Gets the bounding box of this piece. * * @return a Rectangle encompassing the visual elements of the piece */ Rectangle getBoundingBox(); /** * Draws the piece inside the given graphics context. * Do not assume anything about the state of the context. * * @param g */ void draw(Graphics g); /** * Detect a collision with another piece on the field. * By definition, a piece does NOT touch itself (i.e. it won't collide with itself). * * @param otherPiece * @return */ boolean isTouching(GamePiece otherPiece); } ================================================ FILE: ch08/GameUtilities.java ================================================ package ch08; // collision detection between a circle and a rectangle // https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection import java.awt.*; /** * Utility class with a few helper methods for calculating various collisions and * intersections. */ public class GameUtilities { static boolean isPointInsideBox(int x, int y, Rectangle box) { // Our own custom test. We could of course use box.contains(), // but we can practice some interesting conditional checking here. // Let's test left and right first if (x >= box.x && x <= (box.x + box.width)) { // Our x coordinate is ok, so check our y if (y >= box.y && y <= (box.y + box.height)) { return true; } } // x or y was outside the box, so return false return false; } static boolean doesBoxIntersect(Rectangle box, Rectangle other) { // If any of the four corners of box are inside other, we intersect, so // let's check each one. Happily, that answer doesn't change if more // than one corner is contained in other, so we can return as soon as // we find the first contained corner. // Let's get some local copies of the corner coordinates // to make the call arguments easier to read. int x1 = box.x; int y1 = box.y; int x2 = x1 + box.width; int y2 = y1 + box.height; if (isPointInsideBox(x1, y1, other)) { // upper left return true; } else if (isPointInsideBox(x1, y2, other)) { // lower left return true; } else if (isPointInsideBox(x2, y1, other)) { // upper right return true; } else if (isPointInsideBox(x2, y2, other)) { // lower right return true; } // No box points in other so no intersection return false; } /** * Given two rectangles, do the overlap at all? This includes one box being * completely contained by the other box. * * @param box1 a box to test, order does not matter * @param box2 the other box * @return true if the boxes overlap, false otherwise */ public static boolean doBoxesIntersect(Rectangle box1, Rectangle box2) { // Another custom test. We could of course use box1.intersects(box2) // but we can practice method calls and some boolean logic here. if (doesBoxIntersect(box1, box2)) { // At least one of box1's points must be inside box2 return true; } else if (doesBoxIntersect(box2, box1)) { // None of box1's points were in box2, but at least one of box2's points are inside box1 return true; } // No intersections in either direction return false; } } ================================================ FILE: ch08/Physicist.java ================================================ package ch08; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import static java.awt.Color.*; /** * Our player class. A physicist has an apple that they can throw in addition * to implementing the basic methods for GamePiece. We can also set a custom * color in case you build on the game and allow multiple physicists to be * on the screen at the same time. */ public class Physicist implements GamePiece { Color color; int centerX, centerY; Apple aimingApple; float aimingAngle; float aimingForce; Field field; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; // Boundary helpers private final int physicistHeight = (int)(1.5 * Field.PHYSICIST_SIZE_IN_PIXELS); private Rectangle boundingBox; /** * Create a default, blue physicist */ public Physicist() { this(BLUE); } /** * Create a Physicist of the given color */ public Physicist(Color color) { setColor(color); aimingAngle = 90.0f; aimingForce = 50.0f; getNewApple(); } public void setAimingAngle(Float angle) { aimingAngle = angle; } public void setAimingForce(Float force) { if (force < 0) { force = 0.0f; } aimingForce = force; } /** * Sets the color for the physicist. * * @param color */ public void setColor(Color color) { this.color = color; } @Override public void setPosition(int x, int y) { // Our position is based on the center of our dome, // but it will be drawn from the upper left corner. // Figure out the distance of that gap int offset = (int)(Field.PHYSICIST_SIZE_IN_PIXELS / 2.0f); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, physicistHeight); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } /** * Sets the active field once our physicist is being displayed. * * @param field Active game field */ public void setField(Field field) { this.field = field; } /** * Take the current apple away from the physicist. Returns the apple for * use in animating on the field. Note that if there is no current apple being * aimed, null is returned. * * @return the current apple (if any) being aimed */ public Apple takeApple() { Apple myApple = aimingApple; aimingApple = null; return myApple; } /** * Get a new apple ready if we need one. Do not discard an existing apple. */ public void getNewApple() { // Don't drop the current apple if we already have one! if (aimingApple == null) { aimingApple = new Apple(this); } } @Override public void draw(Graphics g) { // Make sure our physicist is the right color, then draw the semi-circle and box g.setColor(color); g.fillArc(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS, 0, 180); g.fillRect(x, centerY, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS); // Do we have an apple to draw as well? if (aimingApple != null) { // Yes. Position the center of the apple on the edge of our semi-circle. // Use the current aimingAngle to determine where on the edge. double angleInRadians = Math.toRadians(aimingAngle); double radius = Field.PHYSICIST_SIZE_IN_PIXELS / 2.0; int aimingX = centerX + (int)(Math.cos(angleInRadians) * radius); int aimingY = centerY - (int)(Math.sin(angleInRadians) * radius); aimingApple.setPosition(aimingX, aimingY); aimingApple.draw(g); } } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } } ================================================ FILE: ch08/Tree.java ================================================ package ch08; import java.awt.*; /** * An obstacle for our game. Trees includ a simple circle and rectangle shape. * They are built using sizes determined by the constants in the Field class. */ public class Tree implements GamePiece { int x, y; // Drawing helpers private Color leafColor = Color.GREEN.darker(); private Color trunkColor = new Color(101, 67, 33); private int trunkWidth = (int)(Field.TREE_WIDTH_IN_PIXELS * 0.2); private int trunkHeight = (int)(Field.TREE_WIDTH_IN_PIXELS * 1.1); private int trunkX, trunkY; // Boundary helpers private Rectangle boundingBox; @Override public void setPosition(int x, int y) { this.x = x; this.y = y; trunkX = x + (Field.TREE_WIDTH_IN_PIXELS - trunkWidth) / 2; trunkY = y + 2 * Field.TREE_WIDTH_IN_PIXELS - trunkHeight; boundingBox = new Rectangle(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_HEIGHT_IN_PIXELS); } @Override public int getPositionX() { return x; } @Override public int getPositionY() { return y; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { g.setColor(trunkColor); g.fillRect(trunkX, trunkY, trunkWidth, trunkHeight); g.setColor(leafColor); g.fillOval(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_WIDTH_IN_PIXELS); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } } ================================================ FILE: ch09/URLConsumer.java ================================================ package ch09; import java.util.Random; /** * A threaded client for our URL producer. Uses a synchronized queue * to safely read a URL for processing. (Our simple demo "processes" * by printing out the ID of this consumer and the url it consumed.) */ public class URLConsumer extends Thread { String consumerID; URLQueue queue; boolean keepWorking; Random delay; /** * Creates a new consumer with the given ID and reference to the shared queue. * * @param id A unique (unenforced) name for this consumer * @param queue The shared queue for storing and distributing URLs */ public URLConsumer(String id, URLQueue queue) { if (queue == null) { throw new IllegalArgumentException("Shared queue cannot be null"); } consumerID = id; this.queue = queue; keepWorking = true; delay = new Random(); } /** * Our working method for the thread. Watches the boolean flag * keepWorking as well as the state of the queue to determine * whether or not to complete the loop. While working, grab a * URL and print it out then repeat. */ public void run() { while (keepWorking || !queue.isEmpty()) { String url = queue.getURL(); if (url != null) { System.out.println(consumerID + " consumed " + url); } else { System.out.println(consumerID + " skipped empty queue"); } try { Thread.sleep(delay.nextInt(1000)); } catch (InterruptedException ie) { System.err.println("Consumer " + consumerID + " interrupted. Quitting."); break; } } } /** * Allow for politely halting this consumer. * Watched in the run() method. * * @see #run() */ public void setKeepWorking(boolean keepWorking) { this.keepWorking = keepWorking; } } ================================================ FILE: ch09/URLDemo.java ================================================ package ch09; /** * Manage multiple producers and consumers to demonstrate how * threads work in tandem. Creates a pair of producers and a * pair of consumers all with access to a shared queue. * * @see URLQueue * @see URLProducer * @see URLConsumer */ public class URLDemo { public static void main(String args[]) { // Create our shared queue object URLQueue queue = new URLQueue(); // Now create some producers with unique names and a reference to our queue URLProducer p1 = new URLProducer("P1", 3, queue); URLProducer p2 = new URLProducer("P2", 3, queue); // And some consumers with their own names and a reference to our queue URLConsumer c1 = new URLConsumer("C1", queue); URLConsumer c2 = new URLConsumer("C2", queue); // Get everyone going! System.out.println("Starting..."); p1.start(); p2.start(); c1.start(); c2.start(); // First wait around for the producers to finish try { p1.join(); p2.join(); } catch (InterruptedException ie) { System.err.println("Interrupted waiting for producers to finish"); } // OK, we know there won't be any more URLs made, so let the consumers // finish once the queue is empty c1.setKeepWorking(false); c2.setKeepWorking(false); try { c1.join(); c2.join(); } catch (InterruptedException ie) { System.err.println("Interrupted waiting for consumers to finish"); } System.out.println("Done"); } } ================================================ FILE: ch09/URLProducer.java ================================================ package ch09; import java.util.Random; /** * Simple producer for use in our multithreaded example. Uses a synchronized queue * to safely store URLs for processing. */ public class URLProducer extends Thread { String producerID; int urlCount; URLQueue queue; Random delay; /** * Create a new producer with the given name. It will produce the * specified number of URLs and store them in the provided queue. * * @param id A unique (unenforced) name for this producer * @param count How many URLs this producer will create before quitting * @param queue The shared, synchronized queue for URLs */ public URLProducer(String id, int count, URLQueue queue) { // Don't even create this producer if a negative count was supplied or there's no queue if (count <= 0) { throw new IllegalArgumentException("Count must be positive"); } if (queue == null) { throw new IllegalArgumentException("Shared queue cannot be null"); } producerID = id; urlCount = count; this.queue = queue; delay = new Random(); } /** * Our working method for the thread. Uses the count supplied to * the constructor to produce a batch of URLs and store them to * the shared queue. To make it a little more interesting, a random * delay is added at the end of each iteration. */ public void run() { for (int i = 1; i <= urlCount; i++) { String url = "https://some.url/at/path/" + i; queue.addURL(producerID + " " + url); System.out.println(producerID + " produced " + url); try { Thread.sleep(delay.nextInt(500)); } catch (InterruptedException ie) { System.err.println("Producer " + producerID + " interrupted. Quitting."); break; } } } } ================================================ FILE: ch09/URLQueue.java ================================================ package ch09; import java.util.LinkedList; /** * A manually synchronized wrapper for a LinkedList. * Allows for safely adding and removing URLs in order. */ public class URLQueue { LinkedList urlQueue = new LinkedList<>(); public synchronized void addURL(String url) { urlQueue.add(url); } public synchronized String getURL() { if (!urlQueue.isEmpty()) { return urlQueue.removeFirst(); } return null; } public boolean isEmpty() { return urlQueue.isEmpty(); } } ================================================ FILE: ch09/game/Apple.java ================================================ package ch09.game; import java.awt.*; /** * Apple * * This class sums up everything we know about the apples our physicists will be lobbing. * We keep the size and weight details and provide a few simple methods for lobbing. We * also provide animation helpers for use with the PlayingField class. */ public class Apple implements GamePiece { public static final int SMALL = 0; public static final int MEDIUM = 1; public static final int LARGE = 2; int size; double diameter; double mass; int centerX, centerY; Physicist myPhysicist; // In game play, apples can be thrown so track their velocities long lastStep; float velocityX, velocityY; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; int scaledLength; // Boundary helper for optimizing collision detection with physicists and trees Rectangle boundingBox; // If we bumped into something, keep a reference to that thing around for cleanup and removal GamePiece collided; /** * Create a default, Medium apple */ public Apple(Physicist owner) { this(owner, MEDIUM); } /** * Create an Apple of the given size */ public Apple(Physicist owner, int size) { myPhysicist = owner; setSize(size); } /** * Sets the size (and dependent properties) of the apple based on the * supplied value which must be one of the size constants. * * @param size one of SMALL, MEDIUM, or LARGE, other values are bounded to SMALL or LARGE */ public void setSize(int size) { if (size < SMALL) { size = SMALL; } if (size > LARGE) { size = LARGE; } this.size = size; switch (size) { case SMALL: diameter = 0.9; mass = 0.5; break; case MEDIUM: diameter = 1.0; mass = 1.0; break; case LARGE: diameter = 1.1; mass = 1.8; break; } // fillOval() used below draws an oval bounded by a box, so figure out the length of the sides. // Since we want a circle, we simply make our box a square so we only need one length. scaledLength = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS + 0.5); boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } public double getDiameter() { return diameter; } @Override public void setPosition(int x, int y) { // Our position is based on the center of the apple, but it will be drawn from the // upper left corner, so figure out the distance of that gap int offset = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS / 2); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { // Make sure our apple will be red, then paint it! g.setColor(Color.RED); g.fillOval(x, y, scaledLength, scaledLength); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece || myPhysicist == otherPiece || collided != null) { // By definition we don't collide with ourselves, our physicist, or with more than one other piece return false; } if (otherPiece instanceof Apple) { // The other piece is an apple, so we can do a simple distance calculation using // the diameters of both apples. Apple otherApple = (Apple) otherPiece; int v = this.y - otherPiece.getPositionY(); // vertical difference int h = this.x - otherPiece.getPositionX(); // horizontal difference double distance = Math.sqrt(v * v + h * h); double myRadius = diameter * Field.APPLE_SIZE_IN_PIXELS / 2; double otherRadius = otherApple.getDiameter() * Field.APPLE_SIZE_IN_PIXELS / 2; if (distance < (myRadius + otherRadius)) { // Since apples track collisions, we'll update the other apple to keep everyone in sync setCollided(otherPiece); otherApple.setCollided(this); return true; } return false; } if (GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox())) { setCollided(otherPiece); return true; } return false; } public GamePiece getCollidedPiece() { return collided; } public void setCollided(GamePiece otherPiece) { this.collided = otherPiece; } public void toss(float angle, float velocity) { lastStep = System.currentTimeMillis(); double radians = angle / 180 * Math.PI; velocityX = (float)(velocity * Math.cos(radians) / mass); // Start with negative velocity since "up" means smaller values of y velocityY = (float)(-velocity * Math.sin(radians) / mass); } public void step() { // Make sure we're moving at all using our lastStep tracker as a sentinel if (lastStep > 0) { // let's apply our gravity long now = System.currentTimeMillis(); float slice = (now - lastStep) / 1000.0f; velocityY = velocityY + (slice * Field.GRAVITY); int newX = (int)(centerX + velocityX); int newY = (int)(centerY + velocityY); setPosition(newX, newY); } } } ================================================ FILE: ch09/game/AppleToss.java ================================================ package ch09.game; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Random; /** * Our apple tossing game. This class extends JFrame to create our * main application window. We can now demonstrate one apple being * tossed. (The ability for the player to aim and throw on demand * will be covered in Chapter 10.) */ public class AppleToss extends JFrame { public static final int FIELD_WIDTH = 800; public static final int FIELD_HEIGHT = 500; Field field = new Field(); Physicist player1 = new Physicist(); ArrayList otherPlayers = new ArrayList<>(); Random random = new Random(); public AppleToss() { // Create our frame super("Apple Toss Game"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(false); setSize(FIELD_WIDTH,FIELD_HEIGHT + 20); // Build the field with our player and some trees setupFieldForOnePlayer(); add(field); } /** * Helper method to return a good x value for a tree so it's not off the left or right edge. * * @return x value within the bounds of the playing field width */ private int goodX() { // at least half the width of the tree plus a few pixels int leftMargin = Field.TREE_WIDTH_IN_PIXELS / 2 + 5; // now find a random number between a left and right margin int rightMargin = FIELD_WIDTH - leftMargin; // And return a random number starting at the left margin return leftMargin + random.nextInt(rightMargin - leftMargin); } /** * Helper method to return a good y value for a tree so it's not off the top or bottom of the screen. * * @return y value within the bounds of the playing field height */ private int goodY() { // at least half the height of the "leaves" plus a few pixels int topMargin = Field.TREE_WIDTH_IN_PIXELS / 2 + 5; // a little higher off the bottom int bottomMargin = FIELD_HEIGHT - Field.TREE_HEIGHT_IN_PIXELS; // And return a random number starting at the top margin but not past the bottom return topMargin + random.nextInt(bottomMargin - topMargin); } /** * A helper method to populate a one player field with target trees. */ private void setupFieldForOnePlayer() { // place our (new) physicist in the lower left corner if (field.physicists.size() == 0) { player1.setPosition(Field.PHYSICIST_SIZE_IN_PIXELS, FIELD_HEIGHT - (int) (Field.PHYSICIST_SIZE_IN_PIXELS * 1.5)); field.physicists.add(player1); player1.setField(field); } // Create some trees for target practice for (int i = field.trees.size(); i < 10; i++) { Tree t = new Tree(); t.setPosition(goodX(), goodY()); // Trees can be close to each other and overlap, but they shouldn't intersect our physicist while(player1.isTouching(t)) { // We do intersect this tree, so let's try again t.setPosition(goodX(), goodY()); System.err.println("Repositioning an intersecting tree..."); } field.trees.add(t); } } public static void main(String args[]) { AppleToss game = new AppleToss(); game.setVisible(true); try { game.player1.setAimingAngle(45.0f); game.field.repaint(); Thread.sleep(1000); game.field.startTossFromPlayer(game.player1); } catch (InterruptedException ie) { System.err.println("Interrupted during initial pause before tossing an apple."); } } } ================================================ FILE: ch09/game/Field.java ================================================ package ch09.game; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * The playing field for our game. Now we can setup some constants for * other game classes to use and create member variables for our player and some trees. */ public class Field extends JComponent implements ActionListener { public static final float GRAVITY = 9.8f; // feet per second per second public static final int STEP = 40; // duration of an animation frame in milliseconds public static final int APPLE_SIZE_IN_PIXELS = 30; public static final int TREE_WIDTH_IN_PIXELS = 60; public static final int TREE_HEIGHT_IN_PIXELS = 2 * TREE_WIDTH_IN_PIXELS; public static final int PHYSICIST_SIZE_IN_PIXELS = 75; public static final int MAX_PHYSICISTS = 5; public static final int MAX_APPLES_PER_PHYSICIST = 3; public static final int MAX_TREES = 12; Color fieldColor = Color.GRAY; // ArrayList was covered in Generics chapter List physicists = Collections.synchronizedList(new ArrayList<>()); List apples = Collections.synchronizedList(new ArrayList<>()); List trees = Collections.synchronizedList(new ArrayList<>()); boolean animating = false; Thread animationThread; Timer animationTimer; protected void paintComponent(Graphics g) { g.setColor(fieldColor); g.fillRect(0,0, getWidth(), getHeight()); for (Physicist p : physicists) { p.draw(g); } for (Tree t : trees) { t.draw(g); } for (Apple a : apples) { a.draw(g); } } public void actionPerformed(ActionEvent event) { if (animating && event.getActionCommand().equals("repaint")) { System.out.println("Timer stepping " + apples.size() + " apples"); for (Apple a : apples) { a.step(); detectCollisions(a); } repaint(); cullFallenApples(); } } /** * Toss an apple from the given physicist using that physicist's aim and force. * Make sure the field is in the animating state. * * @param physicist the player whose apple should be tossed */ public void startTossFromPlayer(Physicist physicist) { if (!animating) { System.out.println("Starting animation!"); animating = true; startAnimation(); } if (animating) { // Check to make sure we have an apple to toss if (physicist.aimingApple != null) { Apple apple = physicist.takeApple(); apple.toss(physicist.aimingAngle, physicist.aimingForce); apples.add(apple); } } } void cullFallenApples() { Iterator iterator = apples.iterator(); while (iterator.hasNext()) { Apple a = iterator.next(); if (a.getPositionY() > 600) { System.out.println("Culling apple"); iterator.remove(); } } if (apples.size() <= 0) { animating = false; if (animationTimer != null && animationTimer.isRunning()) { animationTimer.stop(); } } } void detectCollisions(Apple apple) { // Check for other apples for (Apple a : apples) { if (apple.isTouching(a)) { System.out.println("Touching another apple!"); return; } } // Check for physicists for (Physicist p : physicists) { if (apple.isTouching(p)) { System.out.println("Touching a physicist!"); return; } } // Check for trees for (Tree t : trees) { if (apple.isTouching(t)) { System.out.println("Touching a tree!"); return; } } } void hitPhysicist(Physicist physicist) { // do any scoring or notifications here physicists.remove(physicist); } void hitTree(Tree tree) { // do any scoring or notifications here trees.remove(tree); } void startAnimation() { // Animator myAnimator = new Animator(); // animationThread = new Thread(myAnimator); // animationThread.start(); if (animationTimer == null) { animationTimer = new Timer(STEP, this); animationTimer.setActionCommand("repaint"); animationTimer.setRepeats(true); animationTimer.start(); } else if (!animationTimer.isRunning()) { animationTimer.restart(); } } class Animator implements Runnable { public void run() { while (animating) { System.out.println("Stepping " + apples.size() + " apples"); for (Apple a : apples) { a.step(); detectCollisions(a); } SwingUtilities.invokeLater(new Runnable() { public void run() { Field.this.repaint(); } }); cullFallenApples(); try { Thread.sleep((int)(STEP * 1000)); } catch (InterruptedException ie) { System.err.println("Animation interrupted"); animating = false; } } } } } ================================================ FILE: ch09/game/GamePiece.java ================================================ package ch09.game; import java.awt.*; /** * Interface to hold common elements for our apples, trees, and physicists * From the README: * GamePiece * - methods for positioning on a PlayingField * - methods for Drawing * - methods for detecting a collision with other GamePieces */ public interface GamePiece { /** * Sets the position of the piece on the playing field. * (0,0) is the upper left per standard Java drawing methods. * * @param x * @param y */ void setPosition(int x, int y); /** * Gets the current horizontal position of the piece on the field. * * @return current X position of the piece */ int getPositionX(); /** * Gets the current vertical position of the piece on the field. * * @return current Y position of the piece */ int getPositionY(); /** * Gets the bounding box of this piece. * * @return a Rectangle encompassing the visual elements of the piece */ Rectangle getBoundingBox(); /** * Draws the piece inside the given graphics context. * Do not assume anything about the state of the context. * * @param g */ void draw(Graphics g); /** * Detect a collision with another piece on the field. * By definition, a piece does NOT touch itself (i.e. it won't collide with itself). * * @param otherPiece * @return */ boolean isTouching(GamePiece otherPiece); } ================================================ FILE: ch09/game/GameUtilities.java ================================================ package ch09.game; // collision detection between a circle and a rectangle // https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection import java.awt.*; /** * Utility class with a few helper methods for calculating various collisions and * intersections. */ public class GameUtilities { static boolean isPointInsideBox(int x, int y, Rectangle box) { // Our own custom test. We could of course use box.contains(), // but we can practice some interesting conditional checking here. // Let's test left and right first if (x >= box.x && x <= (box.x + box.width)) { // Our x coordinate is ok, so check our y if (y >= box.y && y <= (box.y + box.height)) { return true; } } // x or y was outside the box, so return false return false; } static boolean doesBoxIntersect(Rectangle box, Rectangle other) { // If any of the four corners of box are inside other, we intersect, so // let's check each one. Happily, that answer doesn't change if more // than one corner is contained in other, so we can return as soon as // we find the first contained corner. // Let's get some local copies of the corner coordinates // to make the call arguments easier to read. int x1 = box.x; int y1 = box.y; int x2 = x1 + box.width; int y2 = y1 + box.height; if (isPointInsideBox(x1, y1, other)) { // upper left return true; } else if (isPointInsideBox(x1, y2, other)) { // lower left return true; } else if (isPointInsideBox(x2, y1, other)) { // upper right return true; } else if (isPointInsideBox(x2, y2, other)) { // lower right return true; } // No box points in other so no intersection return false; } /** * Given two rectangles, do the overlap at all? This includes one box being * completely contained by the other box. * * @param box1 a box to test, order does not matter * @param box2 the other box * @return true if the boxes overlap, false otherwise */ public static boolean doBoxesIntersect(Rectangle box1, Rectangle box2) { // Another custom test. We could of course use box1.intersects(box2) // but we can practice method calls and some boolean logic here. if (doesBoxIntersect(box1, box2)) { // At least one of box1's points must be inside box2 return true; } else if (doesBoxIntersect(box2, box1)) { // None of box1's points were in box2, but at least one of box2's points are inside box1 return true; } // No intersections in either direction return false; } } ================================================ FILE: ch09/game/Physicist.java ================================================ package ch09.game; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import static java.awt.Color.*; /** * Our player class. A physicist has an apple that they can throw in addition * to implementing the basic methods for GamePiece. We can also set a custom * color in case you build on the game and allow multiple physicists to be * on the screen at the same time. */ public class Physicist implements GamePiece, ActionListener { Color color; int centerX, centerY; Apple aimingApple; float aimingAngle; float aimingForce; Field field; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; // Boundary helpers private final int physicistHeight = (int)(1.5 * Field.PHYSICIST_SIZE_IN_PIXELS); private Rectangle boundingBox; /** * Create a default, blue physicist */ public Physicist() { this(BLUE); } /** * Create a Physicist of the given color */ public Physicist(Color color) { setColor(color); aimingAngle = 90.0f; aimingForce = 50.0f; getNewApple(); } public void setAimingAngle(Float angle) { aimingAngle = angle; } public void setAimingForce(Float force) { if (force < 0) { force = 0.0f; } aimingForce = force; } /** * Sets the color for the physicist. * * @param color */ public void setColor(Color color) { this.color = color; } @Override public void setPosition(int x, int y) { // Our position is based on the center of our dome, // but it will be drawn from the upper left corner. // Figure out the distance of that gap int offset = (int)(Field.PHYSICIST_SIZE_IN_PIXELS / 2.0f); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, physicistHeight); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } /** * Sets the active field once our physicist is being displayed. * * @param field Active game field */ public void setField(Field field) { this.field = field; } /** * Take the current apple away from the physicist. Returns the apple for * use in animating on the field. Note that if there is no current apple being * aimed, null is returned. * * @return the current apple (if any) being aimed */ public Apple takeApple() { Apple myApple = aimingApple; aimingApple = null; return myApple; } /** * Get a new apple ready if we need one. Do not discard an existing apple. */ public void getNewApple() { // Don't drop the current apple if we already have one! if (aimingApple == null) { aimingApple = new Apple(this); } } @Override public void draw(Graphics g) { // Make sure our physicist is the right color, then draw the semi-circle and box g.setColor(color); g.fillArc(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS, 0, 180); g.fillRect(x, centerY, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS); // Do we have an apple to draw as well? if (aimingApple != null) { // Yes. Position the center of the apple on the edge of our semi-circle. // Use the current aimingAngle to determine where on the edge. double angleInRadians = Math.toRadians(aimingAngle); double radius = Field.PHYSICIST_SIZE_IN_PIXELS / 2.0; int aimingX = centerX + (int)(Math.cos(angleInRadians) * radius); int aimingY = centerY - (int)(Math.sin(angleInRadians) * radius); aimingApple.setPosition(aimingX, aimingY); aimingApple.draw(g); } } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } @Override public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("New Apple")) { getNewApple(); if (field != null) { field.repaint(); } } } } ================================================ FILE: ch09/game/Tree.java ================================================ package ch09.game; import java.awt.*; /** * An obstacle for our game. Trees includ a simple circle and rectangle shape. * They are built using sizes determined by the constants in the Field class. */ public class Tree implements GamePiece { int x, y; // Drawing helpers private Color leafColor = Color.GREEN.darker(); private Color trunkColor = new Color(101, 67, 33); private int trunkWidth = (int)(Field.TREE_WIDTH_IN_PIXELS * 0.2); private int trunkHeight = (int)(Field.TREE_WIDTH_IN_PIXELS * 1.1); private int trunkX, trunkY; // Boundary helpers private Rectangle boundingBox; @Override public void setPosition(int x, int y) { this.x = x; this.y = y; trunkX = x + (Field.TREE_WIDTH_IN_PIXELS - trunkWidth) / 2; trunkY = y + 2 * Field.TREE_WIDTH_IN_PIXELS - trunkHeight; boundingBox = new Rectangle(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_HEIGHT_IN_PIXELS); } @Override public int getPositionX() { return x; } @Override public int getPositionY() { return y; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { g.setColor(trunkColor); g.fillRect(trunkX, trunkY, trunkWidth, trunkHeight); g.setColor(leafColor); g.fillOval(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_WIDTH_IN_PIXELS); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } } ================================================ FILE: ch10/ActionDemo1.java ================================================ package ch10; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * A simple, classic demonstration of event handling. Create a frame * with a button and a label. As the button is pressed, a counter * shown in the label is incremented. */ public class ActionDemo1 extends JFrame implements ActionListener { int counterValue = 0; JLabel counterLabel; public ActionDemo1() { super( "ActionEvent Counter Demo" ); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new FlowLayout()); setSize( 300, 180 ); counterLabel = new JLabel("Count: 0", JLabel.CENTER ); add(counterLabel); JButton incrementer = new JButton("Increment"); incrementer.addActionListener(this); add(incrementer); } public void actionPerformed(ActionEvent e) { counterValue++; counterLabel.setText("Count: " + counterValue); } public static void main( String[] args ) { ActionDemo1 demo = new ActionDemo1(); demo.setVisible(true); } } ================================================ FILE: ch10/ActionDemo2.java ================================================ package ch10; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * A simple, classic demonstration of event handling. Create a frame * with a button and a label. As the button is pressed, a counter * shown in the label is incremented. * * This second variation uses a separate class to handle the action * events rather than implementing the ActionListener interface * directly as in ActionDemo1. */ public class ActionDemo2 { public static void main( String[] args ) { JFrame frame = new JFrame( "ActionListener Demo" ); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new FlowLayout()); frame.setSize( 300, 180 ); JLabel label = new JLabel("Results go here", JLabel.CENTER ); ActionCommandHelper helper = new ActionCommandHelper(label); JButton simpleButton = new JButton("Button"); simpleButton.addActionListener(helper); JTextField simpleField = new JTextField(10); simpleField.addActionListener(helper); frame.add(simpleButton); frame.add(simpleField); frame.add(label); frame.setVisible( true ); } } /** * Helper class to show the command property of any ActionEvent in a given label. */ class ActionCommandHelper implements ActionListener { JLabel resultLabel; public ActionCommandHelper(JLabel label) { resultLabel = label; } public void actionPerformed(ActionEvent ae) { resultLabel.setText(ae.getActionCommand()); } } ================================================ FILE: ch10/BorderLayoutDemo.java ================================================ package ch10; import java.awt.*; import javax.swing.*; /** * A basic demonstration of the BorderLayout with higlighted * areas in different colors. */ public class BorderLayoutDemo { public static void main( String[] args ) { JFrame frame = new JFrame("BorderLayout Demo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 200); JLabel northLabel = new JLabel("Top - North", JLabel.CENTER); JLabel southLabel = new JLabel("Bottom - South", JLabel.CENTER); JLabel eastLabel = new JLabel("Right - East", JLabel.CENTER); JLabel westLabel = new JLabel("Left - West", JLabel.CENTER); JLabel centerLabel = new JLabel("Center (everything else)", JLabel.CENTER); // Color the labels so we can see their boundaries better northLabel.setOpaque(true); northLabel.setBackground(Color.GREEN); southLabel.setOpaque(true); southLabel.setBackground(Color.GREEN); eastLabel.setOpaque(true); eastLabel.setBackground(Color.RED); westLabel.setOpaque(true); westLabel.setBackground(Color.RED); centerLabel.setOpaque(true); centerLabel.setBackground(Color.YELLOW); frame.add(northLabel, BorderLayout.NORTH); frame.add(southLabel, BorderLayout.SOUTH); frame.add(eastLabel, BorderLayout.EAST); frame.add(westLabel, BorderLayout.WEST); frame.add(centerLabel, BorderLayout.CENTER); frame.setVisible(true); } } ================================================ FILE: ch10/Buttons.java ================================================ package ch10; import javax.swing.*; import java.awt.*; /** * A very simple button placed on a frame. This button is * not connected to any listener so it will "press" when * clicked but action is taken in response. */ public class Buttons { public static void main( String[] args ) { JFrame frame = new JFrame( "JButton Examples" ); frame.setLayout(new FlowLayout()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize( 300, 200 ); JButton basic = new JButton("Try me!"); frame.add(basic); frame.setVisible( true ); } } ================================================ FILE: ch10/HelloJavaAgain.java ================================================ package ch10; import javax.swing.*; /** * A simple label placed on a frame. */ public class HelloJavaAgain { public static void main( String[] args ) { JFrame frame = new JFrame( "Hello, Java!" ); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize( 300, 300 ); JLabel label = new JLabel("Hello, Java!", JLabel.CENTER ); frame.add(label); frame.setVisible( true ); } } ================================================ FILE: ch10/HelloMouse.java ================================================ package ch10; import java.awt.*; import javax.swing.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; /** * A quick demo of how mouse events work. Clicking around the * frame will move the label. * * Note that "mouse" events are the up, down, and click actions * of mouse buttons. If you want to catch the mouse moving or dragging, * those are handled by the MouseMotionListener interface. */ public class HelloMouse extends JFrame implements MouseListener { JLabel label; public HelloMouse() { super("MouseEvent Demo"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(null); setSize( 300, 100 ); label = new JLabel("Hello, Mouse!", JLabel.CENTER ); label.setOpaque(true); label.setBackground(Color.YELLOW); label.setSize(100,20); label.setLocation(100,100); add(label); getContentPane().addMouseListener(this); } public void mouseClicked(MouseEvent e) { label.setLocation(e.getX(), e.getY()); } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public static void main( String[] args ) { HelloMouse frame = new HelloMouse(); frame.setVisible( true ); } } ================================================ FILE: ch10/HelloMouseHelper.java ================================================ package ch10; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.*; /** * A variation on HelloMouse with a separate class implementing * the mouse event handler. Note that we have to pass a reference * to the label we wish to affect when creating the event * helper. */ public class HelloMouseHelper { public static void main( String[] args ) { JFrame frame = new JFrame( "MouseAdapter Demo" ); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(null); frame.setSize( 300, 300 ); JLabel label = new JLabel("Hello, Mouse!", JLabel.CENTER ); label.setOpaque(true); label.setBackground(Color.YELLOW); label.setSize(100,20); label.setLocation(100,100); frame.add(label); LabelMover mover = new LabelMover(label); frame.getContentPane().addMouseListener(mover); frame.setVisible( true ); } } /** * Helper class to move a label to the position of a mouse click. */ class LabelMover extends MouseAdapter { JLabel labelToMove; public LabelMover(JLabel label) { labelToMove = label; } public void mouseClicked(MouseEvent e) { labelToMove.setLocation(e.getX(), e.getY()); } } ================================================ FILE: ch10/Labels.java ================================================ package ch10; import javax.swing.*; import java.awt.*; /** * A simple demo of several label options with variations * on color, alignment, and icons. */ public class Labels { // To make this simple example run from inside an IDE like IntelliJ IDEA, // set this path to match where you unzipped the book's projects. static final String PROJECT_PATH = "/Users/work/LearningJava5e"; public static void main( String[] args ) { JFrame frame = new JFrame( "JLabel Examples" ); frame.setLayout(new FlowLayout()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize( 300, 300 ); JLabel basic = new JLabel("Default Label"); basic.setOpaque(true); basic.setBackground(Color.YELLOW); JLabel another = new JLabel("Another Label"); another.setOpaque(true); another.setBackground(Color.GREEN); JLabel simple = new JLabel("A Simple Label"); simple.setOpaque(true); simple.setBackground(Color.WHITE); JLabel standard = new JLabel("A Standard Label"); standard.setOpaque(true); standard.setBackground(Color.ORANGE); JLabel centered = new JLabel("Centered Text", JLabel.CENTER); centered.setPreferredSize(new Dimension(150, 24)); centered.setOpaque(true); centered.setBackground(Color.WHITE); JLabel times = new JLabel("Times Roman"); times.setOpaque(true); times.setBackground(Color.WHITE); times.setFont(new Font("TimesRoman", Font.BOLD, 18)); JLabel styled = new JLabel("Some styling is also allowed"); styled.setOpaque(true); styled.setBackground(Color.WHITE); JLabel icon = new JLabel("Verified", new ImageIcon(PROJECT_PATH + "/ch10/check.png"), JLabel.LEFT); icon.setOpaque(true); icon.setBackground(Color.WHITE); frame.add(basic); frame.add(another); frame.add(simple); frame.add(standard); frame.add(centered); frame.add(times); frame.add(styled); frame.add(icon); frame.setVisible( true ); } } ================================================ FILE: ch10/MenuDemo.java ================================================ package ch10; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * Basic demonstration of creating menus and catching events * from selected menu items. */ public class MenuDemo extends JFrame implements ActionListener { JLabel resultsLabel; public MenuDemo() { super( "JMenu Demo" ); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new FlowLayout()); setSize( 300, 180 ); resultsLabel = new JLabel("Click a menu item!" ); add(resultsLabel); // Now let's create a couple menus and populate them JMenu fileMenu = new JMenu("File"); JMenuItem saveItem = new JMenuItem("Save"); saveItem.addActionListener(this); fileMenu.add(saveItem); JMenuItem quitItem = new JMenuItem("Quit"); quitItem.addActionListener(this); fileMenu.add(quitItem); JMenu editMenu = new JMenu("Edit"); JMenuItem cutItem = new JMenuItem("Cut"); cutItem.addActionListener(this); editMenu.add(cutItem); JMenuItem copyItem = new JMenuItem("Copy"); copyItem.addActionListener(this); editMenu.add(copyItem); JMenuItem pasteItem = new JMenuItem("Paste"); pasteItem.addActionListener(this); editMenu.add(pasteItem); // And finally build a JMenuBar for the application JMenuBar mainBar = new JMenuBar(); mainBar.add(fileMenu); mainBar.add(editMenu); setJMenuBar(mainBar); } public void actionPerformed(ActionEvent event) { resultsLabel.setText("Menu selected: " + event.getActionCommand()); } public static void main(String args[]) { MenuDemo demo = new MenuDemo(); demo.setVisible(true); } } ================================================ FILE: ch10/ModalDemo.java ================================================ package ch10; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * A demonstration of showing modal dialogs. Pressing * the Go button will popup a new window which must be * dismissed before interacting with the main application again. */ public class ModalDemo extends JFrame implements ActionListener { JLabel modalLabel; public ModalDemo() { super( "Modal Dialog Demo" ); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new FlowLayout()); setSize( 300, 180 ); modalLabel = new JLabel("Press 'Go' to show the popup!", JLabel.CENTER ); add(modalLabel); JButton goButton = new JButton("Go"); goButton.addActionListener(this); add(goButton); } public void actionPerformed(ActionEvent ae) { JOptionPane.showMessageDialog(this, "We're going!", "Alert", JOptionPane.INFORMATION_MESSAGE); modalLabel.setText("Go pressed! Press again if you like."); } public static void main(String args[]) { ModalDemo demo = new ModalDemo(); demo.setVisible(true); } } ================================================ FILE: ch10/NestedPanelDemo.java ================================================ package ch10; import javax.swing.*; import java.awt.*; /** * An alternate way to arrange complex UIs. Rather than * use more flexible (but complicated) layout managers, * you can nest containers each with simpler managers. */ public class NestedPanelDemo { public static void main( String[] args ) { JFrame frame = new JFrame("Nested Panel Demo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 200); // Create the text area and go ahead and add it to the center JTextArea messageArea = new JTextArea(); frame.add(messageArea, BorderLayout.CENTER); // Create the button container //JPanel buttonPanel = new JPanel(new FlowLayout()); JPanel buttonPanel = new JPanel(new GridLayout(1,0)); // Create the buttons JButton sendButton = new JButton("Send"); JButton saveButton = new JButton("Save"); JButton resetButton = new JButton("Reset"); JButton cancelButton = new JButton("Cancel"); // Add the buttons to their container buttonPanel.add(sendButton); buttonPanel.add(saveButton); buttonPanel.add(resetButton); buttonPanel.add(cancelButton); // And finally, add the button container to the bottom of the app frame.add(buttonPanel, BorderLayout.SOUTH); frame.setVisible(true); } } ================================================ FILE: ch10/PhoneGridDemo.java ================================================ package ch10; import javax.swing.*; import java.awt.*; /** * Demo of the GridLayout manager used to create a * dial pad like you might find on a phone. */ public class PhoneGridDemo { public static void main( String[] args ) { JFrame frame = new JFrame("Nested Panel Demo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(200, 300); // Create the phone pad container JPanel phonePad = new JPanel(new GridLayout(4,3)); // Create and add the 12 buttons, top-left to bottom-right phonePad.add(new JButton("1")); phonePad.add(new JButton("2")); phonePad.add(new JButton("3")); phonePad.add(new JButton("4")); phonePad.add(new JButton("5")); phonePad.add(new JButton("6")); phonePad.add(new JButton("7")); phonePad.add(new JButton("8")); phonePad.add(new JButton("9")); phonePad.add(new JButton("*")); phonePad.add(new JButton("0")); phonePad.add(new JButton("#")); // And finally, add the pad to the center of the app frame.add(phonePad, BorderLayout.CENTER); frame.setVisible(true); } } ================================================ FILE: ch10/ProgressDemo.java ================================================ package ch10; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * A multithreaded example showing how to safely update Swing components * from a background thread. The ProgressPretender class below slowly * counts up to 100 and keeps a JLabel updated with the current value. */ public class ProgressDemo { public static void main( String[] args ) { JFrame frame = new JFrame( "SwingUtilities 'invoke' Demo" ); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new FlowLayout()); frame.setSize( 300, 180 ); JLabel label = new JLabel("Download Progress Goes Here!", JLabel.CENTER ); Thread pretender = new Thread(new ProgressPretender(label)); JButton simpleButton = new JButton("Start"); simpleButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { simpleButton.setEnabled(false); pretender.start(); } }); JLabel checkLabel = new JLabel("Can you still type?"); JTextField checkField = new JTextField(10); frame.add(label); frame.add(simpleButton); frame.add(checkLabel); frame.add(checkField); frame.setVisible( true ); } } /** * Simulated worker that updates a provided JLabel * with the work "progress". In this simulation, we just * count from 0 to 100 with a one-second delay between * each step. */ class ProgressPretender implements Runnable { JLabel label; int progress; public ProgressPretender(JLabel label) { this.label = label; progress = 0; } public void run() { while (progress <= 100) { SwingUtilities.invokeLater(new Runnable() { public void run() { label.setText(progress + "%"); } }); try { Thread.sleep(1000); } catch (InterruptedException ie) { System.err.println("Someone interrupted us. Skipping download."); break; } progress++; } } } ================================================ FILE: ch10/TextInputs.java ================================================ package ch10; import javax.swing.*; import java.awt.*; /** * Some example text inputs including a text area embedded in a * JScrollPane. */ public class TextInputs { public static void main( String[] args ) { JFrame frame = new JFrame( "JTextField Examples" ); frame.setLayout(new FlowLayout()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize( 400, 300 ); JLabel nameLabel = new JLabel("Name:"); JTextField nameField = new JTextField(10); JLabel emailLabel = new JLabel("Email:"); JTextField emailField = new JTextField(24); JLabel bodyLabel = new JLabel("Body:"); JTextArea bodyArea = new JTextArea(10,30); bodyArea.setLineWrap(true); bodyArea.setWrapStyleWord(true); JScrollPane bodyScroller = new JScrollPane(bodyArea); bodyScroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); bodyScroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); frame.add(nameLabel); frame.add(nameField); frame.add(emailLabel); frame.add(emailField); frame.add(bodyLabel); frame.add(bodyScroller); frame.setVisible( true ); } } ================================================ FILE: ch10/Widget.java ================================================ package ch10; import javax.swing.*; import java.awt.*; /** * A helper class aimed at making it easier to try out * the many Swing components in jshell. A small frame will * show up once the Widget class is imported into jshell * and a new instance is created. Be sure to keep a variable * for the new object! * *
 * jshell> import javax.swing.*
 *
 * jshell> import ch10.Widget
 * 
 * jshell> Widget w = new Widget("My demo widget")
 * w ==> ch10.Widget[frame0,0,23,300x300,layout=java.awt.B ... tPaneCheckingEnabled=true]
 * 
 * jshell> w.add(new JLabel("Hi"))
 * $5 ==> javax.swing.JLabel[,0,0,0x0,invalid,alignmentX=0 ... rticalTextPosition=CENTER]
 * 
*/ public class Widget extends JFrame { public Widget() { this("jshell GUI Widget"); } public Widget(String title) { super(title); setLayout(new FlowLayout()); setSize(300,300); setVisible(true); } @Override public Component add(Component comp) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { getContentPane().add(comp); getContentPane().doLayout(); } }); return comp; } @Override public void remove(Component comp) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { getContentPane().remove(comp); getContentPane().doLayout(); getContentPane().repaint(); } }); } public void reset() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { getContentPane().removeAll(); getContentPane().doLayout(); getContentPane().repaint(); } }); } } ================================================ FILE: ch10/game/Apple.java ================================================ package ch10.game; import java.awt.*; /** * Apple * * This class sums up everything we know about the apples our physicists will be lobbing. * We keep the size and weight details and provide a few simple methods for lobbing. We * also provide animation helpers for use with the PlayingField class. */ public class Apple implements GamePiece { public static final int SMALL = 0; public static final int MEDIUM = 1; public static final int LARGE = 2; int size; double diameter; double mass; int centerX, centerY; Physicist myPhysicist; // In game play, apples can be thrown so track their velocities long lastStep; float velocityX, velocityY; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; int scaledLength; // Boundary helper for optimizing collision detection with physicists and trees Rectangle boundingBox; // If we bumped into something, keep a reference to that thing around for cleanup and removal GamePiece collided; /** * Create a default, Medium apple */ public Apple(Physicist owner) { this(owner, MEDIUM); } /** * Create an Apple of the given size */ public Apple(Physicist owner, int size) { myPhysicist = owner; setSize(size); } /** * Sets the size (and dependent properties) of the apple based on the * supplied value which must be one of the size constants. * * @param size one of SMALL, MEDIUM, or LARGE, other values are bounded to SMALL or LARGE */ public void setSize(int size) { if (size < SMALL) { size = SMALL; } if (size > LARGE) { size = LARGE; } this.size = size; switch (size) { case SMALL: diameter = 0.9; mass = 0.5; break; case MEDIUM: diameter = 1.0; mass = 1.0; break; case LARGE: diameter = 1.1; mass = 1.8; break; } // fillOval() used below draws an oval bounded by a box, so figure out the length of the sides. // Since we want a circle, we simply make our box a square so we only need one length. scaledLength = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS + 0.5); boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } public double getDiameter() { return diameter; } @Override public void setPosition(int x, int y) { // Our position is based on the center of the apple, but it will be drawn from the // upper left corner, so figure out the distance of that gap int offset = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS / 2); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { // Make sure our apple will be red, then paint it! g.setColor(Color.RED); g.fillOval(x, y, scaledLength, scaledLength); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece || myPhysicist == otherPiece || collided != null) { // By definition we don't collide with ourselves, our physicist, or with more than one other piece return false; } if (otherPiece instanceof Apple) { // The other piece is an apple, so we can do a simple distance calculation using // the diameters of both apples. Apple otherApple = (Apple) otherPiece; int v = this.y - otherPiece.getPositionY(); // vertical difference int h = this.x - otherPiece.getPositionX(); // horizontal difference double distance = Math.sqrt(v * v + h * h); double myRadius = diameter * Field.APPLE_SIZE_IN_PIXELS / 2; double otherRadius = otherApple.getDiameter() * Field.APPLE_SIZE_IN_PIXELS / 2; if (distance < (myRadius + otherRadius)) { // Since apples track collisions, we'll update the other apple to keep everyone in sync setCollided(otherPiece); otherApple.setCollided(this); return true; } return false; } if (GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox())) { setCollided(otherPiece); return true; } return false; } public GamePiece getCollidedPiece() { return collided; } public void setCollided(GamePiece otherPiece) { this.collided = otherPiece; } public void toss(float angle, float velocity) { lastStep = System.currentTimeMillis(); double radians = angle / 180 * Math.PI; velocityX = (float)(velocity * Math.cos(radians) / mass); // Start with negative velocity since "up" means smaller values of y velocityY = (float)(-velocity * Math.sin(radians) / mass); } public void step() { // Make sure we're moving at all using our lastStep tracker as a sentinel if (lastStep > 0) { // let's apply our gravity long now = System.currentTimeMillis(); float slice = (now - lastStep) / 1000.0f; velocityY = velocityY + (slice * Field.GRAVITY); int newX = (int)(centerX + velocityX); int newY = (int)(centerY + velocityY); setPosition(newX, newY); } } } ================================================ FILE: ch10/game/AppleToss.java ================================================ package ch10.game; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Random; /** * Our apple tossing game. This class extends JFrame to create our * main application window. With the new Swing components discussed * in this chapter, we can now make a fully functioning physicist * who can aim and toss apples. */ public class AppleToss extends JFrame { public static final int SCORE_HEIGHT = 30; public static final int CONTROL_WIDTH = 300; public static final int CONTROL_HEIGHT = 40; public static final int FIELD_WIDTH = 3 * CONTROL_WIDTH; public static final int FIELD_HEIGHT = 2 * CONTROL_WIDTH; public static final float FORCE_SCALE = 0.7f; GridBagLayout gameLayout = new GridBagLayout(); GridBagConstraints gameConstraints = new GridBagConstraints(); JPanel gamePane = new JPanel(gameLayout); Field field = new Field(); Physicist player1 = new Physicist(); ArrayList otherPlayers = new ArrayList<>(); Random random = new Random(); public AppleToss() { // Create our frame super("Apple Toss Game"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(false); // Build the field with our player and some trees setupFieldForOnePlayer(); // Setup the grid we'll use to layout our various components gameLayout.columnWidths = new int[] { CONTROL_WIDTH, CONTROL_WIDTH, CONTROL_WIDTH }; gameLayout.rowHeights = new int[] { SCORE_HEIGHT, FIELD_HEIGHT, CONTROL_HEIGHT, CONTROL_HEIGHT }; // Now build and add those components at the desired position JLabel player1score = new JLabel(" Player 1: 0"); field.scoreLabels[1] = player1score; gamePane.add(player1score, buildConstraints(0, 0, 1, 1)); gamePane.add(buildRestartButton(), buildConstraints(0, 2, 1, 1)); gamePane.add(field, buildConstraints(1, 0, 1, 3)); gamePane.add(buildAngleControl(), buildConstraints(2, 0, 1, 1)); gamePane.add(buildForceControl(), buildConstraints(2, 1, 1, 1)); gamePane.add(buildTossButton(), buildConstraints(2, 2, 2, 1)); gamePane.add(new JLabel("Angle", JLabel.CENTER), buildConstraints(3, 0, 1, 1)); gamePane.add(new JLabel("Force", JLabel.CENTER), buildConstraints(3, 1, 1, 1)); // replace the frame's content with our game setContentPane(gamePane); // And set the correct size + a buffer for any OS frame title setSize(FIELD_WIDTH,SCORE_HEIGHT + FIELD_HEIGHT + (2 * CONTROL_HEIGHT) + 20); } private GridBagConstraints buildConstraints(int row, int col, int rowspan, int colspan) { gameConstraints.fill = GridBagConstraints.BOTH; gameConstraints.gridy = row; gameConstraints.gridx = col; gameConstraints.gridheight = rowspan; gameConstraints.gridwidth = colspan; return gameConstraints; } private JButton buildRestartButton() { JButton button = new JButton("Play Again"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { setupFieldForOnePlayer(); field.repaint(); } }); return button; } private JSlider buildAngleControl() { JSlider slider = new JSlider(0,180); slider.setInverted(true); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { player1.setAimingAngle((float)slider.getValue()); field.repaint(); } }); return slider; } private JSlider buildForceControl() { JSlider slider = new JSlider(); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { player1.setAimingForce(slider.getValue() * FORCE_SCALE); } }); return slider; } private JButton buildTossButton() { JButton button = new JButton("Toss"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { field.startTossFromPlayer(player1); } }); return button; } /** * Helper method to return a good x value for a tree so it's not off the left or right edge. * * @return x value within the bounds of the playing field width */ private int goodX() { // at least half the width of the tree plus a few pixels int leftMargin = Field.TREE_WIDTH_IN_PIXELS / 2 + 5; // now find a random number between a left and right margin int rightMargin = FIELD_WIDTH - leftMargin; // And return a random number starting at the left margin return leftMargin + random.nextInt(rightMargin - leftMargin); } /** * Helper method to return a good y value for a tree so it's not off the top or bottom of the screen. * * @return y value within the bounds of the playing field height */ private int goodY() { // at least half the height of the "leaves" plus a few pixels int topMargin = Field.TREE_WIDTH_IN_PIXELS / 2 + 5; // a little higher off the bottom int bottomMargin = FIELD_HEIGHT - Field.TREE_HEIGHT_IN_PIXELS; // And return a random number starting at the top margin but not past the bottom return topMargin + random.nextInt(bottomMargin - topMargin); } /** * A helper method to populate a one player field with target trees. */ private void setupFieldForOnePlayer() { // place our (new) physicist in the lower left corner if (field.physicists.size() == 0) { player1.setPosition(Field.PHYSICIST_SIZE_IN_PIXELS, FIELD_HEIGHT - (int) (Field.PHYSICIST_SIZE_IN_PIXELS * 1.5)); field.physicists.add(player1); player1.setField(field); } // Reset the score for our sole player field.resetScore(1); // Create some trees for target practice for (int i = field.trees.size(); i < 10; i++) { Tree t = new Tree(); t.setPosition(goodX(), goodY()); // Trees can be close to each other and overlap, but they shouldn't intersect our physicist while(player1.isTouching(t)) { // We do intersect this tree, so let's try again t.setPosition(goodX(), goodY()); System.err.println("Repositioning an intersecting tree..."); } field.trees.add(t); } } public static void main(String args[]) { AppleToss game = new AppleToss(); game.setVisible(true); } } ================================================ FILE: ch10/game/Field.java ================================================ package ch10.game; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * The playing field for our game. Now we can setup some constants for * other game classes to use and create member variables for our player and some trees. */ public class Field extends JComponent implements ActionListener { public static final float GRAVITY = 9.8f; // feet per second per second public static final int STEP = 40; // duration of an animation frame in milliseconds public static final int APPLE_SIZE_IN_PIXELS = 30; public static final int TREE_WIDTH_IN_PIXELS = 60; public static final int TREE_HEIGHT_IN_PIXELS = 2 * TREE_WIDTH_IN_PIXELS; public static final int PHYSICIST_SIZE_IN_PIXELS = 75; public static final int MAX_PHYSICISTS = 5; public static final int MAX_APPLES_PER_PHYSICIST = 3; public static final int MAX_TREES = 12; Color fieldColor = Color.GRAY; // Keep track of our own score and also make room for multiple players int myScore = 0; String[] scores = new String[3]; JLabel[] scoreLabels = new JLabel[3]; List physicists = Collections.synchronizedList(new ArrayList<>()); List apples = Collections.synchronizedList(new ArrayList<>()); List trees = Collections.synchronizedList(new ArrayList<>()); boolean animating = false; Thread animationThread; Timer animationTimer; protected void paintComponent(Graphics g) { g.setColor(fieldColor); g.fillRect(0,0, getWidth(), getHeight()); for (Physicist p : physicists) { p.draw(g); } for (Tree t : trees) { t.draw(g); } for (Apple a : apples) { a.draw(g); } } public void actionPerformed(ActionEvent event) { if (animating && event.getActionCommand().equals("repaint")) { System.out.println("Timer stepping " + apples.size() + " apples"); for (Apple a : apples) { a.step(); detectCollisions(a); } repaint(); cullFallenApples(); } } /** * Toss an apple from the given physicist using that physicist's aim and force. * Make sure the field is in the animating state. * * @param physicist the player whose apple should be tossed */ public void startTossFromPlayer(Physicist physicist) { if (!animating) { System.out.println("Starting animation!"); animating = true; startAnimation(); } if (animating) { // Check to make sure we have an apple to toss if (physicist.aimingApple != null) { Apple apple = physicist.takeApple(); apple.toss(physicist.aimingAngle, physicist.aimingForce); apples.add(apple); Timer appleLoader = new Timer(800, physicist); appleLoader.setActionCommand("New Apple"); appleLoader.setRepeats(false); appleLoader.start(); } } } void cullFallenApples() { Iterator iterator = apples.iterator(); while (iterator.hasNext()) { Apple a = iterator.next(); if (a.getCollidedPiece() != null) { GamePiece otherPiece = a.getCollidedPiece(); if (otherPiece instanceof Physicist) { hitPhysicist((Physicist) otherPiece); } else if (otherPiece instanceof Tree) { hitTree((Tree) otherPiece); } // Remove ourselves. If the other piece we hit was an apple, leave it alone. // It will be removed when the iterator comes to it. iterator.remove(); } else if (a.getPositionY() > 600) { System.out.println("Culling apple"); iterator.remove(); } } if (apples.size() <= 0) { animating = false; if (animationTimer != null && animationTimer.isRunning()) { animationTimer.stop(); } } } void detectCollisions(Apple apple) { // Check for other apples for (Apple a : apples) { if (apple.isTouching(a)) { System.out.println("Touching another apple!"); return; } } // Check for physicists for (Physicist p : physicists) { if (apple.isTouching(p)) { System.out.println("Touching a physicist!"); return; } } // Check for trees for (Tree t : trees) { if (apple.isTouching(t)) { System.out.println("Touching a tree!"); return; } } } void hitPhysicist(Physicist physicist) { // do any scoring or notifications here physicists.remove(physicist); } void hitTree(Tree tree) { // do any scoring or notifications here myScore += 10; trees.remove(tree); setScore(1, String.valueOf(myScore)); } public void resetScore(int playerNumber) { myScore = 0; setScore(playerNumber, "0"); } public String getScore(int playerNumber) { return scores[playerNumber]; } public void setScore(int playerNumber, String score) { scores[playerNumber] = score; SwingUtilities.invokeLater(new Runnable() { public void run() { String newScore = " Player " + playerNumber + ": " + score; scoreLabels[playerNumber].setText(newScore); } }); } void startAnimation() { // Animator myAnimator = new Animator(); // animationThread = new Thread(myAnimator); // animationThread.start(); if (animationTimer == null) { animationTimer = new Timer(STEP, this); animationTimer.setActionCommand("repaint"); animationTimer.setRepeats(true); animationTimer.start(); } else if (!animationTimer.isRunning()) { animationTimer.restart(); } } class Animator implements Runnable { public void run() { while (animating) { System.out.println("Stepping " + apples.size() + " apples"); for (Apple a : apples) { a.step(); detectCollisions(a); } SwingUtilities.invokeLater(new Runnable() { public void run() { Field.this.repaint(); } }); cullFallenApples(); try { Thread.sleep((int)(STEP * 1000)); } catch (InterruptedException ie) { System.err.println("Animation interrupted"); animating = false; } } } } } ================================================ FILE: ch10/game/GamePiece.java ================================================ package ch10.game; import java.awt.*; /** * Interface to hold common elements for our apples, trees, and physicists * From the README: * GamePiece * - methods for positioning on a PlayingField * - methods for Drawing * - methods for detecting a collision with other GamePieces */ public interface GamePiece { /** * Sets the position of the piece on the playing field. * (0,0) is the upper left per standard Java drawing methods. * * @param x * @param y */ void setPosition(int x, int y); /** * Gets the current horizontal position of the piece on the field. * * @return current X position of the piece */ int getPositionX(); /** * Gets the current vertical position of the piece on the field. * * @return current Y position of the piece */ int getPositionY(); /** * Gets the bounding box of this piece. * * @return a Rectangle encompassing the visual elements of the piece */ Rectangle getBoundingBox(); /** * Draws the piece inside the given graphics context. * Do not assume anything about the state of the context. * * @param g */ void draw(Graphics g); /** * Detect a collision with another piece on the field. * By definition, a piece does NOT touch itself (i.e. it won't collide with itself). * * @param otherPiece * @return */ boolean isTouching(GamePiece otherPiece); } ================================================ FILE: ch10/game/GameUtilities.java ================================================ package ch10.game; // collision detection between a circle and a rectangle // https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection import java.awt.*; /** * Utility class with a few helper methods for calculating various collisions and * intersections. */ public class GameUtilities { static boolean isPointInsideBox(int x, int y, Rectangle box) { // Our own custom test. We could of course use box.contains(), // but we can practice some interesting conditional checking here. // Let's test left and right first if (x >= box.x && x <= (box.x + box.width)) { // Our x coordinate is ok, so check our y if (y >= box.y && y <= (box.y + box.height)) { return true; } } // x or y was outside the box, so return false return false; } static boolean doesBoxIntersect(Rectangle box, Rectangle other) { // If any of the four corners of box are inside other, we intersect, so // let's check each one. Happily, that answer doesn't change if more // than one corner is contained in other, so we can return as soon as // we find the first contained corner. // Let's get some local copies of the corner coordinates // to make the call arguments easier to read. int x1 = box.x; int y1 = box.y; int x2 = x1 + box.width; int y2 = y1 + box.height; if (isPointInsideBox(x1, y1, other)) { // upper left return true; } else if (isPointInsideBox(x1, y2, other)) { // lower left return true; } else if (isPointInsideBox(x2, y1, other)) { // upper right return true; } else if (isPointInsideBox(x2, y2, other)) { // lower right return true; } // No box points in other so no intersection return false; } /** * Given two rectangles, do the overlap at all? This includes one box being * completely contained by the other box. * * @param box1 a box to test, order does not matter * @param box2 the other box * @return true if the boxes overlap, false otherwise */ public static boolean doBoxesIntersect(Rectangle box1, Rectangle box2) { // Another custom test. We could of course use box1.intersects(box2) // but we can practice method calls and some boolean logic here. if (doesBoxIntersect(box1, box2)) { // At least one of box1's points must be inside box2 return true; } else if (doesBoxIntersect(box2, box1)) { // None of box1's points were in box2, but at least one of box2's points are inside box1 return true; } // No intersections in either direction return false; } } ================================================ FILE: ch10/game/Physicist.java ================================================ package ch10.game; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import static java.awt.Color.*; /** * Our player class. A physicist has an apple that they can throw in addition * to implementing the basic methods for GamePiece. We can also set a custom * color in case you build on the game and allow multiple physicists to be * on the screen at the same time. */ public class Physicist implements GamePiece, ActionListener { Color color; int centerX, centerY; Apple aimingApple; float aimingAngle; float aimingForce; Field field; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; // Boundary helpers private final int physicistHeight = (int)(1.5 * Field.PHYSICIST_SIZE_IN_PIXELS); private Rectangle boundingBox; /** * Create a default, blue physicist */ public Physicist() { this(BLUE); } /** * Create a Physicist of the given color */ public Physicist(Color color) { setColor(color); aimingAngle = 90.0f; aimingForce = 50.0f; getNewApple(); } public void setAimingAngle(Float angle) { aimingAngle = angle; } public void setAimingForce(Float force) { if (force < 0) { force = 0.0f; } aimingForce = force; } /** * Sets the color for the physicist. * * @param color */ public void setColor(Color color) { this.color = color; } @Override public void setPosition(int x, int y) { // Our position is based on the center of our dome, // but it will be drawn from the upper left corner. // Figure out the distance of that gap int offset = (int)(Field.PHYSICIST_SIZE_IN_PIXELS / 2.0f); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, physicistHeight); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } /** * Sets the active field once our physicist is being displayed. * * @param field Active game field */ public void setField(Field field) { this.field = field; } /** * Take the current apple away from the physicist. Returns the apple for * use in animating on the field. Note that if there is no current apple being * aimed, null is returned. * * @return the current apple (if any) being aimed */ public Apple takeApple() { Apple myApple = aimingApple; aimingApple = null; return myApple; } /** * Get a new apple ready if we need one. Do not discard an existing apple. */ public void getNewApple() { // Don't drop the current apple if we already have one! if (aimingApple == null) { aimingApple = new Apple(this); } } @Override public void draw(Graphics g) { // Make sure our physicist is the right color, then draw the semi-circle and box g.setColor(color); g.fillArc(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS, 0, 180); g.fillRect(x, centerY, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS); // Do we have an apple to draw as well? if (aimingApple != null) { // Yes. Position the center of the apple on the edge of our semi-circle. // Use the current aimingAngle to determine where on the edge. double angleInRadians = Math.toRadians(aimingAngle); double radius = Field.PHYSICIST_SIZE_IN_PIXELS / 2.0; int aimingX = centerX + (int)(Math.cos(angleInRadians) * radius); int aimingY = centerY - (int)(Math.sin(angleInRadians) * radius); aimingApple.setPosition(aimingX, aimingY); aimingApple.draw(g); } } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } @Override public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("New Apple")) { getNewApple(); if (field != null) { field.repaint(); } } } } ================================================ FILE: ch10/game/Tree.java ================================================ package ch10.game; import java.awt.*; /** * An obstacle for our game. Trees includ a simple circle and rectangle shape. * They are built using sizes determined by the constants in the Field class. */ public class Tree implements GamePiece { int x, y; // Drawing helpers private Color leafColor = Color.GREEN.darker(); private Color trunkColor = new Color(101, 67, 33); private int trunkWidth = (int)(Field.TREE_WIDTH_IN_PIXELS * 0.2); private int trunkHeight = (int)(Field.TREE_WIDTH_IN_PIXELS * 1.1); private int trunkX, trunkY; // Boundary helpers private Rectangle boundingBox; @Override public void setPosition(int x, int y) { this.x = x; this.y = y; trunkX = x + (Field.TREE_WIDTH_IN_PIXELS - trunkWidth) / 2; trunkY = y + 2 * Field.TREE_WIDTH_IN_PIXELS - trunkHeight; boundingBox = new Rectangle(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_HEIGHT_IN_PIXELS); } @Override public int getPositionX() { return x; } @Override public int getPositionY() { return y; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { g.setColor(trunkColor); g.fillRect(trunkX, trunkY, trunkWidth, trunkHeight); g.setColor(leafColor); g.fillOval(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_WIDTH_IN_PIXELS); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } } ================================================ FILE: ch11/CopyChannels.java ================================================ package ch11; import java.io.*; import java.nio.*; import java.nio.channels.*; /** * A quick demo of copying files using FileChannels. * The names of the source and destination are passed as * command line arguments. */ public class CopyChannels { public static void main( String [] args ) throws Exception { String fromFileName = args[0]; String toFileName = args[1]; FileChannel in = new FileInputStream( fromFileName ).getChannel(); FileChannel out = new FileOutputStream( toFileName ).getChannel(); ByteBuffer buff = ByteBuffer.allocate( 32*1024 ); while ( in.read( buff ) > 0 ) { buff.flip(); out.write( buff ); buff.clear(); } in.close(); out.close(); } } ================================================ FILE: ch11/CopyChannels2.java ================================================ package ch11; import java.io.*; import java.nio.*; import java.nio.channels.*; /** * A quick demo of copying files using FileChannels. * The names of the source and destination are passed as * command line arguments. */ public class CopyChannels2 { public static void main( String [] args ) throws Exception { String fromFileName = args[0]; String toFileName = args[1]; FileChannel in = new FileInputStream( fromFileName ).getChannel(); FileChannel out = new FileOutputStream( toFileName ).getChannel(); ByteBuffer buff = ByteBuffer.allocateDirect( 32*1024 ); while ( in.read( buff ) > 0 ) { buff.flip(); out.write( buff ); buff.clear(); } in.close(); out.close(); } } ================================================ FILE: ch11/CopyChannels3.java ================================================ package ch11; import java.io.*; import java.nio.*; import java.nio.channels.*; /** * A quick demo of copying files using FileChannels. * The names of the source and destination are passed as * command line arguments. */ public class CopyChannels3 { public static void main( String [] args ) throws Exception { String fromFileName = args[0]; String toFileName = args[1]; FileChannel in = new FileInputStream( fromFileName ).getChannel(); FileChannel out = new FileOutputStream( toFileName ).getChannel(); in.transferTo( 0, (int)in.size(), out ); in.close(); out.close(); } } ================================================ FILE: ch11/CopyFile.java ================================================ package ch11; import java.nio.channels.*; import java.nio.file.*; import static java.nio.file.StandardOpenOption.*; /** * A quick demo of copying files using the FileChannel class. * The names of the source and destination are passed as * command line arguments. */ public class CopyFile { public static void main( String [] args ) throws Exception { FileSystem fs = FileSystems.getDefault(); Path fromFile = fs.getPath( args[0] ); Path toFile = fs.getPath( args[1] ); // By using the try-with-resources pattern, our channels // will automagically be cleaned up when we're done. try ( FileChannel in = FileChannel.open( fromFile ); FileChannel out = FileChannel.open( toFile, CREATE, WRITE ); ) { in.transferTo( 0, (int)in.size(), out ); } } } ================================================ FILE: ch11/CopyStreams.java ================================================ package ch11; import java.io.*; /** * A quick demo of copying files using basic streams. * The names of the source and destination are passed as * command line arguments. */ public class CopyStreams { public static void main( String [] args ) throws Exception { String fromFileName = args[0]; String toFileName = args[1]; BufferedInputStream in = new BufferedInputStream( new FileInputStream( fromFileName ) ); BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream( toFileName ) ); byte [] buff = new byte [ 32*1024 ]; int len; while ( (len = in.read( buff )) > 0 ) out.write( buff, 0, len ); in.close(); out.close(); } } ================================================ FILE: ch11/DateAtHost.java ================================================ //file: DateAtHost.java import java.net.Socket; import java.io.*; public class DateAtHost extends java.util.Date { static int timePort = 37; // seconds from start of 20th century to Jan 1, 1970 00:00 GMT static final long offset = 2208988800L; public DateAtHost( String host ) throws IOException { this( host, timePort ); } public DateAtHost( String host, int port ) throws IOException { Socket server = new Socket( host, port ); DataInputStream din = new DataInputStream( server.getInputStream( ) ); int time = din.readInt( ); server.close( ); setTime( (((1L << 32) + time) - offset) * 1000 ); } } ================================================ FILE: ch11/ListIt.java ================================================ package ch11; import java.io.*; /** * A quick demo of file and directory operations. If the file * exists and is a directory, the directory gets listed. If it * is a readable file, the content is dumped to the console. */ public class ListIt { public static void main ( String args[] ) throws Exception { File file = new File( args[0] ); if ( !file.exists() || !file.canRead( ) ) { System.out.println( "Can't read " + file ); return; } if ( file.isDirectory( ) ) { String [] files = file.list( ); for (int i=0; i< files.length; i++) System.out.println( files[i] ); } else try { Reader ir = new InputStreamReader( new FileInputStream( file ) ); BufferedReader in = new BufferedReader( ir ); String line; while ((line = in.readLine( )) != null) System.out.println(line); } catch ( FileNotFoundException e ) { System.out.println( "File Disappeared" ); } } } ================================================ FILE: ch11/game/Apple.java ================================================ package ch11.game; import java.awt.*; /** * Apple * * This class sums up everything we know about the apples our physicists will be lobbing. * We keep the size and weight details and provide a few simple methods for lobbing. We * also provide animation helpers for use with the PlayingField class. */ public class Apple implements GamePiece { public static final int SMALL = 0; public static final int MEDIUM = 1; public static final int LARGE = 2; int size; double diameter; double mass; int centerX, centerY; Physicist myPhysicist; // In game play, apples can be thrown so track their velocities long lastStep; float velocityX, velocityY; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; int scaledLength; // Boundary helper for optimizing collision detection with physicists and trees Rectangle boundingBox; // If we bumped into something, keep a reference to that thing around for cleanup and removal GamePiece collided; /** * Create a default, Medium apple */ public Apple(Physicist owner) { this(owner, MEDIUM); } /** * Create an Apple of the given size */ public Apple(Physicist owner, int size) { myPhysicist = owner; setSize(size); } /** * Sets the size (and dependent properties) of the apple based on the * supplied value which must be one of the size constants. * * @param size one of SMALL, MEDIUM, or LARGE, other values are bounded to SMALL or LARGE */ public void setSize(int size) { if (size < SMALL) { size = SMALL; } if (size > LARGE) { size = LARGE; } this.size = size; switch (size) { case SMALL: diameter = 0.9; mass = 0.5; break; case MEDIUM: diameter = 1.0; mass = 1.0; break; case LARGE: diameter = 1.1; mass = 1.8; break; } // fillOval() used below draws an oval bounded by a box, so figure out the length of the sides. // Since we want a circle, we simply make our box a square so we only need one length. scaledLength = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS + 0.5); boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } public double getDiameter() { return diameter; } @Override public void setPosition(int x, int y) { // Our position is based on the center of the apple, but it will be drawn from the // upper left corner, so figure out the distance of that gap int offset = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS / 2); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { // Make sure our apple will be red, then paint it! g.setColor(Color.RED); g.fillOval(x, y, scaledLength, scaledLength); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece || myPhysicist == otherPiece || collided != null) { // By definition we don't collide with ourselves, our physicist, or with more than one other piece return false; } if (otherPiece instanceof Apple) { // The other piece is an apple, so we can do a simple distance calculation using // the diameters of both apples. Apple otherApple = (Apple) otherPiece; int v = this.y - otherPiece.getPositionY(); // vertical difference int h = this.x - otherPiece.getPositionX(); // horizontal difference double distance = Math.sqrt(v * v + h * h); double myRadius = diameter * Field.APPLE_SIZE_IN_PIXELS / 2; double otherRadius = otherApple.getDiameter() * Field.APPLE_SIZE_IN_PIXELS / 2; if (distance < (myRadius + otherRadius)) { // Since apples track collisions, we'll update the other apple to keep everyone in sync setCollided(otherPiece); otherApple.setCollided(this); return true; } return false; } if (GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox())) { setCollided(otherPiece); return true; } return false; } public GamePiece getCollidedPiece() { return collided; } public void setCollided(GamePiece otherPiece) { this.collided = otherPiece; } public void toss(float angle, float velocity) { lastStep = System.currentTimeMillis(); double radians = angle / 180 * Math.PI; velocityX = (float)(velocity * Math.cos(radians) / mass); // Start with negative velocity since "up" means smaller values of y velocityY = (float)(-velocity * Math.sin(radians) / mass); } public void step() { // Make sure we're moving at all using our lastStep tracker as a sentinel if (lastStep > 0) { // let's apply our gravity long now = System.currentTimeMillis(); float slice = (now - lastStep) / 1000.0f; velocityY = velocityY + (slice * Field.GRAVITY); int newX = (int)(centerX + velocityX); int newY = (int)(centerY + velocityY); setPosition(newX, newY); } } } ================================================ FILE: ch11/game/AppleToss.java ================================================ package ch11.game; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Random; /** * Our apple tossing game. This class extends JFrame to create our * main application window. We're getting closer to our final * version of the game which allows two players to compete in a timed * trial to see who can clear the trees fastest. */ public class AppleToss extends JFrame { public static final int SCORE_HEIGHT = 30; public static final int CONTROL_WIDTH = 300; public static final int CONTROL_HEIGHT = 40; public static final int FIELD_WIDTH = 3 * CONTROL_WIDTH; public static final int FIELD_HEIGHT = 2 * CONTROL_WIDTH; public static final float FORCE_SCALE = 0.7f; GridBagLayout gameLayout = new GridBagLayout(); GridBagConstraints gameConstraints = new GridBagConstraints(); JPanel gamePane = new JPanel(gameLayout); Field field = new Field(); Physicist player1 = new Physicist(); ArrayList otherPlayers = new ArrayList<>(); Random random = new Random(); public AppleToss() { // Create our frame super("Apple Toss Game"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(false); // Build the field with our player and some trees setupFieldForOnePlayer(); // Setup the grid we'll use to layout our various components gameLayout.columnWidths = new int[] { CONTROL_WIDTH, CONTROL_WIDTH, CONTROL_WIDTH }; gameLayout.rowHeights = new int[] { SCORE_HEIGHT, FIELD_HEIGHT, CONTROL_HEIGHT, CONTROL_HEIGHT }; // Now build and add those components at the desired position gamePane.add(new JLabel(" Player 1: 0"), buildConstraints(0, 0, 1, 1)); // gamePane.add(new JLabel(" Player 2: 0"), buildConstraints(0, 1, 1, 1)); gamePane.add(buildRestartButton(), buildConstraints(0, 2, 1, 1)); gamePane.add(field, buildConstraints(1, 0, 1, 3)); gamePane.add(buildAngleControl(), buildConstraints(2, 0, 1, 1)); gamePane.add(buildForceControl(), buildConstraints(2, 1, 1, 1)); gamePane.add(buildTossButton(), buildConstraints(2, 2, 2, 1)); gamePane.add(new JLabel("Angle", JLabel.CENTER), buildConstraints(3, 0, 1, 1)); gamePane.add(new JLabel("Force", JLabel.CENTER), buildConstraints(3, 1, 1, 1)); // Setup the networking menu (actions are left to the read to implement) setupNetworkMenu(); // replace the frame's content with our game setContentPane(gamePane); // And set the correct size + a buffer for any OS frame title setSize(FIELD_WIDTH,SCORE_HEIGHT + FIELD_HEIGHT + (2 * CONTROL_HEIGHT) + 20); } private GridBagConstraints buildConstraints(int row, int col, int rowspan, int colspan) { gameConstraints.fill = GridBagConstraints.BOTH; gameConstraints.gridy = row; gameConstraints.gridx = col; gameConstraints.gridheight = rowspan; gameConstraints.gridwidth = colspan; return gameConstraints; } private JButton buildRestartButton() { JButton button = new JButton("Play Again"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { setupFieldForOnePlayer(); field.repaint(); } }); return button; } private JSlider buildAngleControl() { JSlider slider = new JSlider(0,180); slider.setInverted(true); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { player1.setAimingAngle((float)slider.getValue()); field.repaint(); } }); return slider; } private JSlider buildForceControl() { JSlider slider = new JSlider(); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { player1.setAimingForce(slider.getValue() * FORCE_SCALE); } }); return slider; } private JButton buildTossButton() { JButton button = new JButton("Toss"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { field.startTossFromPlayer(player1); } }); return button; } private void setupNetworkMenu() { JMenu netMenu = new JMenu("Multiplayer"); JMenuItem startItem = new JMenuItem("Start Server"); startItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(AppleToss.this, "Start Server selected", "Network Alert", JOptionPane.INFORMATION_MESSAGE); } }); netMenu.add(startItem); JMenuItem joinItem = new JMenuItem("Join Game..."); joinItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(AppleToss.this, "Join Game selected", "Network Alert", JOptionPane.INFORMATION_MESSAGE); } }); netMenu.add(joinItem); JMenuItem quitItem = new JMenuItem("Disconnect"); quitItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(AppleToss.this, "Disconnect selected", "Network Alert", JOptionPane.INFORMATION_MESSAGE); } }); netMenu.add(quitItem); // build a JMenuBar for the application JMenuBar mainBar = new JMenuBar(); mainBar.add(netMenu); setJMenuBar(mainBar); } /** * Helper method to return a good x value for a tree so it's not off the left or right edge. * * @return x value within the bounds of the playing field width */ private int goodX() { // at least half the width of the tree plus a few pixels int leftMargin = Field.TREE_WIDTH_IN_PIXELS / 2 + 5; // now find a random number between a left and right margin int rightMargin = FIELD_WIDTH - leftMargin; // And return a random number starting at the left margin return leftMargin + random.nextInt(rightMargin - leftMargin); } /** * Helper method to return a good y value for a tree so it's not off the top or bottom of the screen. * * @return y value within the bounds of the playing field height */ private int goodY() { // at least half the height of the "leaves" plus a few pixels int topMargin = Field.TREE_WIDTH_IN_PIXELS / 2 + 5; // a little higher off the bottom int bottomMargin = FIELD_HEIGHT - Field.TREE_HEIGHT_IN_PIXELS; // And return a random number starting at the top margin but not past the bottom return topMargin + random.nextInt(bottomMargin - topMargin); } /** * A helper method to populate a one player field with target trees. */ private void setupFieldForOnePlayer() { // place our (new) physicist in the lower left corner if (field.physicists.size() == 0) { player1.setPosition(Field.PHYSICIST_SIZE_IN_PIXELS, FIELD_HEIGHT - (int) (Field.PHYSICIST_SIZE_IN_PIXELS * 1.5)); field.physicists.add(player1); player1.setField(field); } // Create some trees for target practice for (int i = field.trees.size(); i < 10; i++) { Tree t = new Tree(); t.setPosition(goodX(), goodY()); // Trees can be close to each other and overlap, but they shouldn't intersect our physicist while(player1.isTouching(t)) { // We do intersect this tree, so let's try again t.setPosition(goodX(), goodY()); System.err.println("Repositioning an intersecting tree..."); } field.trees.add(t); } } public static void main(String args[]) { AppleToss game = new AppleToss(); game.setVisible(true); } } ================================================ FILE: ch11/game/Field.java ================================================ package ch11.game; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * The playing field for our game. Now we can setup some constants for * other game classes to use and create member variables for our player and some trees. */ public class Field extends JComponent implements ActionListener { public static final float GRAVITY = 9.8f; // feet per second per second public static final int STEP = 40; // duration of an animation frame in milliseconds public static final int APPLE_SIZE_IN_PIXELS = 30; public static final int TREE_WIDTH_IN_PIXELS = 60; public static final int TREE_HEIGHT_IN_PIXELS = 2 * TREE_WIDTH_IN_PIXELS; public static final int PHYSICIST_SIZE_IN_PIXELS = 75; public static final int MAX_PHYSICISTS = 5; public static final int MAX_APPLES_PER_PHYSICIST = 3; public static final int MAX_TREES = 12; Color fieldColor = Color.GRAY; // ArrayList will be covered in Generics chapter // synchronizedArrayList will be covered in Threads chapter List physicists = Collections.synchronizedList(new ArrayList<>()); List apples = Collections.synchronizedList(new ArrayList<>()); List trees = Collections.synchronizedList(new ArrayList<>()); boolean animating = false; Thread animationThread; Timer animationTimer; protected void paintComponent(Graphics g) { g.setColor(fieldColor); g.fillRect(0,0, getWidth(), getHeight()); for (Physicist p : physicists) { p.draw(g); } for (Tree t : trees) { t.draw(g); } for (Apple a : apples) { a.draw(g); } } public void actionPerformed(ActionEvent event) { if (animating && event.getActionCommand().equals("repaint")) { System.out.println("Timer stepping " + apples.size() + " apples"); for (Apple a : apples) { a.step(); detectCollisions(a); } repaint(); cullFallenApples(); } } /** * Toss an apple from the given physicist using that physicist's aim and force. * Make sure the field is in the animating state. * * @param physicist the player whose apple should be tossed */ public void startTossFromPlayer(Physicist physicist) { if (!animating) { System.out.println("Starting animation!"); animating = true; startAnimation(); } if (animating) { // Check to make sure we have an apple to toss if (physicist.aimingApple != null) { Apple apple = physicist.takeApple(); apple.toss(physicist.aimingAngle, physicist.aimingForce); apples.add(apple); Timer appleLoader = new Timer(800, physicist); appleLoader.setActionCommand("New Apple"); appleLoader.setRepeats(false); appleLoader.start(); } } } void cullFallenApples() { Iterator iterator = apples.iterator(); while (iterator.hasNext()) { Apple a = iterator.next(); if (a.getCollidedPiece() != null) { GamePiece otherPiece = a.getCollidedPiece(); if (otherPiece instanceof Physicist) { hitPhysicist((Physicist) otherPiece); } else if (otherPiece instanceof Tree) { hitTree((Tree) otherPiece); } // Remove ourselves. If the other piece we hit was an apple, leave it alone. // It will be removed when the iterator comes to it. iterator.remove(); } else if (a.getPositionY() > 600) { System.out.println("Culling apple"); iterator.remove(); } } if (apples.size() <= 0) { animating = false; if (animationTimer != null && animationTimer.isRunning()) { animationTimer.stop(); } } } void detectCollisions(Apple apple) { // Check for other apples for (Apple a : apples) { if (apple.isTouching(a)) { System.out.println("Touching another apple!"); return; } } // Check for physicists for (Physicist p : physicists) { if (apple.isTouching(p)) { System.out.println("Touching a physicist!"); return; } } // Check for trees for (Tree t : trees) { if (apple.isTouching(t)) { System.out.println("Touching a tree!"); return; } } } void hitPhysicist(Physicist physicist) { // do any scoring or notifications here physicists.remove(physicist); } void hitTree(Tree tree) { // do any scoring or notifications here trees.remove(tree); } void startAnimation() { // Animator myAnimator = new Animator(); // animationThread = new Thread(myAnimator); // animationThread.start(); if (animationTimer == null) { animationTimer = new Timer(STEP, this); animationTimer.setActionCommand("repaint"); animationTimer.setRepeats(true); animationTimer.start(); } else if (!animationTimer.isRunning()) { animationTimer.restart(); } } class Animator implements Runnable { public void run() { while (animating) { System.out.println("Stepping " + apples.size() + " apples"); for (Apple a : apples) { a.step(); detectCollisions(a); } SwingUtilities.invokeLater(new Runnable() { public void run() { Field.this.repaint(); } }); cullFallenApples(); try { Thread.sleep((int)(STEP * 1000)); } catch (InterruptedException ie) { System.err.println("Animation interrupted"); animating = false; } } } } } ================================================ FILE: ch11/game/GamePiece.java ================================================ package ch11.game; import java.awt.*; /** * Interface to hold common elements for our apples, trees, and physicists * From the README: * GamePiece * - methods for positioning on a PlayingField * - methods for Drawing * - methods for detecting a collision with other GamePieces */ public interface GamePiece { /** * Sets the position of the piece on the playing field. * (0,0) is the upper left per standard Java drawing methods. * * @param x * @param y */ void setPosition(int x, int y); /** * Gets the current horizontal position of the piece on the field. * * @return current X position of the piece */ int getPositionX(); /** * Gets the current vertical position of the piece on the field. * * @return current Y position of the piece */ int getPositionY(); /** * Gets the bounding box of this piece. * * @return a Rectangle encompassing the visual elements of the piece */ Rectangle getBoundingBox(); /** * Draws the piece inside the given graphics context. * Do not assume anything about the state of the context. * * @param g */ void draw(Graphics g); /** * Detect a collision with another piece on the field. * By definition, a piece does NOT touch itself (i.e. it won't collide with itself). * * @param otherPiece * @return */ boolean isTouching(GamePiece otherPiece); } ================================================ FILE: ch11/game/GameUtilities.java ================================================ package ch11.game; // collision detection between a circle and a rectangle // https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection import java.awt.*; /** * Utility class with a few helper methods for calculating various collisions and * intersections. */ public class GameUtilities { static boolean isPointInsideBox(int x, int y, Rectangle box) { // Our own custom test. We could of course use box.contains(), // but we can practice some interesting conditional checking here. // Let's test left and right first if (x >= box.x && x <= (box.x + box.width)) { // Our x coordinate is ok, so check our y if (y >= box.y && y <= (box.y + box.height)) { return true; } } // x or y was outside the box, so return false return false; } static boolean doesBoxIntersect(Rectangle box, Rectangle other) { // If any of the four corners of box are inside other, we intersect, so // let's check each one. Happily, that answer doesn't change if more // than one corner is contained in other, so we can return as soon as // we find the first contained corner. // Let's get some local copies of the corner coordinates // to make the call arguments easier to read. int x1 = box.x; int y1 = box.y; int x2 = x1 + box.width; int y2 = y1 + box.height; if (isPointInsideBox(x1, y1, other)) { // upper left return true; } else if (isPointInsideBox(x1, y2, other)) { // lower left return true; } else if (isPointInsideBox(x2, y1, other)) { // upper right return true; } else if (isPointInsideBox(x2, y2, other)) { // lower right return true; } // No box points in other so no intersection return false; } /** * Given two rectangles, do the overlap at all? This includes one box being * completely contained by the other box. * * @param box1 a box to test, order does not matter * @param box2 the other box * @return true if the boxes overlap, false otherwise */ public static boolean doBoxesIntersect(Rectangle box1, Rectangle box2) { // Another custom test. We could of course use box1.intersects(box2) // but we can practice method calls and some boolean logic here. if (doesBoxIntersect(box1, box2)) { // At least one of box1's points must be inside box2 return true; } else if (doesBoxIntersect(box2, box1)) { // None of box1's points were in box2, but at least one of box2's points are inside box1 return true; } // No intersections in either direction return false; } } ================================================ FILE: ch11/game/Physicist.java ================================================ package ch11.game; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import static java.awt.Color.*; /** * Our player class. A physicist has an apple that they can throw in addition * to implementing the basic methods for GamePiece. We can also set a custom * color in case you build on the game and allow multiple physicists to be * on the screen at the same time. */ public class Physicist implements GamePiece, ActionListener { Color color; int centerX, centerY; Apple aimingApple; float aimingAngle; float aimingForce; Field field; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; // Boundary helpers private final int physicistHeight = (int)(1.5 * Field.PHYSICIST_SIZE_IN_PIXELS); private Rectangle boundingBox; /** * Create a default, blue physicist */ public Physicist() { this(BLUE); } /** * Create a Physicist of the given color */ public Physicist(Color color) { setColor(color); aimingAngle = 90.0f; aimingForce = 50.0f; getNewApple(); } public void setAimingAngle(Float angle) { aimingAngle = angle; } public void setAimingForce(Float force) { if (force < 0) { force = 0.0f; } aimingForce = force; } /** * Sets the color for the physicist. * * @param color */ public void setColor(Color color) { this.color = color; } @Override public void setPosition(int x, int y) { // Our position is based on the center of our dome, // but it will be drawn from the upper left corner. // Figure out the distance of that gap int offset = (int)(Field.PHYSICIST_SIZE_IN_PIXELS / 2.0f); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, physicistHeight); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } /** * Sets the active field once our physicist is being displayed. * * @param field Active game field */ public void setField(Field field) { this.field = field; } /** * Take the current apple away from the physicist. Returns the apple for * use in animating on the field. Note that if there is no current apple being * aimed, null is returned. * * @return the current apple (if any) being aimed */ public Apple takeApple() { Apple myApple = aimingApple; aimingApple = null; return myApple; } /** * Get a new apple ready if we need one. Do not discard an existing apple. */ public void getNewApple() { // Don't drop the current apple if we already have one! if (aimingApple == null) { aimingApple = new Apple(this); } } @Override public void draw(Graphics g) { // Make sure our physicist is the right color, then draw the semi-circle and box g.setColor(color); g.fillArc(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS, 0, 180); g.fillRect(x, centerY, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS); // Do we have an apple to draw as well? if (aimingApple != null) { // Yes. Position the center of the apple on the edge of our semi-circle. // Use the current aimingAngle to determine where on the edge. double angleInRadians = Math.toRadians(aimingAngle); double radius = Field.PHYSICIST_SIZE_IN_PIXELS / 2.0; int aimingX = centerX + (int)(Math.cos(angleInRadians) * radius); int aimingY = centerY - (int)(Math.sin(angleInRadians) * radius); aimingApple.setPosition(aimingX, aimingY); aimingApple.draw(g); } } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } @Override public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("New Apple")) { getNewApple(); if (field != null) { field.repaint(); } } } } ================================================ FILE: ch11/game/Tree.java ================================================ package ch11.game; import java.awt.*; /** * An obstacle for our game. Trees includ a simple circle and rectangle shape. * They are built using sizes determined by the constants in the Field class. */ public class Tree implements GamePiece { int x, y; // Drawing helpers private Color leafColor = Color.GREEN.darker(); private Color trunkColor = new Color(101, 67, 33); private int trunkWidth = (int)(Field.TREE_WIDTH_IN_PIXELS * 0.2); private int trunkHeight = (int)(Field.TREE_WIDTH_IN_PIXELS * 1.1); private int trunkX, trunkY; // Boundary helpers private Rectangle boundingBox; @Override public void setPosition(int x, int y) { this.x = x; this.y = y; trunkX = x + (Field.TREE_WIDTH_IN_PIXELS - trunkWidth) / 2; trunkY = y + 2 * Field.TREE_WIDTH_IN_PIXELS - trunkHeight; boundingBox = new Rectangle(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_HEIGHT_IN_PIXELS); } @Override public int getPositionX() { return x; } @Override public int getPositionY() { return y; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { g.setColor(trunkColor); g.fillRect(trunkX, trunkY, trunkWidth, trunkHeight); g.setColor(leafColor); g.fillOval(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_WIDTH_IN_PIXELS); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } } ================================================ FILE: ch12/Post.java ================================================ package ch12; import java.net.*; import java.io.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * A small graphical application that demonstrates use of the * HTTP POST mechanism. Provide a POST-able URL to the command line * and use the "Post" button to send sample name and password * data to the URL. * * See the servlet section of this chapter for the ShowParameters * example that can serve (ha!) as the receiving (server) side. */ public class Post extends JPanel implements ActionListener { JTextField nameField; JPasswordField passwordField; String postURL; GridBagConstraints constraints = new GridBagConstraints( ); void addGB( Component component, int x, int y ) { constraints.gridx = x; constraints.gridy = y; add ( component, constraints ); } public Post( String postURL ) { this.postURL = postURL; setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 5)); JButton postButton = new JButton("Post"); postButton.addActionListener( this ); setLayout( new GridBagLayout( ) ); constraints.fill = GridBagConstraints.HORIZONTAL; addGB( new JLabel("Name ", JLabel.TRAILING), 0, 0 ); addGB( nameField = new JTextField(20), 1, 0 ); addGB( new JLabel("Password ", JLabel.TRAILING), 0, 1 ); addGB( passwordField = new JPasswordField(20), 1, 1 ); constraints.fill = GridBagConstraints.NONE; constraints.gridwidth = 2; constraints.anchor = GridBagConstraints.EAST; addGB( postButton, 1, 2 ); } public void actionPerformed(ActionEvent e) { postData( ); } protected void postData( ) { StringBuilder sb = new StringBuilder(); String pw = new String(passwordField.getPassword()); try { sb.append( URLEncoder.encode("Name", "UTF-8") + "=" ); sb.append( URLEncoder.encode(nameField.getText(), "UTF-8") ); sb.append( "&" + URLEncoder.encode("Password", "UTF-8") + "=" ); sb.append( URLEncoder.encode(pw, "UTF-8") ); } catch (UnsupportedEncodingException uee) { System.out.println(uee); } String formData = sb.toString( ); try { URL url = new URL( postURL ); HttpURLConnection urlcon = (HttpURLConnection) url.openConnection( ); urlcon.setRequestMethod("POST"); urlcon.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); urlcon.setDoOutput(true); urlcon.setDoInput(true); PrintWriter pout = new PrintWriter( new OutputStreamWriter( urlcon.getOutputStream( ), "8859_1"), true ); pout.print( formData ); pout.flush( ); // Did the post succeed? if ( urlcon.getResponseCode() == HttpURLConnection.HTTP_OK ) System.out.println("Posted ok!"); else { System.out.println("Bad post..."); return; } // Hooray! Go ahead and read the results... //InputStream in = urlcon.getInputStream( ); // ... } catch (MalformedURLException e) { System.out.println(e); // bad postURL } catch (IOException e2) { System.out.println(e2); // I/O error } } public static void main( String [] args ) { if (args.length != 1) { System.err.println("Must specify URL on command line. Exiting."); System.exit(1); } JFrame frame = new JFrame("SimplePost"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add( new Post(args[0]), "Center" ); frame.pack(); frame.setVisible(true); } } ================================================ FILE: ch12/Read.java ================================================ package ch12; import java.io.*; import java.net.*; /** * A simple command line demonstration of reading from a URL. * The URL to read must be passed as the only argument on the command line. */ public class Read { public static void main(String args[]) { // Did we get an argument to use as the URL? if (args.length != 1) { System.err.println("Must specify URL on command line. Exiting."); System.exit(1); } // Great! Let's try to read it and dump the contents to the terminal. try { URL url = new URL(args[0]); BufferedReader bin = new BufferedReader ( new InputStreamReader( url.openStream() )); String line; while ( (line = bin.readLine()) != null ) { System.out.println( line ); } bin.close(); } catch (Exception e) { System.err.println("Error occurred while reading:" + e); } } } ================================================ FILE: ch13/ActionDemoLambda.java ================================================ package ch13; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * An update to the ch10.ActionDemo2 class with a lambda expression * to help handle action events. */ public class ActionDemoLambda { public static void main( String[] args ) { JFrame frame = new JFrame( "ActionListener & Lambdas Demo" ); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new FlowLayout()); frame.setSize( 300, 180 ); JLabel label = new JLabel("Results go here", JLabel.CENTER ); // We can replace our separate helper class with a simple lambda expression ActionListener helper = ae -> label.setText(ae.getActionCommand()); JButton simpleButton = new JButton("Button"); simpleButton.addActionListener(helper); JTextField simpleField = new JTextField(10); simpleField.addActionListener(helper); frame.add(simpleButton); frame.add(simpleField); frame.add(label); frame.setVisible( true ); } } ================================================ FILE: ch13/ListItLambda.java ================================================ package ch13; import java.io.*; import java.util.Arrays; /** * An update to ListIt from ch11 with a lambda expression example * when listing the contents of a directory. */ public class ListItLambda { public static void main ( String args[] ) throws Exception { File file = new File( args[0] ); if ( !file.exists() || !file.canRead( ) ) { System.out.println( "Can't read " + file ); return; } if ( file.isDirectory( ) ) { // We can condense the previous explicit loop to a forEach + lambda Arrays.asList(file.list()).forEach(f -> System.out.println(f)); } else try { Reader ir = new InputStreamReader( new FileInputStream( file ) ); BufferedReader in = new BufferedReader( ir ); String line; while ((line = in.readLine( )) != null) System.out.println(line); } catch ( FileNotFoundException e ) { System.out.println( "File Disappeared" ); } } } ================================================ FILE: game/Apple.java ================================================ /** * Apple * * This class sums up everything we know about the apples our physicists will be lobbing. * We keep the size and weight details and provide a few simple methods for lobbing. We * also provide animation helpers for use with the PlayingField class. */ package game; import java.awt.*; public class Apple implements GamePiece { public static final int SMALL = 0; public static final int MEDIUM = 1; public static final int LARGE = 2; int size; double diameter; double mass; int centerX, centerY; Physicist myPhysicist; // In game play, apples can be thrown so track their velocities long lastStep; float velocityX, velocityY; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; int scaledLength; // Boundary helper for optimizing collision detection with physicists and trees Rectangle boundingBox; // If we bumped into something, keep a reference to that thing around for cleanup and removal GamePiece collided; /** * Create a default, Medium apple */ public Apple(Physicist owner) { this(owner, MEDIUM); } /** * Create an Apple of the given size */ public Apple(Physicist owner, int size) { myPhysicist = owner; setSize(size); } /** * Sets the size (and dependent properties) of the apple based on the * supplied value which must be one of the size constants. * * @param size one of SMALL, MEDIUM, or LARGE, other values are bounded to SMALL or LARGE */ public void setSize(int size) { if (size < SMALL) { size = SMALL; } if (size > LARGE) { size = LARGE; } this.size = size; switch (size) { case SMALL: diameter = 0.9; mass = 0.5; break; case MEDIUM: diameter = 1.0; mass = 1.0; break; case LARGE: diameter = 1.1; mass = 1.8; break; } // fillOval() used below draws an oval bounded by a box, so figure out the length of the sides. // Since we want a circle, we simply make our box a square so we only need one length. scaledLength = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS + 0.5); boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } public double getDiameter() { return diameter; } @Override public void setPosition(int x, int y) { // Our position is based on the center of the apple, but it will be drawn from the // upper left corner, so figure out the distance of that gap int offset = (int)(diameter * Field.APPLE_SIZE_IN_PIXELS / 2); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, scaledLength, scaledLength); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { // Make sure our apple will be red, then paint it! g.setColor(Color.RED); g.fillOval(x, y, scaledLength, scaledLength); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece || myPhysicist == otherPiece || collided != null) { // By definition we don't collide with ourselves, our physicist, or with more than one other piece return false; } if (otherPiece instanceof Apple) { // The other piece is an apple, so we can do a simple distance calculation using // the diameters of both apples. Apple otherApple = (Apple) otherPiece; int v = this.y - otherPiece.getPositionY(); // vertical difference int h = this.x - otherPiece.getPositionX(); // horizontal difference double distance = Math.sqrt(v * v + h * h); double myRadius = diameter * Field.APPLE_SIZE_IN_PIXELS / 2; double otherRadius = otherApple.getDiameter() * Field.APPLE_SIZE_IN_PIXELS / 2; if (distance < (myRadius + otherRadius)) { // Since apples track collisions, we'll update the other apple to keep everyone in sync setCollided(otherPiece); otherApple.setCollided(this); return true; } return false; } if (GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox())) { setCollided(otherPiece); return true; } return false; } public GamePiece getCollidedPiece() { return collided; } public void setCollided(GamePiece otherPiece) { this.collided = otherPiece; } public void toss(float angle, float velocity) { lastStep = System.currentTimeMillis(); double radians = angle / 180 * Math.PI; velocityX = (float)(velocity * Math.cos(radians) / mass); // Start with negative velocity since "up" means smaller values of y velocityY = (float)(-velocity * Math.sin(radians) / mass); } public void step() { // Make sure we're moving at all using our lastStep tracker as a sentinel if (lastStep > 0) { // let's apply our gravity long now = System.currentTimeMillis(); float slice = (now - lastStep) / 1000.0f; velocityY = velocityY + (slice * Field.GRAVITY); int newX = (int)(centerX + velocityX); int newY = (int)(centerY + velocityY); setPosition(newX, newY); } } } ================================================ FILE: game/AppleToss.java ================================================ package game; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Random; public class AppleToss extends JFrame { public static final int SCORE_HEIGHT = 30; public static final int CONTROL_WIDTH = 300; public static final int CONTROL_HEIGHT = 40; public static final int FIELD_WIDTH = 3 * CONTROL_WIDTH; public static final int FIELD_HEIGHT = 2 * CONTROL_WIDTH; public static final float FORCE_SCALE = 0.7f; GridBagLayout gameLayout = new GridBagLayout(); GridBagConstraints gameConstraints = new GridBagConstraints(); JPanel gamePane = new JPanel(gameLayout); Field field = new Field(); Physicist player1 = new Physicist(); Multiplayer multiplayerHelper; public AppleToss() { // Create our frame super("Apple Toss Game"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(false); // Build the field with our player and some trees setupFieldForOnePlayer(); // Setup the grid we'll use to layout our various components gameLayout.columnWidths = new int[] { CONTROL_WIDTH, CONTROL_WIDTH, CONTROL_WIDTH }; gameLayout.rowHeights = new int[] { SCORE_HEIGHT, FIELD_HEIGHT, CONTROL_HEIGHT, CONTROL_HEIGHT }; // Now build and add those components at the desired position JLabel player1score = new JLabel(" Player 1: 0"); field.scoreLabels[1] = player1score; JLabel player2score = new JLabel(" Player 2"); field.scoreLabels[2] = player2score; gamePane.add(player1score, buildConstraints(0, 0, 1, 1)); gamePane.add(player2score, buildConstraints(0, 1, 1, 1)); gamePane.add(buildRestartButton(), buildConstraints(0, 2, 1, 1)); gamePane.add(field, buildConstraints(1, 0, 1, 3)); gamePane.add(buildAngleControl(), buildConstraints(2, 0, 1, 1)); gamePane.add(buildForceControl(), buildConstraints(2, 1, 1, 1)); gamePane.add(buildTossButton(), buildConstraints(2, 2, 2, 1)); gamePane.add(new JLabel("Angle", JLabel.CENTER), buildConstraints(3, 0, 1, 1)); gamePane.add(new JLabel("Force", JLabel.CENTER), buildConstraints(3, 1, 1, 1)); // replace the frame's content with our game and set the pane's size gamePane.setPreferredSize(new Dimension(FIELD_WIDTH,SCORE_HEIGHT + FIELD_HEIGHT + (2 * CONTROL_HEIGHT))); setContentPane(gamePane); // Setup our networking menu setupNetworkMenu(); // And set the correct size of the frame itself pack(); } private GridBagConstraints buildConstraints(int row, int col, int rowspan, int colspan) { gameConstraints.fill = GridBagConstraints.BOTH; gameConstraints.gridy = row; gameConstraints.gridx = col; gameConstraints.gridheight = rowspan; gameConstraints.gridwidth = colspan; return gameConstraints; } private JButton buildRestartButton() { JButton button = new JButton("Play Again"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { setupFieldForOnePlayer(); field.repaint(); } }); return button; } private JSlider buildAngleControl() { JSlider slider = new JSlider(0,180); slider.setInverted(true); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { player1.setAimingAngle((float)slider.getValue()); field.repaint(); } }); return slider; } private JSlider buildForceControl() { JSlider slider = new JSlider(); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { player1.setAimingForce(slider.getValue() * FORCE_SCALE); } }); return slider; } private JButton buildTossButton() { JButton button = new JButton("Toss"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { field.startTossFromPlayer(player1); } }); return button; } /** * A helper method to populate a one player field with target trees. */ private void setupFieldForOnePlayer() { // place our (new) physicist in the lower left corner and connect them to the field player1.setPosition(Field.PHYSICIST_SIZE_IN_PIXELS, FIELD_HEIGHT - (int) (Field.PHYSICIST_SIZE_IN_PIXELS * 1.5)); field.setPlayer(player1); player1.setField(field); field.setupNewGame(); } private void setupNetworkMenu() { JMenu netMenu = new JMenu("Multiplayer"); multiplayerHelper = new Multiplayer(field); JMenuItem startItem = new JMenuItem("Start Server"); startItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { multiplayerHelper.startServer(); } }); netMenu.add(startItem); JMenuItem joinItem = new JMenuItem("Join Game..."); joinItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String otherServer = JOptionPane.showInputDialog(AppleToss.this, "Enter server name or address:"); multiplayerHelper.joinGame(otherServer); } }); netMenu.add(joinItem); JMenuItem quitItem = new JMenuItem("Disconnect"); quitItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { multiplayerHelper.disconnect(); } }); netMenu.add(quitItem); // build a JMenuBar for the application JMenuBar mainBar = new JMenuBar(); mainBar.add(netMenu); setJMenuBar(mainBar); } public static void main(String args[]) { AppleToss game = new AppleToss(); game.setVisible(true); } } ================================================ FILE: game/Field.java ================================================ package game; import javax.swing.*; import javax.swing.Timer; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.util.*; import java.util.List; public class Field extends JComponent implements ActionListener { public static final float GRAVITY = 9.8f; // feet per second per second public static final int STEP = 40; // duration of an animation frame in milliseconds public static final int APPLE_SIZE_IN_PIXELS = 30; public static final int TREE_WIDTH_IN_PIXELS = 60; public static final int TREE_HEIGHT_IN_PIXELS = 2 * TREE_WIDTH_IN_PIXELS; public static final int PHYSICIST_SIZE_IN_PIXELS = 75; public static final int MAX_TREES = 12; Color fieldColor = Color.GRAY; Random random = new Random(); // ArrayList covered in Generics chapter // synchronizedArrayList covered in Threads chapter Physicist physicist; int myScore = 0; String[] scores = new String[3]; JLabel[] scoreLabels = new JLabel[3]; List apples = Collections.synchronizedList(new ArrayList<>()); List trees = Collections.synchronizedList(new ArrayList<>()); boolean animating = false; Thread animationThread; Timer animationTimer; protected void paintComponent(Graphics g) { g.setColor(fieldColor); g.fillRect(0,0, getWidth(), getHeight()); physicist.draw(g); for (Tree t : trees) { t.draw(g); } for (Apple a : apples) { a.draw(g); } } public void setPlayer(Physicist p) { physicist = p; } public void actionPerformed(ActionEvent event) { if (animating && event.getActionCommand().equals("repaint")) { for (Apple a : apples) { a.step(); detectCollisions(a); } repaint(); cullFallenApples(); } } /** * Toss an apple from the given physicist using that physicist's aim and force. * Make sure the field is in the animating state. * * @param physicist the player whose apple should be tossed */ public void startTossFromPlayer(Physicist physicist) { if (!animating) { System.out.println("Starting animation!"); animating = true; startAnimation(); } if (animating) { // Check to make sure we have an apple to toss if (physicist.aimingApple != null) { Apple apple = physicist.takeApple(); apple.toss(physicist.aimingAngle, physicist.aimingForce); apples.add(apple); Timer appleLoader = new Timer(800, physicist); appleLoader.setActionCommand("New Apple"); appleLoader.setRepeats(false); appleLoader.start(); } } } void cullFallenApples() { Iterator iterator = apples.iterator(); while (iterator.hasNext()) { Apple a = iterator.next(); if (a.getCollidedPiece() != null) { GamePiece otherPiece = a.getCollidedPiece(); if (otherPiece instanceof Physicist) { hitPhysicist((Physicist) otherPiece); } else if (otherPiece instanceof Tree) { hitTree((Tree) otherPiece); } // Remove ourselves. If the other piece we hit was an apple, leave it alone. // It will be removed when the iterator comes to it. iterator.remove(); } else if (a.getPositionY() > 600) { System.out.println("Culling apple"); iterator.remove(); } } if (apples.size() <= 0) { animating = false; if (animationTimer != null && animationTimer.isRunning()) { animationTimer.stop(); } } } void detectCollisions(Apple apple) { // Check for other apples for (Apple a : apples) { if (apple.isTouching(a)) { System.out.println("Touching another apple!"); return; } } // Check our physicist if (apple.isTouching(physicist)) { System.out.println("Touching a physicist!"); return; } // Check for trees for (Tree t : trees) { if (apple.isTouching(t)) { System.out.println("Touching a tree!"); return; } } } void hitPhysicist(Physicist physicist) { // do any scoring or notifications here } void hitTree(Tree tree) { // do any scoring or notifications here myScore += 10; trees.remove(tree); setScore(1, String.valueOf(myScore)); } void startAnimation() { // Animator myAnimator = new Animator(); // animationThread = new Thread(myAnimator); // animationThread.start(); if (animationTimer == null) { animationTimer = new Timer(STEP, this); animationTimer.setActionCommand("repaint"); animationTimer.setRepeats(true); animationTimer.start(); } else if (!animationTimer.isRunning()) { animationTimer.restart(); } } public String getScore(int playerNumber) { return scores[playerNumber]; } public void setScore(int playerNumber, String score) { scores[playerNumber] = score; SwingUtilities.invokeLater(new Runnable() { public void run() { String newScore = " Player " + playerNumber + ": " + score; scoreLabels[playerNumber].setText(newScore); } }); } public String getWinner() { int score2 = -1; try { score2 = Integer.parseInt(scores[2]); } catch (NumberFormatException nfe) { System.err.println("Couldn't parse the other player's score: " + scores[2]); } if (myScore == score2) { return "It's a tie!"; } else if (myScore > score2) { return "You won!"; } else { return "They won."; } } /** * Helper method to return a good x value for a tree so it's not off the left or right edge. * * @return x value within the bounds of the playing field width */ private int goodX() { // at least half the width of the tree plus a few pixels int leftMargin = TREE_WIDTH_IN_PIXELS / 2 + 5; // now find a random number between a left and right margin int rightMargin = AppleToss.FIELD_WIDTH - leftMargin; // And return a random number starting at the left margin return leftMargin + random.nextInt(rightMargin - leftMargin); } /** * Helper method to return a good y value for a tree so it's not off the top or bottom of the screen. * * @return y value within the bounds of the playing field height */ private int goodY() { // at least half the height of the "leaves" plus a few pixels int topMargin = TREE_WIDTH_IN_PIXELS / 2 + 5; // a little higher off the bottom int bottomMargin = AppleToss.FIELD_HEIGHT - TREE_HEIGHT_IN_PIXELS; // And return a random number starting at the top margin but not past the bottom return topMargin + random.nextInt(bottomMargin - topMargin); } public void setupNewGame() { // Clear out any old trees trees.clear(); // Now create some trees for target practice for (int i = trees.size(); i < Field.MAX_TREES; i++) { Tree t = new Tree(); t.setPosition(goodX(), goodY()); // Trees can be close to each other and overlap, but they shouldn't intersect our physicist while(physicist.isTouching(t)) { // We do intersect this tree, so let's try again t.setPosition(goodX(), goodY()); System.err.println("Repositioning an intersecting tree..."); } trees.add(t); } repaint(); } class Animator implements Runnable { public void run() { while (animating) { System.out.println("Stepping " + apples.size() + " apples"); for (Apple a : apples) { a.step(); detectCollisions(a); } SwingUtilities.invokeLater(new Runnable() { public void run() { Field.this.repaint(); } }); cullFallenApples(); try { Thread.sleep((int)(STEP * 1000)); } catch (InterruptedException ie) { System.err.println("Animation interrupted"); animating = false; } } } } } ================================================ FILE: game/GamePiece.java ================================================ package game; import java.awt.*; /** * Interface to hold common elements for our apples, trees, and physicists * From the README: * GamePiece * - methods for positioning on a PlayingField * - methods for Drawing * - methods for detecting a collision with other GamePieces */ public interface GamePiece { /** * Sets the position of the piece on the playing field. * (0,0) is the upper left per standard Java drawing methods. * * @param x * @param y */ void setPosition(int x, int y); /** * Gets the current horizontal position of the piece on the field. * * @return current X position of the piece */ int getPositionX(); /** * Gets the current vertical position of the piece on the field. * * @return current Y position of the piece */ int getPositionY(); /** * Gets the bounding box of this piece. * * @return a Rectangle encompassing the visual elements of the piece */ Rectangle getBoundingBox(); /** * Draws the piece inside the given graphics context. * Do not assume anything about the state of the context. * * @param g */ void draw(Graphics g); /** * Detect a collision with another piece on the field. * By definition, a piece does NOT touch itself (i.e. it won't collide with itself). * * @param otherPiece * @return */ boolean isTouching(GamePiece otherPiece); } ================================================ FILE: game/GameUtilities.java ================================================ package game; // collision detection between a circle and a rectangle // https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection import java.awt.*; /** * Utility class with a few helper methods for calculating various collisions and * intersections. */ public class GameUtilities { static boolean isPointInsideBox(int x, int y, Rectangle box) { // Our own custom test. We could of course use box.contains(), // but we can practice some interesting conditional checking here. // Let's test left and right first if (x >= box.x && x <= (box.x + box.width)) { // Our x coordinate is ok, so check our y if (y >= box.y && y <= (box.y + box.height)) { return true; } } // x or y was outside the box, so return false return false; } static boolean doesBoxIntersect(Rectangle box, Rectangle other) { // If any of the four corners of box are inside other, we intersect, so // let's check each one. Happily, that answer doesn't change if more // than one corner is contained in other, so we can return as soon as // we find the first contained corner. // Let's get some local copies of the corner coordinates // to make the call arguments easier to read. int x1 = box.x; int y1 = box.y; int x2 = x1 + box.width; int y2 = y1 + box.height; if (isPointInsideBox(x1, y1, other)) { // upper left return true; } else if (isPointInsideBox(x1, y2, other)) { // lower left return true; } else if (isPointInsideBox(x2, y1, other)) { // upper right return true; } else if (isPointInsideBox(x2, y2, other)) { // lower right return true; } // No box points in other so no intersection return false; } /** * Given two rectangles, do the overlap at all? This includes one box being * completely contained by the other box. * * @param box1 a box to test, order does not matter * @param box2 the other box * @return true if the boxes overlap, false otherwise */ public static boolean doBoxesIntersect(Rectangle box1, Rectangle box2) { // Another custom test. We could of course use box1.intersects(box2) // but we can practice method calls and some boolean logic here. if (doesBoxIntersect(box1, box2)) { // At least one of box1's points must be inside box2 return true; } else if (doesBoxIntersect(box2, box1)) { // None of box1's points were in box2, but at least one of box2's points are inside box1 return true; } // No intersections in either direction return false; } } ================================================ FILE: game/Multiplayer.java ================================================ package game; import javax.swing.*; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; public class Multiplayer { private Field gameField; private int gamePort; private volatile boolean keepListening; private volatile boolean keepPlaying; private volatile boolean startNewGame; private volatile boolean disconnecting; private Server server; private Thread serverThread; private Thread clientThread; public Multiplayer(Field field) { this(field,8677); } public Multiplayer(Field field, int port) { gameField = field; gamePort = port; } public void startServer() { keepListening = true; keepPlaying = false; startNewGame = true; disconnecting = false; server = new Server(); serverThread = new Thread(server); serverThread.start(); gameField.setScore(2, "waiting..."); } public void joinGame(String otherServer) { clientThread = new Thread(new Client(otherServer)); clientThread.start(); } public void startGame() { startNewGame = true; } public void disconnect() { disconnecting = true; keepListening = false; // Are we in the middle of a game and regularly checking these flags? // If not, just close the server socket to interrupt the blocking accept() method. if (server != null && keepPlaying == false) { server.stopListening(); } keepPlaying = false; } class Server implements Runnable { ServerSocket listener; public void run() { Socket socket = null; try { listener = new ServerSocket(gamePort); while (keepListening) { socket = listener.accept(); // wait for connection InputStream in = socket.getInputStream(); BufferedReader reader = new BufferedReader( new InputStreamReader(in) ); OutputStream out = socket.getOutputStream(); PrintWriter writer = new PrintWriter(out, true); while (startNewGame) { // Create a new game, but assume this will be the last writer.println("NEW_GAME"); startNewGame = false; // If the client agrees, Send over the location of the trees String response = reader.readLine(); if (response != null && response.equals("OK")) { gameField.setupNewGame(); for (Tree tree : gameField.trees) { writer.println("TREE " + tree.getPositionX() + " " + tree.getPositionY()); } } else { System.err.println("Unexpected start response: " + response); System.err.println("Skipping game and waiting again."); keepPlaying = false; break; } // Start the action! writer.println("START"); response = reader.readLine(); keepPlaying = response.equals("OK"); while (keepPlaying) { try { if (gameField.trees.size() > 0) { writer.print("SCORE "); } else { writer.print("END "); keepPlaying = false; } writer.println(gameField.getScore(1)); response = reader.readLine(); if (response == null) { keepPlaying = false; disconnecting = true; } else { String parts[] = response.split(" "); switch (parts[0]) { case "END": keepPlaying = false; case "SCORE": gameField.setScore(2, parts[1]); break; case "DISCONNECT": disconnecting = true; keepPlaying = false; break; default: System.err.println("Warning. Unexpected command: " + parts[0] + ". Ignoring."); } } Thread.sleep(500); } catch(InterruptedException e) { System.err.println("Interrupted while polling. Ignoring."); } } // If we're not disconnecting, ask about playing again with the same player if (!disconnecting) { String message = gameField.getWinner() + " Would you like to ask them to play again?"; int myPlayAgain = JOptionPane.showConfirmDialog(gameField, message, "Play Again?", JOptionPane.YES_NO_OPTION); if (myPlayAgain == JOptionPane.YES_OPTION) { // If they haven't disconnected, ask if they want to play again writer.println("PLAY_AGAIN"); String playAgain = reader.readLine(); if (playAgain != null) { switch (playAgain) { case "YES": startNewGame = true; break; case "DISCONNECT": keepPlaying = false; startNewGame = false; disconnecting = true; break; default: System.err.println("Warning. Unexpected response: " + playAgain + ". Not playing again."); } } } } } if (socket.isConnected()) { // say goodbye writer.println("DISCONNECT"); socket.close(); } } } catch (SocketException se) { System.err.println("Disconnecting. Closing down server."); keepListening = false; } catch (IOException ioe) { System.err.println("Networking error. Closing down server."); ioe.printStackTrace(); keepListening = false; } catch (Exception e) { System.err.println("Other exception occurred. Closing down server."); e.printStackTrace(); keepListening = false; } finally { try { if (socket != null && !socket.isClosed()) { socket.close(); } } catch (IOException closingException) { System.err.println("Error closing client socket: " + closingException.getMessage()); } } } public void stopListening() { if (listener != null && !listener.isClosed()) { try { listener.close(); } catch (IOException ioe) { System.err.println("Error disconnecting listener: " + ioe.getMessage()); } } } } class Client implements Runnable { String gameHost; boolean startNewGame; public Client(String host) { gameHost = host; keepPlaying = false; startNewGame = false; } public void run() { try (Socket socket = new Socket(gameHost, gamePort)) { InputStream in = socket.getInputStream(); BufferedReader reader = new BufferedReader( new InputStreamReader( in ) ); OutputStream out = socket.getOutputStream(); PrintWriter writer = new PrintWriter( out, true ); // Assume the first game will start... startNewGame = true; while (startNewGame) { // ... but only the first startNewGame = false; // We expect to see the NEW_GAME command first String response = reader.readLine(); // If we don't see that command, bail if (response == null || !response.equals("NEW_GAME")) { System.err.println("Unexpected initial command: " + response); System.err.println("Disconnecting"); writer.println("DISCONNECT"); return; } // Yay! We're going to play a game. Acknowledge this command writer.println("OK"); // And now gather the trees and setup our field gameField.trees.clear(); response = reader.readLine(); while (response.startsWith("TREE")) { String[] parts = response.split(" "); int x = Integer.parseInt(parts[1]); int y = Integer.parseInt(parts[2]); Tree tree = new Tree(); tree.setPosition(x, y); gameField.trees.add(tree); response = reader.readLine(); } if (!response.equals("START")) { // Hmm, we should have ended the list of trees with a START, but didn't. Bail out. System.err.println("Unexpected start to the game: " + response); System.err.println("Disconnecting"); writer.println("DISCONNECT"); return; } else { // Yay again! We're starting a game. Acknowledge this command writer.println("OK"); keepPlaying = true; gameField.repaint(); } while (keepPlaying) { response = reader.readLine(); System.out.println("DEBUG: --" + response + "--"); String[] parts = response.split(" "); switch (parts[0]) { case "END": keepPlaying = false; case "SCORE": gameField.setScore(2, parts[1]); break; case "DISCONNECT": disconnecting = true; keepPlaying = false; break; default: System.err.println("Unexpected game command: " + response + ". Ignoring."); } if (disconnecting) { // We're disconnecting or they are. Acknowledge and quit. writer.println("DISCONNECT"); return; } else { // If we're not disconnecting, reply with our current score if (gameField.trees.size() > 0) { writer.print("SCORE "); } else { keepPlaying = false; writer.print("END "); } writer.println(gameField.getScore(1)); } } if (!disconnecting) { // Check to see if they want to play again response = reader.readLine(); if (response != null && response.equals("PLAY_AGAIN")) { // Do we want to play again? String message = gameField.getWinner() + " Would you like to play again?"; int myPlayAgain = JOptionPane.showConfirmDialog(gameField, message, "Play Again?", JOptionPane.YES_NO_OPTION); if (myPlayAgain == JOptionPane.YES_OPTION) { writer.println("YES"); startNewGame = true; } else { // Not playing again so disconnect. disconnecting = true; writer.println("DISCONNECT"); } } } } } catch (IOException e ) { System.err.println("Networking error. Closing down client."); e.printStackTrace(); } } } } ================================================ FILE: game/Physicist.java ================================================ package game; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import static java.awt.Color.*; public class Physicist implements GamePiece, ActionListener { Color color; int centerX, centerY; Apple aimingApple; float aimingAngle; float aimingForce; Field field; // Some helpers for optimizing the draw() method that can be called many, many times int x, y; // Boundary helpers private final int physicistHeight = (int)(1.5 * Field.PHYSICIST_SIZE_IN_PIXELS); private Rectangle boundingBox; /** * Create a default, blue physicist */ public Physicist() { this(BLUE); } /** * Create a Physicist of the given color */ public Physicist(Color color) { setColor(color); aimingAngle = 90.0f; aimingForce = 50.0f; getNewApple(); } public void setAimingAngle(Float angle) { aimingAngle = angle; } public void setAimingForce(Float force) { if (force < 0) { force = 0.0f; } aimingForce = force; } /** * Sets the color for the physicist. * * @param color */ public void setColor(Color color) { this.color = color; } @Override public void setPosition(int x, int y) { // Our position is based on the center of our dome, // but it will be drawn from the upper left corner. // Figure out the distance of that gap int offset = (int)(Field.PHYSICIST_SIZE_IN_PIXELS / 2.0f); this.centerX = x; this.centerY = y; this.x = x - offset; this.y = y - offset; boundingBox = new Rectangle(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, physicistHeight); } @Override public int getPositionX() { return centerX; } @Override public int getPositionY() { return centerY; } @Override public Rectangle getBoundingBox() { return boundingBox; } /** * Sets the active field once our physicist is being displayed. * * @param field Active game field */ public void setField(Field field) { this.field = field; } /** * Take the current apple away from the physicist. Returns the apple for * use in animating on the field. Note that if there is no current apple being * aimed, null is returned. * * @return the current apple (if any) being aimed */ public Apple takeApple() { Apple myApple = aimingApple; aimingApple = null; return myApple; } /** * Get a new apple ready if we need one. Do not discard an existing apple. */ public void getNewApple() { // Don't drop the current apple if we already have one! if (aimingApple == null) { aimingApple = new Apple(this); } } @Override public void draw(Graphics g) { // Make sure our physicist is the right color, then draw the semi-circle and box g.setColor(color); g.fillArc(x, y, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS, 0, 180); g.fillRect(x, centerY, Field.PHYSICIST_SIZE_IN_PIXELS, Field.PHYSICIST_SIZE_IN_PIXELS); // Do we have an apple to draw as well? if (aimingApple != null) { // Yes. Position the center of the apple on the edge of our semi-circle. // Use the current aimingAngle to determine where on the edge. double angleInRadians = Math.toRadians(aimingAngle); double radius = Field.PHYSICIST_SIZE_IN_PIXELS / 2.0; int aimingX = centerX + (int)(Math.cos(angleInRadians) * radius); int aimingY = centerY - (int)(Math.sin(angleInRadians) * radius); aimingApple.setPosition(aimingX, aimingY); aimingApple.draw(g); } } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } @Override public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("New Apple")) { getNewApple(); if (field != null) { field.repaint(); } } } } ================================================ FILE: game/Tree.java ================================================ package game; import java.awt.*; public class Tree implements GamePiece { int x, y; // Drawing helpers private Color leafColor = Color.GREEN.darker(); private Color trunkColor = new Color(101, 67, 33); private int trunkWidth = (int)(Field.TREE_WIDTH_IN_PIXELS * 0.2); private int trunkHeight = (int)(Field.TREE_WIDTH_IN_PIXELS * 1.1); private int trunkX, trunkY; // Boundary helpers private Rectangle boundingBox; @Override public void setPosition(int x, int y) { this.x = x; this.y = y; trunkX = x + (Field.TREE_WIDTH_IN_PIXELS - trunkWidth) / 2; trunkY = y + 2 * Field.TREE_WIDTH_IN_PIXELS - trunkHeight; boundingBox = new Rectangle(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_HEIGHT_IN_PIXELS); } @Override public int getPositionX() { return x; } @Override public int getPositionY() { return y; } @Override public Rectangle getBoundingBox() { return boundingBox; } @Override public void draw(Graphics g) { g.setColor(trunkColor); g.fillRect(trunkX, trunkY, trunkWidth, trunkHeight); g.setColor(leafColor); g.fillOval(x, y, Field.TREE_WIDTH_IN_PIXELS, Field.TREE_WIDTH_IN_PIXELS); } @Override public boolean isTouching(GamePiece otherPiece) { if (this == otherPiece) { // By definition we don't collide with ourselves return false; } return GameUtilities.doBoxesIntersect(boundingBox, otherPiece.getBoundingBox()); } }