JAVA 프로그래밍

문제

왼쪽에서 날아오는 총알을 피해 살아남아야 하는 게임입니다 이를 해결하는 다음 프로그램을 해석하세요 
 

알고리즘

 뷰 패널 
   키리스너에 방향키로 이동하는 캐릭터를 등록
   주기적으로 활성화되는 타이머 등록
      최신 정보 업데이트
      화면 출력

폭탄 피하기 패널
   최신 정보 업데이트
      모든 폭탄은 아래로 떨어지며, 주기적으로 새 폭탄을 투하
      캐릭터는 좌우로 이동하고 HP 업데이트
         캐릭터와 폭탄이 충돌하면 캐릭터 HP 감소
   화면 출력
      먼저, 제한시간동안 폭탄을 피해 살아남으면 성공 출력
      또는, HP가 더 이상 없으면 실패 출력
      아니면, 캐릭터 및 폭탄의 현재 게임 상황 출력

총알 피하기 패널
   최신 정보 업데이트
      모든 총알은 오른쪽으로 이동하며, 주기적으로 새 총알을 발사
      캐릭터는 상하로 이동하고 HP 업데이트

방향키로 이동하는 객체
   키를 누르면 상하좌우 이동방향 설정
   키를 해제하면 이동방향 해제
   이동 허용 범위내에서만 객체 이동

충돌가능한 객체
   폭탄 초기화 : 위쪽(임의위치)에서 아래쪽으로 폭탄을 투하
   총알 초기화 : 왼쪽(임의위치)에서 오른쪽으로 총알을 발사
   다른 객체와 충돌하거나 이동 허용 범위를 벗어나는지 여부 확인


  이미지: 캐릭터, 총알

 

  총알 피하기 게임의 클래스 다이어그램

 

 
  총알 피하기 게임의 초기화 과정

 

  총알 피하기 게임에서 방향키 입력시 처리 과정

 

  총알 피하기 게임에서 주기적으로 화면 업데이트 과정

 
 
 
 

프로그램 코드

E001	// 이벤트 대기중
E002	// ↓ 키 입력
E003	// 1초마다 타이머 활성화
	 
	// 파일명 : ./Chapter14/BulletDodgeGameMain.java
	import javax.swing.*;
	import move.*;
	 
	public class BulletDodgeGameMain
	{
1		public static void main( String [] args )
		{
			final String imagePath = "C:\\Users\\user\\Downloads\\JAVA-main\\src\\move\\image\\";
			final int WIDTH = 600;
			final int HEIGHT= 300;
	 
			// 판을 틀에 끼우고 실행 준비 완료
2			BulletDodgePanel panel =
3			                         new BulletDodgePanel(
4			                                               new CollidableObject( imagePath+"character.png", WIDTH-CollidableObject.IMGSIZE, HEIGHT/2, WIDTH, HEIGHT ), imagePath+"bullet.png" );
			JFrame frame = new JFrame ( "총알 피하기 게임" );
			frame.getContentPane().add( panel );
			frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
			frame.pack();
			frame.setVisible( true );
			 
5		}
	}
	 
	// 파일명 : ./src/move/ViewPanel.java
	package move;
	 
	import java.awt.*;
	import java.awt.event.*;
	import javax.swing.*;
	 
	// 뷰 패널  
	public class ViewPanel extends JPanel
	{
		protected ObjectByKey character;
		protected Timer timer;
		 
V1b		public ViewPanel( ObjectByKey character ){
			// 키리스너에 방향키로 이동하는 캐릭터를 등록   
			this.character = character;
			addKeyListener( character );
			setFocusable( true );
			requestFocus();
			setPreferredSize( new Dimension( character.backgroundWidth(), character.backgroundHeight() ) );
	 
			// 주기적으로 활성화되는 타이머 등록 
			timer = new Timer( 100, new ActionListener() {
			                	@Override
V2b			                	public void actionPerformed( ActionEvent event ) {
V21			                		update();
V22			                		repaint();
V2e			                	}
			                });
			timer.start();
V1e		}
		 
		// 최신 정보 업데이트  
V3b		protected void update() {
V31			character.move();
V3e		}
		 
		// 화면 출력 
		@Override
V4b		public void paint( Graphics g ){
			super.paint( g );
V41			character.paint( g );
V4e		}
	}
 
 
	// 파일명 : ./src/move/BombDodgePanel.java
	package move;
	 
	import java.awt.*;
	import java.util.ArrayList;
	 
	// 폭탄 피하기 패널 
	public class BombDodgePanel extends ViewPanel
	{
		protected ArrayList<CollidableObject> bombs;
		protected int characterHP, time, width, height;
		protected String imageBomb;
	 
B1b		public BombDodgePanel( CollidableObject character, final String imageBomb ) {
B11			super( character );
			this.characterHP = 5;
			this.bombs = new ArrayList<CollidableObject>();
			this.imageBomb = imageBomb;
			this.width = character.backgroundWidth();
			this.height= character.backgroundHeight();
			this.time = 300;
			setBackground( Color.white );
			setPreferredSize( new Dimension( width + CollidableObject.IMGSIZE, height ) );
B1e		}
	 
		// 최신 정보 업데이트  
		@Override
B2b		protected void update() {
			// 모든 폭탄은 아래로 떨어지며, 주기적으로 새 폭탄을 투하 
			for ( CollidableObject bomb : bombs )
B21				bomb.move();
			if ( ( ( time-- ) % 8 ) == 0 ) {
				bombs.add( new CollidableObject( CollidableObject.STOP, CollidableObject.DOWN, imageBomb, width, height ) );
			}
			 
			// 캐릭터는 좌우로 이동하고 HP 업데이트 
B22			character.move( character.directionX(), CollidableObject.STOP );
B23			updateHP();
B2e		}
	 
		// 캐릭터와 폭탄이 충돌하면 캐릭터 HP 감소 
B3b		protected void updateHP() {
			for ( int i = 0; i < bombs.size(); i++ ) {
				CollidableObject bomb = bombs.get(i);
B31				if ( ( bomb.collide( character )
B32				                                 == true ) ) {
					characterHP--;
					bombs.remove( bomb );
				}
				// 이동 허용 범위를 벗어난 폭탄은 제거
				else if ( ( bomb.collide() == true ) ) {
					bombs.remove( bomb );
				}
			}
B3e		}
		 
		// 화면 출력  
		@Override
B4b		public void paint( Graphics g ) {
			super.paint( g );
			// 먼저, 제한시간동안 폭탄을 피해 살아남으면 성공 출력 
			if ( this.time <= 0 ) {
				g.setColor( Color.black );
				g.setFont( new Font( "Arial", Font.BOLD, 40 ) );
				g.drawString( "Success!", width/2-80, height/2 );
				timer.stop();
			}
			// 또는, HP가 더 이상 없으면 실패 출력 
			else if ( this.characterHP <= 0 ) {
				g.setColor( Color.black );
				g.setFont( new Font( "Arial", Font.BOLD, 40 ) );
				g.drawString( "Game Over!", width/2-100, height/2 );
				timer.stop();
			}
			// 아니면, 캐릭터 및 폭탄의 현재 게임 상황 출력 
			else {
				g.setColor( Color.black );
				g.setFont( new Font( "Arial", Font.BOLD, 20 ) );
				g.drawString( "HP : " + hp(), 10, 30 );
B41				character.paint( g );
				for ( CollidableObject bomb : bombs )
B42					bomb.paint( g );
			}
B4e		}
	 
		public String hp() {
			switch( this.characterHP ) {
			case 5:
				return "● ● ● ● ●";
			case 4:
				return "● ● ● ● ○";
			case 3:
				return "● ● ● ○ ○";
			case 2:
				return "● ● ○ ○ ○";
			case 1:
				return "● ○ ○ ○ ○";
			default:
				return "○ ○ ○ ○ ○";
			}
		}
	}
 
 
	// 파일명 : ./src/move/BulletDodgePanel.java
	package move;
	import java.awt.*;
	 
	// 총알 피하기 패널 
	public class BulletDodgePanel extends BombDodgePanel
	{
P1b		public BulletDodgePanel( CollidableObject character, final String imageBomb ) {
P11			super( character, imageBomb );
			setPreferredSize( new Dimension( width, height + CollidableObject.IMGSIZE ) );
P1e		}
	 
		// 최신 정보 업데이트  
		@Override
P2b		protected void update() {
			// 모든 총알은 오른쪽으로 이동하며, 주기적으로 새 총알을 발사  
			for ( CollidableObject bomb : bombs )
P21				bomb.move();
			if ( ( ( time-- ) % 8 ) == 0 ) {
				bombs.add( new CollidableObject( CollidableObject.RIGHT, CollidableObject.STOP, imageBomb, width, height ) );
			}
			 
			// 캐릭터는 상하로 이동하고 HP 업데이트 
P22			character.move( CollidableObject.STOP, character.directionY() );
P23			updateHP();
P2e		}
	}
 
 
	// 파일명 : ./src/move/ObjectByKey.java
	package move;
	 
	import java.awt.*;
	import java.awt.event.*;
	import javax.swing.*;
	 
	// 방향키로 이동하는 객체 
	public class ObjectByKey extends KeyAdapter
	{
		private Image image;
		protected int x, y, directionX, directionY;
		protected int minX, minY, maxX, maxY;
		public static final int LEFT = -1, RIGHT = 1, UP = -1, DOWN = 1, STOP = 0, IMGSIZE = 40;
	 
		// 초기화: 이미지, 현재 위치, 이동 허용 범위, 이동 방향을 설정
K1b		public ObjectByKey( String image, int x, int y, int minX, int minY, int maxX, int maxY ) {
			this.image = new ImageIcon( image ).getImage();
			this.x = x;
			this.y = y;
			this.minX = minX;
			this.minY = minY;
			this.maxX = maxX;
			this.maxY = maxY;
			this.directionX = STOP;
			this.directionY = STOP;
K1e		}
	 
		// 키를 누르면 상하좌우 이동방향 설정 
		@Override
K2b		public void keyPressed( KeyEvent event ) {
			switch( event.getKeyCode() ) {
			case KeyEvent.VK_ESCAPE:
				System.exit(0);
				break;
			case KeyEvent.VK_LEFT: case 'A': case 'a':
				directionX = LEFT;
				break;
			case KeyEvent.VK_RIGHT: case 'D': case 'd':
K21				directionX = RIGHT;
				break;
			case KeyEvent.VK_UP: case 'W': case 'w':
				directionY = UP;
				break;
			case KeyEvent.VK_DOWN: case 'S': case 's':
K22				directionY = DOWN;
				break;
			}
K2e		}
		 
		// 키를 해제하면 이동방향 해제   
		@Override
K3b		public void keyReleased( KeyEvent event ) {
			switch( event.getKeyCode() ) {
			case KeyEvent.VK_LEFT: case 'A': case 'a':
			case KeyEvent.VK_RIGHT: case 'D': case 'd':
K31				directionX = STOP;
				break;
			case KeyEvent.VK_UP: case 'W': case 'w':
			case KeyEvent.VK_DOWN: case 'S': case 's':
				directionY = STOP;
				break;
			}
K3e		}
		 
		// 이동 허용 범위내에서만 객체 이동 
K4b		public void move() {
K41			move( directionX, directionY );
K4e		}
K5b		public void move( int directionX, int directionY ) {
			this.x += directionX;
			this.y += directionY;
			this.x = ( this.x <= minX ) ? minX : this.x;
			this.y = ( this.y <= minY ) ? minY : this.y;
			this.x = ( this.x >= maxX ) ? maxX : this.x;
			this.y = ( this.y >= maxY ) ? maxY : this.y;
K5e		}
	 
		// 현재 위치에 객체를 출력
K6b		public void paint( Graphics g ) {
			g.drawImage( image, x, y, IMGSIZE, IMGSIZE, null );
K6e		}
			 
		public int directionX() {
			return directionX;
		}
	 
		public int directionY() {
			return directionY;
		}
		 
		public int backgroundWidth(){
			return maxX;
		}
		 
		public int backgroundHeight(){
			return maxY;
		}
	}
 
 
	// 파일명 : ./src/move/CollidableObject.java
	package move;
		 
	// 충돌가능한 객체 
	public class CollidableObject extends ObjectByKey
	{
		// 초기화: 이미지, 현재 위치, 이동 허용 범위를 설정
C1b		public CollidableObject( final String image, int x, int y, int width, int height ) {
C11			super( image, x, y, 0, 40, width, height );
C1e		}
C2b		public CollidableObject( int directionX, int directionY, final String image, int width, int height ) {
C21			this( image, 0, 40, width, height );
			this.directionX = directionX;
			this.directionY = directionY;
			// 폭탄 초기화 : 위쪽(임의위치)에서 아래쪽으로 폭탄을 투하 
			if ( ( directionX == STOP ) && ( directionY == DOWN ) ) {
				this.x = (int)( Math.random() * this.maxX );
			}
			// 총알 초기화 : 왼쪽(임의위치)에서 오른쪽으로 총알을 발사 
			else if ( ( directionX == RIGHT ) && ( directionY == STOP ) ) {
				this.y = IMGSIZE + (int)( Math.random() * ( this.maxY - IMGSIZE ) );
			}
C2e		}
		 
		// 일정 속도로 위치 이동
		@Override
C3b		public void move( int directionX, int directionY ) {
			final int SPEED = 20;
C31			super.move( directionX * SPEED, directionY * SPEED );
C3e		}
		 
		// 다른 객체와 충돌하거나 이동 허용 범위를 벗어나는지 여부 확인 
C4b		public boolean collide( ObjectByKey that ) {
			return ( Math.abs( this.x - that.x ) < IMGSIZE ) && ( Math.abs( this.y - that.y ) < IMGSIZE );
C4e		}
		public boolean collide() {
			return ( this.x < minX ) || ( maxX < this.x ) || ( this.y < minY ) || ( maxY < this.y );
		}
	}

실행 순서

 
 					※ 실행순서 및 메모리상태는 A키(이전) 및 D키(다음)를 눌러도 확인할 수 있습니다