웹프로그래밍/Java

Java 예외 처리를 이용하여 간단한 게임만들기(2)

피노키오이 2020. 11. 17. 20:52
반응형

참고자료

jung-story.tistory.com/105

 

Java 추상클래스를 이용하여 간단한 게임 만들기(1)

이번에는 Java의 클래스를 이용하여 간단하게 콘솔로 움직이는 게임을 만들어 보도록 하겠습니다. . . . 우선 Java의 추상 클래스인 GameObject 클래스를 만들어 보도록 하겠습니다. 개발하기에 앞서

jung-story.tistory.com

 

 


개요

 

이번에는 Java의 예외처리에 대해서 알아보도록 하겠습니다.


설명

 

프로그래밍을 하다 보면 프로그램에 오류가 나올 경우가 많이 있습니다.

그 오류는 크게 에러와 예외로 나뉘게 됩니다.

 


에러(error) 의 특징

 

  1. 개발자가 해결할 수 없는 치명적인 오류.
  2. 하드웨어의 잘못된 동작 또는 고장으로 인한 오류.
  3. 에러가 발생되면 프로그램 종료.
  4. 정상 실행 상태로 돌아갈 수 없음.

 


예외(exception)의 특징

  1. 개발자가 해결할 수 있는 오류
  2. 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인한 오류
  3. 예외가 발생되면 프로그램을 종료.
  4. 예외 처리 추가하면 정상 실행 상태로 돌아갈 수 있음,
  5. 예외가 발생하면 비정상적인 종료를 막고, 프로그램을 계속 진행할 수 있도록 우회 경로를 제공.

 

예외는 또한 크게 일반 예외와 실행 예외로 나뉩니다.

 


일반 (컴파일 체크) 예외의 특징

 

  1. 컴파일러는 발샐할 가능성을 발견하면 컴파일 오류를 발생.
  2. 개발자는 예외 처리 코드를 반드시 추가.

 


대표적인 일반 예외 ex)

 

일반 예외 발생 이유
ClassNotFoundException 존재하지 않는 클래스를 사용하려고 할 때 발생.
InterruptedException 인터럽트되었을 때 발생.
NoSuchFieldException 클래스가 명시한 필드를 포함하지 않을 때 발생.
NoSuchMethodException 클래스가 명시한 메서드를 포함하지 않을 때 발생
IOException 데이터 읽기 같은 입출력 문제가 있을 때 발생

 


실행 예외(RuntimeException) 의 특징

  1. 예외가 발생하면 JVM은 해당하는 실행 예외 객체를 생성
  2. 실행 예외는 컴파일러가 예외 처리 여부를 확인하지 않음. 따라서 개발자가 예외 처리 코드의 추가 여부를 결정.

 


대표적인 실행 예외

 

실행 에외 발생 이유
ArithmeticException 0으로 나누기와 같은 부적절한 산술 연산을 수행할 때 발생.
IllegalArgumentException 메서드에 부적절한 인수를 전달할 때 발생.
IndexOutOfBoundsException 배열, 벡터 등에서 범위를 벗어난 인덱스를 사용할 때 발생.
NoSuchElementException 요구한 원소가 없을 때 발생.
NullPointerException null 값을 가진 참조 변수에 접근할 때 발생.
NumberFormatException 숫자로 바꿀 수 없는 문자열을 숫자로 변환하려 할 때 발생.

 

이렇게 발생한 예외를 처리하는데에는 두 가지의 방법이 있습니다.

1. 예외를 잡아서 처리한다. try ~ catch 문

2. 예외를 떠넘긴다. throws 문

 


try-catch-finally 문

 

예외 발생시 프로그램 종료를 막고, 정상 실행 유지할 수 있도록 처리

일반 예외 : 반드시 작성해야 컴파일 가능.

실행 예외 : 컴파일러가 체크해주지 않으며 개발자 경험에 의해 작성

 


(정상 실행되었을 경우)

try {


	// 예외 발생 가능 코드
    
} catch(Exception e) {

	// 예외처리 실행되지 않음

} finally {
	
    // 항상 실행
	
} 

 


(예외가 발생 되었을 경우)

 

try {

	//예외발생 이 밑으로 실행안됨

} catch(Exception e) {

	// 여기가 실행됨
    
} finally {

	// 항상 실행
}

 


다중 catch 문

 

여기선 catch의 순서가 중요합니다. 

try {

	//	1. ArrayIndexOutOfBoundsException 예외1 발생 
    
    //  2. NumberFormatException 예외2 발생

} catch(ArrayIndexOutOfBoundsException e) {
	
    // 예외 처리 1 실행

} catch(NumberFormatException e) {

	// 예외 처리 2 실행

}

 


throws : 예외 떠 넘기기

 

메서드에서 처리하지 않은 예외를 호출한 곳으로 넘김.

public void method1() {

	try {
    
    	method2();
    
    } catch(ClassNotFoundException e) {
    	// 예외 처리 코드
        System.out.println("클래스가 존재하지 않습니다.");
    
    }
}


public void method2() throws ClassNotFoundException {	// 5번출에서 호출했으므로 예외를 처리

	Class clazz = Class.forName("java.lang.String2");
}

 


오류내용을 직접 처리하는 방법에 대해서 알아보겠습니다.

 

public class Ex_01 {
	public static void main(String[] args) {
    	int a = 100, b=0;
        int result;
        
        try {
        	if(b==0)
            	throw new Exception("0으로 나눌수 없습니다");	// 나누는 값 b가 0이면 8행을 
                												// 실행하기 전에 오류가 발생한다.
                
            result = a/b;
        
        }catch (Exception e) {	// 예외 타입을 Exception으로 변경한다.
        
        	System.out.print("발생 오류 =>");
            System.out.println(e.getMessage());
        
        }
    
    }


}

 

그렇다면 저번 추상 클래스를 알아보면서 만든 게임을 수정해보도록 하겠습니다.

 


(Food.java)

 

벽을 만났을때 예외를 발생시켜 처리하도록 하였으며, 움직임을 랜덤 하게 움직이도록 하였습니다.

 

package java_2020_11_11;
public class Food extends GameObject {
	public Food(int x, int y, int distance) {
		super(x, y, distance); 
	}
	 
	@Override
	public void move() { // 한 번 움직이는 과정 전개
		int n = (int)(Math.random()*5); // 0,1,2,3,4 중에서 0인 경우 + 방향, 1인 경우 - 방향, 나머지 정지
		if(n==0) x += distance;
		else if(n==1) x -= distance;
		try {
			if(x < 0) throw new Exception("Food 이동 범위 이탈 Hold "); 
		}catch (Exception e) {
			x=0;
			System.out.println(e.getMessage());
		}
		
		try {
			if(x >= Game.MAX_X) throw new Exception("Food 이동 범위 이탈 Hold "); 
		}catch (Exception e) {
			x = Game.MAX_X - 1;
			System.out.println(e.getMessage());
		}
		 
		n = (int)(Math.random()*5);
		if(n==0) y += distance;
		else if(n==1) y -= distance;

		try {
			if(y < 0)throw new Exception("Food 이동 범위 이탈 Hold "); 
		}catch (Exception e) {
			 y=0;
			 System.out.println(e.getMessage());
		}
		try{
			if(y >= Game.MAX_Y)throw new Exception("Food 이동 범위 이탈 Hold "); 
		}catch (Exception e) {
			 y= Game.MAX_Y - 1;
			 System.out.println(e.getMessage());
		}
	}

	@Override
	public char getShape() { // Fish의 모양 리턴
		return 'F';
	}
}

 


(Host.java)

 

마찬가지로 움직일때 벽을 만나면 정지시켜 놓고 예외를 발생시켜 처리하였습니다.

 

package java_2020_11_11;

import java.util.Scanner;

public class Host extends GameObject {
	private Scanner scanner;

	public Host(int x, int y, int distance) {
		super(x, y, distance);
		scanner = new Scanner(System.in);
	}

	@Override
	public void move() {
		System.out.print("왼쪽(a), 아래(s), 위(w), 오른쪽(d) >> ");
		char c;
		c = scanner.next().charAt(0);
		try {
			switch (c) {
			case 'a': // left
				x--;
				if (x < 0) { // 예외처리를 만든다.
					x = 0;
					throw new Exception("왼쪽 으로 이동 범위를 초과하였습니다.\n다시 입력해주세요.");
				}
				break;

			case 'd': // right
				x++;
				if (x >= Game.MAX_X) {
					x = Game.MAX_X - 1;
					throw new Exception("오른쪽 으로 이동 범위를 초과하였습니다.\n 다시 입력해주세요.");
				}
				break;

			case 'w': // up
				y--;
				if (y < 0) {
					y = 0;
					throw new Exception("위쪽 으로 이동 범위를 초과하였습니다.\n 다시 입력해주세요.");
				}
				break;
			case 's': // down
				y++;
				if (y >= Game.MAX_Y) {
					y = Game.MAX_Y - 1;
					throw new Exception("아래쪽 으로 이동 범위를 초과하였습니다.\n 다시 입력해주세요.");
				}
				break;
			}
		} catch (Exception e) {
			System.out.print("발생 오류 => ");
			System.out.println(e.getMessage());
		}
	}

	@Override
	public char getShape() { // Bear의 모양 리턴
		return 'H';
	}
}

 


(Monster.java)

 

장에물을 만나면 예외를 발생시키도록 하였으며 catch문에서 다중 if문을 사용하여 움직임을 어느 정도 랜덤 하게 하도록 하였습니다.

 

package java_2020_11_11;

public class Monster extends GameObject {
	public Monster(int x,int y, int distance) {
		super(x,y,distance);
	}

	@Override
	protected void move() {
		int n = (int)(Math.random()*3);	// 0,1 중에서 0이면 + 1이면 - 2이면 안움직임
		if(n==0) x+=distance;
		else if(n==1) x-=distance;
		try {
			if(x<0) throw new Exception("가로길이 막혀 세로중 랜덤으로 이동");
		}catch (Exception e) {
			int s = (int)(Math.random()*2);	// 위 , 아래 어디로 움직일지 랜덤으로 정한다.
			x=0;	
			if(y!=0) {	// 위 와 아래의 길이 막히지 않았다면 이동
				if(y!=Game.MAX_Y) {
					if(s==0) y++;
					else if(s==0) y--;
				}
			}
			System.out.print("발생 오류=> ");
			System.out.println(e.getMessage());
		}
		try {
			if(x>=Game.MAX_X) throw new Exception("가로길이 막혀 세로중 랜덤으로 이동");
		}catch (Exception e) {
			x=Game.MAX_X-1;
			int s = (int)(Math.random()*2);
			if(y!=0){
				if(y!=Game.MAX_Y) {
					if(s==0) y++;
					else if(s==0) y--;
				}
			}
			System.out.print("발생 오류=> ");
			System.out.println(e.getMessage());
		}
		
		n = (int)(Math.random()*3);
		if(n==0) y+=distance;
		else if(n==1) y-=distance;
		
		try {
			if(y<0) throw new Exception("세로 길이 막혀 가로중 랜덤으로 이동");
		}catch (Exception e) {
			int s =(int)(Math.random()*2);
			y=0;
			if(x!=0) {
				if(x!=Game.MAX_X) {
					if(s==0) x++;
					else if(s==1) x--;
				}
			}
			System.out.println("발생 오류=> ");
			System.out.println(e.getMessage());
		}
		try {
			if(y>=Game.MAX_Y) throw new Exception("세로 길이 막혀 가로중 랜덤으로 이동");
		}catch (Exception e) {
			int s =(int)(Math.random()*2);
			y=Game.MAX_Y-1;
			if(x!=0) {
				if(x!=Game.MAX_X) {
					if(s==0) x++;
					else if(s==1) x--;
				}
			}
			System.out.println("발생 오류=> ");
			System.out.println(e.getMessage());
		}
		
	}

	@Override
	protected char getShape() {
		// TODO Auto-generated method stub
		return '$';
	}
	
}

 


(GameObject.java)

 

package java_2020_11_11;
public abstract class GameObject { // 추상 클래스
	protected int distance; // 한 번 이동 거리
	protected int x, y; // 현재 위치(화면 맵 상의 위치)
	
	public GameObject(int startX, int startY, int distance) { // 초기 위치와 이동 거리 설정
		this.x = startX; this.y = startY;
		this.distance = distance;
	}
	public int getX() { return x; }
	public int getY() { return y; }
	public boolean collide(GameObject p) { // 이 객체가 객체 p와 충돌했으면 true 리턴
		if(this.x == p.getX() && this.y == p.getY())
			return true;
		else 
			return false;
	}
	
	protected abstract void move(); // 이동한 후의 새로운 위치로 x, y 변경
	protected abstract char getShape(); // 객체의 모양을 나타내는 문자 리턴
}

 


(Game.java)

 

package java_2020_11_11;
public class Game {
	public static final int MAX_X = 20;
	public static final int MAX_Y = 10;
	private char map [][] = new char [MAX_Y][MAX_X];
	private GameObject [] m = new GameObject[3];
	int state; // 0: 게임 중, 1: Monster가 winner, 2:Human이 winner
	
	public Game() {
		for(int i=0; i<MAX_Y; i++) 
			for(int j=0; j<MAX_X; j++)
				map[i][j] = '-';
		m[0] = new Host(0, 0, 1);
		m[1] = new Food(0, 1, 2);
		m[2] = new Monster(5,4,2);
	
		
		
		state = 0; // 게임 중
	}
	public void run() {
		System.out.println("** Host의 Food 먹기 게임을 시작합니다.**");;

		update(); // 초기 좌표에 따른 맵 설정
		draw(); // 초기 게임 맵을 보여줌

		while(!doesEnd()) {
			clear(); // 현재의 맵 지움
			for(int i=0; i<m.length; i++) {
				m[i].move();
			}
			show();
			update(); // 움직인 후 좌표 변경에 따른 맵 갱신
			draw(); // 맵 그리기
		}
		
	}
	private void show() {
		for(int i=0;i<26;i++) {
			System.out.println();
		}
	}
	private void update() {
		for(int i=m.length-1; i>=0; i--) // Food부터 먼저 그려서 Food를 먹는 경우 Food가 보이지 않기
			map[m[i].getY()][m[i].getX()] = m[i].getShape();
	}
	private void clear() {
		for(int i=0; i<m.length; i++) 
			map[m[i].getY()][m[i].getX()] = '-';
	}
	private void draw() {
		System.out.println();
		for(int i=0; i<MAX_Y; i++) {
			for(int j=0; j<MAX_X; j++)
				System.out.print(map[i][j]);
			System.out.println();
		}
	}
	private boolean doesEnd() {
		try {
		
		if(m[0].collide(m[1])) {// Host ate Food
			System.out.println("Host Wins!!");
			return true;
		}
		else if(m[0].collide(m[2])) {// Monster ate Host
			System.out.println("Monster Wins!!");
			return true;
		}
		
			else if(m[2].collide(m[1])) {// Monster 와 Food의 충돌
				throw new Exception("Monster Food crush 발생!");
			}
		}catch (Exception e) {
			int n = (int)(Math.random()*4); // 0 오른쪽 /1 왼쪽 /2 위쪽 /3 아래쪽
			if(n==0) {	
				System.out.println(e.getMessage());
				if(m[1].getX()==0) { // 왼쪽 끝에서 충돌이 일어난다면
					m[1].x+=1;		// 오른쪽으로 한칸 이동한다.
					
				}
				else if(m[1].getX()==Game.MAX_X) {	// 오른쪽 끝에서 충돌이 일어나면
					m[1].x-=1;	// 왼쪽으로 한칸 이동한다.
					
				}
				else if(m[1].getY()==0) {	// 맨 위에 줄에서 충돌이 발생한다면
					m[1].y+=1;	// 아래로 한칸 이동한다.
					
				}
				else if(m[1].getY()==Game.MAX_Y) {	// 맨 아래 줄에서 충돌이 발생한다면
					m[1].y-=1;	// 위로 한칸 이동한다.
					
				}else {
					m[1].x++;	// 벽에 막히지 않았으면 오른쪽 이동
				}
			}
			else if(n==1) {
				System.out.println(e.getMessage());
				if(m[1].getX()==0) { // 왼쪽 끝에서 충돌이 일어난다면
					m[1].x+=1;		// 오른쪽으로 한칸 이동한다.
					
				}
				else if(m[1].getX()==Game.MAX_X) {	// 오른쪽 끝에서 충돌이 일어나면
					m[1].x-=1;	// 왼쪽으로 한칸 이동한다.
				
				}
				else if(m[1].getY()==0) {	// 맨 위에 줄에서 충돌이 발생한다면
					m[1].y+=1;	// 아래로 한칸 이동한다.
					
				}
				else if(m[1].getY()==Game.MAX_Y) {	// 맨 아래 줄에서 충돌이 발생한다면
					m[1].y-=1;	// 위로 한칸 이동한다.
					
				}
				else {
					m[1].x--; // 벽에 막히지 않았으면 왼쪽 이동
				}
			}
			else if(n==2) {
				System.out.println(e.getMessage());
				if(m[1].getX()==0) { // 왼쪽 끝에서 충돌이 일어난다면
					m[1].x+=1;		// 오른쪽으로 한칸 이동한다.
					
				}
				else if(m[1].getX()==Game.MAX_X) {	// 오른쪽 끝에서 충돌이 일어나면
					m[1].x-=1;	// 왼쪽으로 한칸 이동한다.
					
				}
				else if(m[1].getY()==0) {	// 맨 위에 줄에서 충돌이 발생한다면
					m[1].y+=1;	// 아래로 한칸 이동한다.
					
				}
				else if(m[1].getY()==Game.MAX_Y) {	// 맨 아래 줄에서 충돌이 발생한다면
					m[1].y-=1;	// 위로 한칸 이동한다.
					
				}
				else {
					m[1].y++; // 벽에 막히지 않았으면 아래로 이동
				}
			}
			else if(n==3) {
				System.out.println(e.getMessage());
				if(m[1].getX()==0) { // 왼쪽 끝에서 충돌이 일어난다면
					m[1].x+=1;		// 오른쪽으로 한칸 이동한다.
					
				}
				else if(m[1].getX()==Game.MAX_X) {	// 오른쪽 끝에서 충돌이 일어나면
					m[1].x-=1;	// 왼쪽으로 한칸 이동한다.
					
				}
				else if(m[1].getY()==0) {	// 맨 위에 줄에서 충돌이 발생한다면
					m[1].y+=1;	// 아래로 한칸 이동한다.
					
				}
				else if(m[1].getY()==Game.MAX_Y) {	// 맨 아래 줄에서 충돌이 발생한다면
					m[1].y-=1;	// 위로 한칸 이동한다.
					
				}
				else {
					m[1].y--; // 벽에 막히지 않았으면 위쪽으로 이동
				}
			}
			show();
			update();
			draw();
			return false;
		}
		return false;
	}
	
	public static void main(String[] args) {
		Game game = new Game();
		game.run();
	}

}

 


 

반응형