📜 ⬆️ ⬇️

Playing pure Java from beginner, for beginners

I am a beginner Java programmer, and my path has been traveled by thousands.



At first there is a long and painful choice of the Most Correct Book, then the first enthusiasm from the work of the program listings that were reprinted from it. Then awareness of the growing coolness and professionalism. Falling into a pit of your own insignificance, when you try to write something yourself. And a long way up.
')
In my case, the Two Book of “Java. The library of a professional. ”By Kay Horstmann and Gary Cornell, and the very first book that opened the door to the world of Java is Jacob Fayn“ Java Programming for Children, Parents, Grandparents and Grandmothers ”.

In order to consolidate the knowledge that is trying to scatter, which stubbornly tried to remain on the pages of Smart Books, and not in my head, I decided to write a simple game. The main task was to write without the use of third-party libraries.

The general idea (not mine, but taken from the Chain Rxn flash game)

On a rectangular playing field, depending on the level, a number of balls appear that are worn over it, at different speeds, reflecting from the walls. The player presses the mouse cursor on the playing field, and at the point of pressing a growing ball appears, which increases to a predetermined radius. After a certain time, the remaining balls, if they collide with it, stop, increase in size, and also decrease and disappear.

For each level, a specific goal is how many balls should be “knocked out”.



Implementation.

To begin with, the GameConstants interface was created, in which all the basic constants were placed. For all classes, the implements of GameConstants were specified:

GameConstants interface
public interface GameConstants { public final int DEFAULT_WIDTH = 600;//   public final int DEFAULT_HEIGHT = 300; //   public final int DELAY = 8; //  «»  public final int BASERADIUS=5; //   public final int LIFETIME=1300; // «»  public final int MAXRADIUS=25; //   public final int STARTQNTBALLS=10; //     } 


Then the Ball class was created. Each object of this class has its own set of coordinates along the x and y axes, the variables dx and dy, in which the increment of coordinates per unit of time is recorded (essentially, speed), the values ​​of the radius and increment of the radius, as well as the color and unique identifier. The identifier will come in handy later when we track collisions.

Also, each ball has an inAction variable characterizing its current state, namely, 0 - before the collision, 1 - collision and growth, 2 - life and size reduction.

A timer has been added to the class, the purpose of which is to track the ball “life” time, starting from the moment the maximum size was reached. After the time specified in the above interface (LIFETIME) has expired, the size increment will become negative, and upon reaching the zero size, the object will be deleted.

Ball class
 public class Ball implements GameConstants { private int inAction; //   private int x; //   x  y private int y; private int dx; //   x  y private int dy; private int radius; // private int dRadius; //  private Color color; // private static int count; public final int id=count++; //  ()  private static int score; //  private Timer gameTimer; private TimerTask gameTimerTask; //     // Ball Ball(int x, int y, int dx, int dy, int radius, Color color, int inAction, int dRadius){ this.x=x; this.y=y; this.dx=dx; this.dy=dy; this.radius=radius; this.color=color; this.inAction=inAction; this.dRadius=dRadius; gameTimer = new Timer(); } //     public Ellipse2D getShape(){ return new Ellipse2D.Double(x-radius, y-radius, radius*2, radius*2); } //    : public void moveBall(BallComponent ballComponent){ x+=dx; y+=dy; radius+=dRadius; if(x<=0+radius){ x=radius; dx=-dx; } if (x>=DEFAULT_WIDTH-radius){ x=DEFAULT_WIDTH-radius; dx=-dx; } if(y<=0+radius){ y=radius; dy=-dy; } if (y>=DEFAULT_HEIGHT-radius){ y=DEFAULT_HEIGHT-radius; dy=-dy; } for(Ball ballVer: ballComponent.listBall){ // -        Ball, //  ,    «» , //  (ballVer),        //       (    id) if(inAction==0) if((Math.sqrt(Math.pow(x-ballVer.x,2)+Math.pow(y-ballVer.y,2)))<=radius+ballVer.radius && id!=ballVer.id && (ballVer.inAction==1 || ballVer.inAction==2)) { ballComponent.score++; ballComponent.totalScore++; dx=dy=0; inAction=1; ballComponent.setBackground(ballComponent.getBackground().brighter()); } if(inAction==1){ dRadius=1; if (radius>=MAXRADIUS){ inAction=2; dRadius=0; // ,     ,     gameTimerTask = new gameTimerTask(this); gameTimer.schedule(gameTimerTask, LIFETIME); } } //    -      if(inAction==2 && radius<=0){ ballComponent.listBall.remove(this); }}} //,    LIFETIME,     : class gameTimerTask extends TimerTask{ private Ball ballTimer; public gameTimerTask(Ball ball) { // TODO Auto-generated constructor stub this.ballTimer = ball; } public void run() { // TODO Auto-generated method stub ballTimer.dRadius=-1; } } } 


In the moveBall function, the position of the ball, and its size are tracked. To do this, the velocity is added to the coordinate, which is in the BallGame class below , is set as a random variable, and its increment is added to the base radius (set to zero).
  x+=dx; y+=dy; radius+=dRadius; 

The BallComponent class inherits JPanel, and is responsible for drawing the playing field directly. It also creates a list in which Ball objects are placed and the score is kept. After the lifetime of the object, it is removed from the list.

Class BallComponent
 public class BallComponent extends JPanel implements GameConstants { List<Ball> listBall = new CopyOnWriteArrayList<>(); boolean startClick; public int score=0; public int totalScore=0; //  Ball   public void addBall(Ball b){ listBall.add(b); } public void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; for(Ball ball: listBall){ g2d.setColor(ball.getColor()); g2d.fill(ball.getShape()); } } public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); }} 


Further, in the best traditions of the educational examples of their Horstmann and Cornell, the main BallGame class was created, from which the BallGameFrame () class was called:

BallGame class
 public class BallGame implements GameConstants { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { JFrame ballFrame = new BallGameFrame(); ballFrame.setVisible(true); }}); }} 


The BallGameFrame class that inherits the JFrame creates an outer shell for the playing field, that is, it is responsible for the placement of elements, the processing of mouse event listeners, and the display of informational messages. It also contains the startGame () function, which is called by a mouse click. This function starts a stream in which the endless game cycle is spinning.

Class BallGameFrame
 class BallGameFrame extends JFrame implements GameConstants{ private int level=1; //  private int ballQnt; private BallComponent ballComponent; private MousePlayer mousePlayerListener; // public BallGameFrame() { ballQnt=STARTQNTBALLS; setTitle("BallGame"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ballComponent = new BallComponent(); ballComponent.setBackground(Color.DARK_GRAY); mousePlayerListener = new MousePlayer(); add(ballComponent, BorderLayout.CENTER); final JPanel buttonPanel = new JPanel(); final JButton startButton = new JButton(" ."); buttonPanel.add(startButton); final JLabel scoreLabel = new JLabel(); buttonPanel.add(scoreLabel); startButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { ballComponent.addMouseListener(mousePlayerListener); ballComponent.addMouseMotionListener(mousePlayerListener); startButton.setVisible(false); ballComponent.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); startGame(scoreLabel, ballQnt); }}); add(buttonPanel, BorderLayout.SOUTH); pack(); } public void startGame(JLabel scoreLabel, int ballQnt){ Runnable r = new BallRunnable(ballComponent, scoreLabel, level, ballQnt); Thread t = new Thread(r); t.start(); } //   MousePlayer,     : class MousePlayer extends MouseAdapter{ public void mouseClicked(MouseEvent e) { //   Random random = new Random(); //  ,      //   (),   Ball ball = new Ball(e.getX(), e.getY(), 0, 0, BASERADIUS, new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)), 1, 1); ballComponent.startClick=true; ballComponent.addBall(ball); //  ,       ,        ballComponent.removeMouseListener(mousePlayerListener); ballComponent.removeMouseMotionListener(mousePlayerListener); ballComponent.setCursor(Cursor.getDefaultCursor()); }}} 


The BallRunnable class in which the main action takes place.

Class BallRunnable
 class BallRunnable implements Runnable, GameConstants{ private BallComponent ballComponent; private JLabel scoreLabel; private int level, ballQnt; private MousePlayer mousePlayerListener; private int goal; public BallRunnable(final BallComponent ballComponent, JLabel scoreLabel, int level, int ballQnt) { this.ballComponent = ballComponent; this.scoreLabel = scoreLabel; this.level=level; this.ballQnt=ballQnt; this.goal=2; } class MousePlayer extends MouseAdapter{ public void mousePressed(MouseEvent e) { Random random = new Random(); Ball ball = new Ball(e.getX(), e.getY(), 0, 0, BASERADIUS, new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)), 1, 1); ballComponent.addBall(ball); ballComponent.startClick=true; ballComponent.removeMouseListener(mousePlayerListener); ballComponent.removeMouseMotionListener(mousePlayerListener); ballComponent.setCursor(Cursor.getDefaultCursor()); }} public void run(){ while(true){ try{ mousePlayerListener = new MousePlayer(); ballComponent.addMouseListener(mousePlayerListener); ballComponent.addMouseMotionListener(mousePlayerListener); //      ballComponent.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); //     int countInWork=1; //    //    //    for (int i=0;i<ballQnt; i++){ Random randomX = new Random(); Random randomY = new Random(); Ball ball = new Ball(randomX.nextInt(DEFAULT_WIDTH), randomY.nextInt(DEFAULT_HEIGHT), randomX.nextInt(2)+1, randomY.nextInt(2)+1, BASERADIUS, new Color(randomX.nextInt(255),randomX.nextInt(255),randomX.nextInt(255)), 0, 0); ballComponent.addBall(ball); } //     while (countInWork!=0){ countInWork=0; if(!ballComponent.startClick) { EventQueue.invokeLater(new Runnable() { public void run() { // TODO Auto-generated method stub scoreLabel.setText(":  "+ goal+"   "+ ballQnt); } } ); countInWork=1; } for(Ball ball: ballComponent.listBall){ if((ball.inAction()==1 || ball.inAction()==2)) countInWork++; //    ball.moveBall(ballComponent); ballComponent.repaint(); if(ballComponent.startClick){ //   EventQueue.invokeLater(new Runnable() { public void run() { scoreLabel.setText(": "+ level+",   "+ballComponent.score+"  "+ballQnt); }}); }} Thread.sleep(DELAY); } } catch (InterruptedException ex){ ex.printStackTrace(); } ballComponent.listBall.clear(); ballComponent.repaint(); //  if(ballComponent.score<goal) { EventQueue.invokeLater(new Runnable() { public void run() { scoreLabel.setText("   !"); } }); JOptionPane.showMessageDialog(ballComponent, "   . \n : "+ ballComponent.totalScore+".\n   ."); ballComponent.startClick=false; ballComponent.score=0; ballComponent.setBackground(Color.DARK_GRAY); } else{ EventQueue.invokeLater(new Runnable() { public void run() { scoreLabel.setText(" !!!"); } }); ballComponent.startClick=false; level++; ballQnt++; goal++; ballComponent.setBackground(Color.DARK_GRAY); ballComponent.score=0; JOptionPane.showMessageDialog(ballComponent, " "+level+".\n:  "+ goal+"   "+ ballQnt); }}} 


Note that the display of messages on the screen takes place in a separate thread. You can read more about this in Horstmann, chapter 14, "Multi-threaded processing," section "Threads and the Swing Library."

With each level increases the total number of balls, and the goal (how many you need to knock out). Initially, I did it so that the player had to first knock out a lot of balls (for example, 8 out of 10), but it seemed boring for the testers, and the game was thrown. Therefore, I decided to gradually increase the degree of complexity inadequate .

The official record is level 86. The author himself went through a maximum of level 15.

So let me leave. Waiting for advice, criticism and support.

Source: https://habr.com/ru/post/266117/


All Articles