HI all, this app is a simple game where the objective is to avoid incoming balls (adversaries) however at present I can only figure out how to get one ball on screen at a time. What I'd really like is, when a player hits 100 points, a second ball is spawned (thus making the game harder) I would like the same to occur at 200 points (3 balls), 400 pts (4 balls) and so forth. I am sure there is a simple way of doing this. what I have tried is creatin new instances of Ball, but to no avail.any help would be greatly appreciated.
cheers

/* A game that is Saucer-like
 * Phillip Wells
 */

//to do - make more balls when 100 points met

import java.awt.*;
import java.awt.Toolkit.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
import java.util.concurrent.*;

public class Saucer extends JFrame {
    public static final int WIDTH = 400;
    public static final int HEIGHT = 400;
    Image img;
    private PaintSurface canvas;
    
    public void init() {
        this.setSize(WIDTH, HEIGHT);
        this.setTitle("Saucer");
        canvas = new PaintSurface();
       img = Toolkit.getDefaultToolkit().getImage("space.jpg");
        this.getContentPane().add(canvas, BorderLayout.CENTER);
        ScheduledThreadPoolExecutor executor =
                new ScheduledThreadPoolExecutor(3);
        executor.scheduleAtFixedRate(new AnimationThread(this),
                0L, 20L, TimeUnit.MILLISECONDS);
        setVisible(true);
    }
    
    
    
    class AnimationThread implements Runnable {
        JFrame c;
        
        public AnimationThread(JFrame c) {
            this.c = c;

        }
        
        public void run() {
            c.repaint();
            
        }
    }
    
    class PaintSurface extends JComponent {
        int saucer_x = 0; // start position of saucer
        int saucer_y = 180;
        
        int score = 0;
              
        Ball Ball;
        
        Color[] color = {Color.RED, Color.ORANGE,
        Color.MAGENTA, Color.ORANGE,
        Color.CYAN, Color.BLUE};
        int colorIndex;
        
        public PaintSurface() {
            addMouseMotionListener(new MouseMotionAdapter() {
                public void mouseMoved(MouseEvent e) {
                    saucer_x = e.getX() -30;
                    saucer_y = e.getY() -0;
                }
            } );
            Ball = new Ball(20);
        }
        
        public void paint(Graphics g) {
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
                    Shape saucer = new Ellipse2D.Float(
                    saucer_x, saucer_y, 60, 10); // saucer dimensions
            g2.drawImage(img, 0, 0, this);
            g2.setColor(color[colorIndex % 6]);
            
            if (Ball.intersects(saucer_x, saucer_y, 60, 10)
            && Ball.y_speed > 0) {
            
                Ball = new Ball(20);
                score -= 100;
                colorIndex = 0;
            }
            
            else if (Ball.getY() + Ball.getHeight() >= Saucer.HEIGHT)
            {
                Ball = new Ball(20);
                score += 10;
                colorIndex = 0;
            }
            Ball.move();
            g2.fill(Ball);
            
            g2.setColor(Color.GREEN);
            g2.fill(saucer);
            g2.drawString("Score: " + score, 300, 20);       
            if (score == 20)
            {
                //code needed to create a second ball on screen
            }
        }
    }
    
    class Ball extends Ellipse2D.Float {
        public int x_speed, y_speed;
        private int d;
        private int width = Saucer.WIDTH;
        private int height = Saucer.HEIGHT;
        
        public Ball(int diameter) {
            super((int)(Math.random() * (Saucer.WIDTH - 20) + 1),
                    0, diameter, diameter);
            this.d = diameter;
            this.x_speed = (int)(Math.random() * 5 + 5);
            this.y_speed = (int)(Math.random() * 5 + 5);
        }
        
        public void move() {
            if (super.x < 0 || super.x > width - d)
                x_speed = -x_speed;
            if (super.y < 0 || super.y > height - d)
                y_speed = -y_speed;
            super.x += x_speed;
            super.y += y_speed;
        }
    }
}

Recommended Answers

All 13 Replies

Hy,
I suggest you to make an array of Balls (the dimension of the array is the maximum number of balls in your game). When the score is 20 for example, the counter of your current balls increases and you instantiate the Ball[counter-1]. You move all the Ball elements which are != null.
Good luck!

many thanks, I was toying with the idea of arrays myself but i find them a little messy! will let you know how i get on when I've tinkered. cheers

hmmm, a little stuck still...have created array
Ball[] Ball = new Ball[10]
but im unsure as to how to initialise (and where) this array.any suggestions?

when you arrive at the score at which you want to add one more ball in the game you initialize Ball[counter -1] = new Ball(**); You have also to declare one boolean variable that helps you to initialize only once that Ball. After that:

for (int i=0;i<counter;i++)
Ball[i].move();
commented: great help, much appreciated! +1

Having tried that, it seems that I have to redo large amounts of the code as I am getting errors related to Ball. for example, when I state Ball = new Ball(20); to create a new ball, i am getting the error 'incompatible types - found Ball but expected Ball[]' so I see it is expecting an array index but i need the (20) as that is the diameter of Ball. cheers

Got it! thanks very much Cyberyn here is the updated version............

/* A game that is Saucer-like
 * Phillip Wells
 */
 
//to do - make more balls when 100 points met
 
import java.awt.*;
import java.awt.Toolkit.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
import java.util.concurrent.*;
import java.util.ArrayList;
 
public class Saucer extends JFrame {
	
    public static final int WIDTH = 400;
    public static final int HEIGHT = 400;
    Image img;
    private PaintSurface canvas;
    
    
    public void init() {
        this.setSize(WIDTH, HEIGHT);
        this.setTitle("Saucer");
        canvas = new PaintSurface();
       img = Toolkit.getDefaultToolkit().getImage("space.jpg");
        this.getContentPane().add(canvas, BorderLayout.CENTER);
        ScheduledThreadPoolExecutor executor =
                new ScheduledThreadPoolExecutor(3);
        executor.scheduleAtFixedRate(new AnimationThread(this),
                0L, 20L, TimeUnit.MILLISECONDS);
        setVisible(true);
        
        ///////////////////////////////
        // this function is necessary to 
        // stop the process when the user 
        // closes the window
        ///////////////////////////////
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    
    class AnimationThread implements Runnable {
        JFrame c;
        
        public AnimationThread(JFrame c) {
            this.c = c;
        }
        
        public void run() {
            c.repaint();
        }
    }
    
    class PaintSurface extends JComponent {
        int saucer_x = 0; // start position of saucer
        int saucer_y = 180;
        
        int score = 0;
        
        ///////////////////////////////
        //use an Array of Balls     
        ///////////////////////////////
        ArrayList<Ball> balls;
        
        Color[] color = {Color.RED, Color.ORANGE,
        Color.MAGENTA, Color.ORANGE,
        Color.CYAN, Color.BLUE};
        int colorIndex;
        
        public PaintSurface() {
            addMouseMotionListener(new MouseMotionAdapter() {
                public void mouseMoved(MouseEvent e) {
                    saucer_x = e.getX() -30;
                    saucer_y = e.getY() -0;
                }
            } );            
          
        	///////////////////////////////
            //initialize the array
            balls = new ArrayList<Ball>();
            //create the first ball:
            balls.add(new Ball(20));
        	///////////////////////////////
        }
        
        public void paint(Graphics g) {
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
                    Shape saucer = new Ellipse2D.Float(
                    saucer_x, saucer_y, 60, 10); // saucer dimensions
            g2.drawImage(img, 0, 0, this);
            g2.setColor(color[colorIndex % 6]);
            
        	///////////////////////////////
            //loop to iterate among the balls:
            for(int i=balls.size()-1; i>=0; i--){
            	
            	Ball ball = balls.get(i);
            	if (ball.intersects(saucer_x, saucer_y, 60, 10)
	            		&& ball.y_speed > 0) {
	            
	                //set the ball position, 
	                ball.reset();
	                
	                
	                score -= 100;
	                colorIndex = 0;
	            }
	            
	            else if (ball.getY() + ball.getHeight() >= Saucer.HEIGHT)
	            {
	                ball.reset();
	                
	                score += 10;
	                colorIndex = 0;
	            }
	            ball.move();
	            g2.fill(ball);
            }
       		            
            g2.setColor(Color.GREEN);
            g2.fill(saucer);
            g2.drawString("Score: " + score, 300, 20);       
            if (score == 20 && balls.size()<2)
            {
                //code to create a second ball on screen
                      		
                balls.add(new Ball(20));
        		
            }
             if (score == 50 && balls.size()<3)
            {
                
                
        		balls.add(new Ball(20));

            }
            if (score == 100 && balls.size()<4)
            {
                               
        		balls.add(new Ball(20));
        		
            }
            if (score == 150 && balls.size()<5)
            {
               
                balls.add(new Ball(20));
        	
            }
            if (score == 200 && balls.size()<6)
            {
               
                balls.add(new Ball(20));
        		
            }
            if (score == 250 && balls.size()<7)
            {
                
                balls.add(new Ball(20));
        		
            }
            if (score == 300 && balls.size()<8)
            {
                balls.add(new Ball(20));
        		
            }
        }
    }
    
    class Ball extends Ellipse2D.Float {
        public int x_speed, y_speed;
        private int d;
        private int width = Saucer.WIDTH;
        private int height = Saucer.HEIGHT;
        
        public Ball(int diameter) {
            super((int)(Math.random() * (Saucer.WIDTH - 20) + 1),
                    0, diameter, diameter);
            this.d = diameter;
            this.x_speed = (int)(Math.random() * 5 + 5);
            this.y_speed = (int)(Math.random() * 5 + 5);
        }
        
        public void move() {
            if (super.x < 0 || super.x > width - d)
                x_speed = -x_speed;
            if (super.y < 0 || super.y > height - d)
                y_speed = -y_speed;
            super.x += x_speed;
            super.y += y_speed;
        }
        
        ///////////////////////////////
        //added this new function ;)
        public void reset(){
        	this.x = (float)(Math.random() * (Saucer.WIDTH - 20) + 1);
        	this.y = 0;
            this.x_speed = (int)(Math.random() * 5 + 5);
            this.y_speed = (int)(Math.random() * 5 + 5);
        }
        ///////////////////////////////
    }
    
    
        
    public static void main(String[] args){
    	Saucer frame = new Saucer();
    	frame.setVisible(true);
    	frame.init();
    }
}

Looks good. One additional change I would suggest though: put the code that updates the ball postions and creates a new one when needed over into a separate method like "updateBalls()" and call that before repaint in your animation loop. Repaint should generally make as few calculations as possible, since it gets called for all sorts of reasons at any time (window resizings, parts of the window becoming occluded, etc). You have the ball coordinates all encapsulated in the Ball class, so this should be very easy to extract to a new method.
Your paint() method can just loop the array list and draw each ball as needed. Always try to keep paint() (or paintComponent() ) as narrowly focused on it's task of just rendering things as possible and your interface will be much happier and lag-free.

ok Ezzaral, thanks for your suggestion will make an attempt at implementation! In addition I have noticed that the middle far-left of the screen (whilst in game) is impervious to ball attacks so a user can just position the saucer there and accumulate points hasle free - any ideas about that ?

Either make sure the balls can get there or don't let the user go that far into the corners.

the thing is, the balls shoot down from the top heading in a right direction (as we look at screen) and bounce off right wall. I need them to shoot in both directions but i do not see how to alter this. cheers

Just randomized the sign of the speed as well.

-1 + (Math.random() * 2)

or something like that should do it. Whatever you want your neg range to start at and then add random() times twice the speed range.

which math instance shall i insert that into ?

lol its ok - school boy error moment. got it sorted now many thanks to you Ezzaral!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.