Java Video Tutorial 53

Rotate Polygons JavaI first started the how to make Java games series to solve a bunch of problems. Many people in the forms are posting extremely complicated ways to rotate polygons in Java for example. It is very easy to rotate polygons!

I also saw many people struggle with other calculations like: Finding the length and angle of a line between vectors / Calculate the points of a rotating polygon / Calculate the area of irregular polygons / Find the center points of an irregular polygon / Repeat keyboard clicks if the user holds down a key…

This tutorial answers all of those questions and much more.

If you like videos like this, please tell Google

Sharing is always appreciated

Code From the Video

GAMEBOARD.JAVA

// Layout used by the JPanel
import java.awt.BorderLayout;

// Define color of shapes
import java.awt.Color;

// Allows me to draw and render shapes on components
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;

// Will hold all of my Rock objects
import java.util.ArrayList;

// Runs commands after a given delay
import java.util.concurrent.ScheduledThreadPoolExecutor;

// Defines time units. In this case TimeUnit.MILLISECONDS
import java.util.concurrent.TimeUnit;

import javax.swing.JComponent;
import javax.swing.JFrame;

public class GameBoard extends JFrame{
	
	// Height and width of the game board
	
	public static int boardWidth = 1000;
	public static int boardHeight = 800;
	
	// Used to check if a key is being held down
	
	public static boolean keyHeld = false;
	
	// Gets the keycode for the key being held down
	
	public static int keyHeldCode;
	
	public static void main(String [] args)
    {
            new GameBoard();
            
    }
	
	public GameBoard()
    {
    	// Define the defaults for the JFrame
    	
        this.setSize(boardWidth, boardHeight);
        this.setTitle("Java Asteroids");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // Handles executing code based on keys being pressed
        
        addKeyListener(new KeyListener() {

			@Override
			public void keyTyped(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode()==87)
			    {
					System.out.println("Forward");
			    } else if (e.getKeyCode()==83){
			    	System.out.println("Backward");
			    } 
			    
				// Id the d key is pressed set keyHeld as if it
				// was being held down. This will cause the ship to 
				// constantly rotate. keyHeldCode stores the keyCode for d
			    
			    else if (e.getKeyCode()==68){
			    	System.out.println("Rotate Right");
			    	
			    	keyHeldCode = e.getKeyCode();
			    	keyHeld = true;
			    	
			    } 
			    
			    // Same thing is done here as was done with the last
				// 65 is the keyCode for a
			    
			    else if (e.getKeyCode()==65){
			    	System.out.println("Rotate Left");
			    	
			    	keyHeldCode = e.getKeyCode();
			    	keyHeld = true;
			    }
				
			}

			// When the key is released this informs the code that
			// the key isn't being held down
			
			public void keyReleased(KeyEvent e) {
		
				keyHeld = false;
				
			}
        	
        });

        
        GameDrawingPanel2 gamePanel = new GameDrawingPanel2();

     // Make the drawing area take up the rest of the frame
        
        this.add(gamePanel, BorderLayout.CENTER);
        
        // Used to execute code after a given delay
        // The attribute is corePoolSize - the number of threads to keep in 
        // the pool, even if they are idle
        
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
		
        // Method to execute, initial delay, subsequent delay, time unit
        
		executor.scheduleAtFixedRate(new RepaintTheBoard2(this), 0L, 20L, TimeUnit.MILLISECONDS);
        
        // Show the frame
        
        this.setVisible(true);
    }
	
}


// Class implements the runnable interface
// By creating this thread we can continually redraw the screen
// while other code continues to execute

class RepaintTheBoard2 implements Runnable{

	GameBoard theBoard;
	
	public RepaintTheBoard2(GameBoard theBoard){
		this.theBoard = theBoard;
	}

	@Override
	public void run() {
		
		// Redraws the game board
		
		theBoard.repaint();
		
	}
	
}

@SuppressWarnings("serial")

// GameDrawingPanel is what we are drawing on

class GameDrawingPanel2 extends JComponent { 
	
	// Holds every Rock I create
	
	public static ArrayList<Rock> rocks = new ArrayList<Rock>();
	
	// Get the original x & y points for the polygon
	
	int[] polyXArray = Rock.sPolyXArray;
	int[] polyYArray = Rock.sPolyYArray;
	
	// Create a SpaceShip
	SpaceShip theShip = new SpaceShip();
	
	// Gets the game board height and weight
	
	int width = Lesson50.boardWidth;
	int height = Lesson50.boardHeight;
	
	// Creates 50 Rock objects and stores them in the ArrayList
	
	public GameDrawingPanel2() { 
		
		for(int i = 0; i < 10; i++){
			
			// Find a random x & y starting point
			// The -40 part is on there to keep the Rock on the screen
			
			int randomStartXPos = (int) (Math.random() * (Lesson50.boardWidth - 40) + 1);
			int randomStartYPos = (int) (Math.random() * (Lesson50.boardHeight - 40) + 1);
			
			// Add the Rock object to the ArrayList based on the attributes sent
			
			rocks.add(new Rock(Rock.getpolyXArray(randomStartXPos), Rock.getpolyYArray(randomStartYPos), 13, randomStartXPos, randomStartYPos));
			
		}
		
	} 
	
	public void paint(Graphics g) { 
		
		// Allows me to make many settings changes in regards to graphics
		
		Graphics2D graphicSettings = (Graphics2D)g; 
		
		AffineTransform identity = new AffineTransform();
		
		// Draw a black background that is as big as the game board
		
		graphicSettings.setColor(Color.BLACK);
		graphicSettings.fillRect(0, 0, getWidth(), getHeight());
		
		// Set rendering rules
		
		graphicSettings.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
		
		// Set the drawing color to white
		
		graphicSettings.setPaint( Color.WHITE ); 
		
		// Cycle through all of the Rock objects
		
		for(Rock rock : rocks){
			
			// Move the Rock polygon 
			
			rock.move(); 
			
			// Stroke the polygon Rock on the screen
			
			graphicSettings.draw(rock); 
			
		} 
		
		// Handles spinning the ship in the right direction when the key
		// is pressed and held
		
		if(GameBoard.keyHeld == true && GameBoard.keyHeldCode == 68){
			
			SpaceShip.rotationAngle += 10;
			
		} else
			
			if(GameBoard.keyHeld == true && GameBoard.keyHeldCode == 65){
				
				SpaceShip.rotationAngle -= 10;
				
			}
		
		theShip.move();
		
		// Sets the origin on the screen so rotation occurs properly
		
		graphicSettings.setTransform(identity);
		
		// Moves the ship to the center of the screen
		
		graphicSettings.translate(GameBoard.boardWidth/2,GameBoard.boardHeight/2);
		
		// Rotates the ship
		
		graphicSettings.rotate(Math.toRadians(SpaceShip.rotationAngle));
		
		graphicSettings.draw(theShip);
		
		
	} 
	
}

SPACESHIP.JAVA

import java.awt.Polygon;
import java.awt.*;

@SuppressWarnings("serial")
public class SpaceShip extends Polygon{
	
	// Upper left hand corner of space ship
	
	static int uLeftXPos = 500, uLeftYPos = 400;
	
	// Center of space ship
	
	int centerX = 515;
	int centerY = 415;
	
	// Determines the direction the ship moves
	
	static int xDirection = 0;
	static int yDirection = 0;
	
	// Get the board width and height
	
	int width = GameBoard.boardWidth;
	int height = GameBoard.boardHeight;
	
	// Will hold the x & y coordinates for the ship
	// Everything is based on coordinates from the center
	// It is done this way so that rotation works properly
		
	public static int[] polyXArray = {-13,14,-13,-5,-13};
	public static int[] polyYArray = {-15,0,15,0,-15};
	
	// Defines if the ship should rotate
	
	static int rotationAngle = 0;
	
	// Holds the area of the ship polygon
	
	static double areaOfShip;
	
	// Creates a new space ship
	
	public SpaceShip(){
			
			// Creates a Polygon by calling the super or parent class of Rock Polygon
			
			super(polyXArray, polyYArray, 5);
			
			getShipArea();
			
			updateCenterPoints();
			
	}
	
	public void move(){
		
		// Get the upper left and top most point for the Polygon
		// This will be dynamic later on
		/*
		int uLeftXPos = super.xpoints[0]; 
		int uLeftYPos = super.ypoints[0];
		
		// If the ship hits a wall it will go in the opposite direction
		
		if (uLeftXPos < 0 || (uLeftXPos + 25) > width) xDirection = -xDirection; 
		
		if (uLeftYPos < 0 || (uLeftYPos + 50) > height) yDirection = -yDirection; 
		
		// Change the values of the points for the Polygon
		
		for (int i = 0; i < super.xpoints.length; i++){
			
			super.xpoints[i] += xDirection;
			super.ypoints[i] += yDirection;
			
		}
		*/
		
		super.xpoints = SpaceShip.polyXArray;
		super.ypoints = SpaceShip.polyYArray;
		
	}
	
	// This is here just as a demonstration. IT ISN'T USED
	
	public void rotateRight(){

		// Calculate the length of the line from 1 vector to the next
		// SQRT ((x1-x2)^2 + (y1-y2)^2)
		// Find the angle of the line from 1 vector to the next
		// x1*x2 + y1*y2 = C
		// SQRT ( x1^2 + x2^2 ) = A
		// SQRT ( y1^2 + y2^2 ) = B
		// ARCCOS ( C / A * B) = Angle of line
		// x2 = x1 + cos(angleOfLine) * length
		// y2 = y1 + sin(angleOfLine) * length
		
		// How to find new x & y points for the rotating polygon
		// This works on paper, but doesn't with Java because errors occur
		// because we have to convert floats into ints
		
		/*
		
		for(int i = 0; i < super.xpoints.length; i++){
			
			polyXArray[i] = this.centerX + (int) ((super.xpoints[i] - this.centerX) * cos(angle) -
					(super.ypoints[i] - this.centerY) * sin(angle));
			
			polyYArray[i] = this.centerY + (int) ((super.xpoints[i] - this.centerX) * sin(angle) +
					(super.ypoints[i] - this.centerY) * cos(angle));
			
		}
		*/
		
	}
	
	// How to find the ships area
	// This is here just as a demonstration. IT ISN'T USED
	
	public void getShipArea(){
		
		// Calculate the area of the polygon
				
		// Temporary sum holder
				
		double sumHolder1 = 0, sumHolder2 = 0;
				
		// Find area of polygon 
		// Multiply x coordinate of each vertex by the y coordinate
		// of the next vertex. Multiply y coordinate of each vertex 
		// by the x coordinate of the next vertex. Subtract these
		// sums and divide by 2 to get the polygons area
				
		// X Points {-13,14,-13,-5,-13}
		// Y Points {-15,0,15,0,-15}
		// -13 * 0
		// 	14 * 15 ... (Do the same for Y)
		
		for(int i = 0, n = 1; i < 4; i++, n++){
					
			sumHolder1 += super.xpoints[i] * super.ypoints[n];
					
		}
				
		for(int i = 0, n = 1; i < 4; i++, n++){
					
			sumHolder2 += super.ypoints[i] * super.xpoints[n];
					
		}
				
		areaOfShip = Math.abs(sumHolder1 - sumHolder2) / 2;
		
		System.out.println("Area of Ship: " + areaOfShip);
		
	}
	
	// How to find the ships center points
	// This is here just as a demonstration. IT ISN'T USED
	
	public void updateCenterPoints(){
		
		// Get the origin points
		// X Points {-13,14,-13,-5,-13}
		// Y Points {-15,0,15,0,-15}
		// Do the following for each row in array
		// (-13 + 14) * (-13 * 0) - (14 * -15)
		
		double xOrigin = 0, yOrigin = 0;
		
		// X Points {-13,14,-13,-5,-13};
		// Y Points {-15,0,15,0,-15};
		// Do the following for each row in array
		// (-13 + 14) * (-13 * 0) - (14 * -15)
		
		for (int j=0; j < 4; j++){

			xOrigin += (super.xpoints[j] + super.xpoints[j+1]) * ((super.xpoints[j]*super.ypoints[j+1]) - (super.xpoints[j+1]*super.ypoints[j]));
			yOrigin += (super.ypoints[j] + super.ypoints[j+1]) * ((super.xpoints[j]*super.ypoints[j+1]) - (super.xpoints[j+1]*super.ypoints[j]));
		}
		
		this.centerX = (int) (xOrigin/(6 * areaOfShip));
		
		this.centerY = (int) (yOrigin/(6 * areaOfShip));
		
		System.out.println("Center X: " + this.centerX);
		System.out.println("Center Y: " + this.centerY);
	}
	
}

ROCK.JAVA

import java.awt.Polygon;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;

// Extending the Polygon class because I'm drawing Polygons

class Rock extends Polygon{
	
	// Upper left hand corner of the Polygon
	
	int uLeftXPos, uLeftYPos;
	
	// Used to change the direction of the asteroid when 
	// it hits something and determines how fast it moves
	
	int xDirection = 1;
	int yDirection = 1;
	
	// Define rock height and width
	
	int rockWidth = 26;
	int rockHeight = 31;
	
	// Copy of the Rock ArrayList
	
	// Holds every Rock I create
	
	static ArrayList<Rock> rocks = new ArrayList<Rock>();
	
	// For JApplet
	// int width = ExampleBoard.WIDTH;
	// int height = ExampleBoard.HEIGHT;
	
	// Get the board width and height
	
	int width = GameBoard.boardWidth;
	int height = GameBoard.boardHeight;
	
	// Will hold the x & y coordinates for the Polygons
	
	int[] polyXArray, polyYArray;
	
	// x & y positions available for other methods
	// There will be more Polygon points available later
	
	public static int[] sPolyXArray = {10,17,26,34,27,36,26,14,8,1,5,1,10};
	public static int[] sPolyYArray = {0,5,1,8,13,20,31,28,31,22,16,7,0};
	
	// Creates a new asteroid 
	
	public Rock(int[] polyXArray, int[] polyYArray, int pointsInPoly, int randomStartXPos, int randomStartYPos){
		
		// Creates a Polygon by calling the super or parent class of Rock Polygon
		
		super(polyXArray, polyYArray, pointsInPoly);
		
		// Randomly generate a speed for the Polygon
		
		this.xDirection = (int) (Math.random() * 4 + 1);
		
		this.yDirection = (int) (Math.random() * 4 + 1);
		
		// Holds the starting x & y position for the Rock
		
		this.uLeftXPos = randomStartXPos;
		
		this.uLeftYPos = randomStartYPos;
		
	}

	public void move(){
		
		// Get the upper left and top most point for the Polygon
		// This will be dynamic later on
		
		int uLeftXPos = super.xpoints[0]; 
		
		int uLeftYPos = super.ypoints[0];
		
		// If the Rock hits a wall it will go in the opposite direction
		
		if (uLeftXPos < 0 || (uLeftXPos + 25) > width) xDirection = -xDirection; 
		
		if (uLeftYPos < 0 || (uLeftYPos + 50) > height) yDirection = -yDirection; 
		
		// Change the values of the points for the Polygon
		
		for (int i = 0; i < super.xpoints.length; i++){
			
			super.xpoints[i] += xDirection;
			super.ypoints[i] += yDirection;
			
		}
		
	}
	
	// public method available for creating Polygon x point arrays
	
	public static int[] getpolyXArray(int randomStartXPos){
		
		// Clones the array so that the original shape isn't changed for the asteroid
		
		int[] tempPolyXArray = (int[])sPolyXArray.clone();
		
		for (int i = 0; i < tempPolyXArray.length; i++){
			
			tempPolyXArray[i] += randomStartXPos;
			
		}
		
		return tempPolyXArray;
		
	}
	
	// public method available for creating Polygon y point arrays
	
	public static int[] getpolyYArray(int randomStartYPos){
		
		// Clones the array so that the original shape isn't changed for the asteroid
		
		int[] tempPolyYArray = (int[])sPolyYArray.clone();
		
		for (int i = 0; i < tempPolyYArray.length; i++){
			
			tempPolyYArray[i] += randomStartYPos;
			
		}
		
		return tempPolyYArray;
		
	}
	
}

4 Responses to “Java Video Tutorial 53”

  1. Mei Walsh says:

    Thank you for the series of wonderful tutorial. I am currently working on a slime mold simulation project and your tutorials give me plenty to start with.

    I do have a question: is there a specific reason why you are extending GameDrawingPanel2 using JComponent instead of JPanel? has it this something to do with using awt and not swing?

    Mei

    • Derek Banas says:

      Thank you 🙂 I’m glad I was able to help. JComponent is just a base class for objects, while JPanel is used normally for layout purposes. Since I was just drawing on the screen I didn’t need the extra stuff that comes with JPanel

  2. isaac says:

    now joining the java party
    thanks a lot

Leave a Reply

Your email address will not be published.

Google+