관리 메뉴

공부한것들을 정리하는 블로그 입니다.

타자 게임(스레드&이벤트핸들러) 본문

전문개발직 START-UP 교육

타자 게임(스레드&이벤트핸들러)

호 두 2017. 7. 30. 18:20
반응형

타자 게임(스레드&이벤트핸들러)




Frame, AWT, 멤버변수 선언 등

import java.util.*;
import java.awt.*;
import java.awt.event.*;

/*
*	해석 : hodu
*/
class 타자게임2 extends Frame {
	// final을 사용한 것은, 
	// 팔드에 저장된 초기값을 프로그램 실행 도중에 수정하지 못하게 하기위함
	final int FRAME_WIDTH = 400;
	final int FRAME_HEIGHT = 300;
	
	final int SCREEN_WIDTH;
	final int SCREEN_HEIGHT;
	
	int speed = 700; // 단어가 떨어지는 속도... 높을 수록 느리다.
	int interval = 2 * 1000; // 새로운 단어가 나오는 간격
	
	int score = 0;
	int life = 3;
	int curLevel = 0;
	final int MAX_LEVEL;
	
	boolean isPlaying = false;
	
	// WordGenerator는 일정시간(interval)간격으로
	// data배열의 한 요소를 골라서
	// words(Vector인스턴스)에 저장하는 일을 수행한다
	WordGenerator wg = null; // 단어를 생성하는 쓰레드
	// WordDropper는 WordGenerator와 같은 쓰레드로
	// 모든 단어의 시간을 10부터 0까지 1초에 1씩 감소시키는 일을 한다
	WordDropper wm = null; // 단어를 떨어뜨리는 쓰레드
	
	FontMetrics fm; // 화면에서의 글자 길이를 구하는데 사용
	ThreadGroup virusGrp = new ThreadGroup("virus"); // 바이러스 쓰레드들의 그룹
	
	String[][] data = { 
			{ "개과천선", "견물생심", "결초보은", "경거망동", "경국지색", "고진감래", "과유불급", "구사일생", "권모술수", "백년대계", "백전백승", "사면초가" },
			{ "개과천선", "견물생심", "결초보은", "경거망동", "경국지색", "고진감래", "과유불급", "구사일생", "권모술수", "백년대계", "백전백승", "사면초가" },
			{ "개과천선", "견물생심", "결초보은", "경거망동", "경국지색", "고진감래", "과유불급", "구사일생", "권모술수", "백년대계", "백전백승", "사면초가" },
			{ "개과천선", "견물생심", "결초보은", "경거망동", "경국지색", "고진감래", "과유불급", "구사일생", "권모술수", "백년대계", "백전백승", "사면초가" },
		};
	
	// Level(int speed, int interval, int levelUpScore, String[] data)
	final Level[] LEVEL = { new Level(500, 2000, 1000, data[0]), 
							new Level(250, 1500, 2000, data[1]),
							new Level(120, 1000, 3000, data[2]), 
							new Level(100, 500, 4000, data[3]), };
	
	Vector words = new Vector();
	
	TextField tf = new TextField();
	Panel pScore = new Panel(new GridLayout(1, 3));
	Label lbLevel = new Label("Level:" + curLevel, Label.CENTER);
	Label lbScore = new Label("Score:" + score, Label.CENTER);
	Label lbLife = new Label("Life:" + life, Label.CENTER);
	MyCanvas screen = new MyCanvas();
	
	타자게임2() {
		this("Typing game ver1.0");
	}
	
	타자게임2(String title) {
		super(title);
	
		pScore.setBackground(Color.YELLOW);
		pScore.add(lbLevel);
		pScore.add(lbScore);
		pScore.add(lbLife);
		add(pScore, "North");
		add(screen, "Center");
		add(tf, "South");
	
		MyEventHandler handler = new MyEventHandler();
		addWindowListener(handler);
		tf.addActionListener(handler);
	
		setBounds(500, 200, FRAME_WIDTH, FRAME_HEIGHT); // 패널의 크기 및 위치 지정 (x,y로 부터 넓이(width, 높이(height))
		setResizable(false); // 크기조절 불가능
		setVisible(true); // 프레임을 보여줌
	
		SCREEN_WIDTH = screen.getWidth();
		SCREEN_HEIGHT = screen.getHeight();
		MAX_LEVEL = LEVEL.length - 1; // LEVEL.length로 LEVEL배열의 길이를 구한다
									  // 문자열의 길이를 부를때는 변수명.length()이고 배열의 길이를 구할때는 배열명.length이다.
	
		fm = getFontMetrics(getFont());
	}
	
	public void repaint() {
		super.repaint();
		screen.repaint();
	}
	
	public void delay(int millis) {
		try {
			Thread.sleep(millis);
		} catch (Exception e) {
		}
	}
	
	// 게임이 시작됨
	public void start() {
		showLevel(0); // Level을 정의하고 title에 출력
		isPlaying = true; // isPlaying은 life와 
	
		wg = new WordGenerator();
		wg.start();
	
		wm = new WordDropper();
		wm.start();
	}
	
	public Level getLevel(int level) {
		if (level > MAX_LEVEL)
			level = MAX_LEVEL;
		if (level < 0)
			level = 0;
	
		return LEVEL[level];
	}
	
	public boolean levelUpCheck() {
		Level lvl = getLevel(curLevel);
	
		return score >= lvl.levelUpScore;
	}
	
	public synchronized int getCurLevel() {
		return curLevel;
	}
	
	public synchronized void levelUp() {
		virusGrp.interrupt();
	
		Level lvl = getLevel(++curLevel);
	
		lbLevel.setText("Level:" + curLevel);
		words.clear();
		screen.clear();
		showLevel(curLevel);
	
		speed = lvl.speed;
		interval = lvl.interval;
	}
	
	// Level을 표시
	public void showLevel(int level) {
		String tmp = "Level " + level;
		showTitle(tmp, 1 * 1000); // 출력되는 title의 정보
	}
	
	// 출력되는 title의 정보
	public void showTitle(String title, int time) {
		Graphics g = screen.getGraphics();
	
		Font titleFont = new Font("Serif", Font.BOLD, 20);
		g.setFont(titleFont);
	
		FontMetrics fm = getFontMetrics(titleFont);
		int width = fm.stringWidth(title);
	
		g.drawString(title, (SCREEN_WIDTH - width) / 2, SCREEN_HEIGHT / 2);
		delay(time);
	}
	
	public static void main(String[] args) {
		타자게임2 win = new 타자게임2(); // 연산자 new가 반환한 레퍼런스는 new TypingGameEx5();
		win.start();							 // 참조변수는 win이다.
	}

}




클래스 및 변수 선언(Canvas, Word, Level 등)

class MyCanvas extends Canvas {
	public void clear() {
		Graphics g = getGraphics();
		g.clearRect(0, 0, FRAME_WIDTH, FRAME_HEIGHT);
	}

	public void paint(Graphics g) {
		clear();

		for (int i = 0; i < words.size(); i++) {
			Word tmp = (Word) words.get(i);
			g.setColor(tmp.color);
			g.drawString(tmp.word, tmp.x, tmp.y);
		}
	}
}



class Level {
	int speed;
	int interval;
	int levelUpScore;
	String[] data;

	Level(int speed, int interval, int levelUpScore, String[] data) {
		this.speed = speed;
		this.interval = interval;
		this.levelUpScore = levelUpScore;
		this.data = data;
	}
} // GameLevel

class Word {
	String word = "";
	int x = 0;
	int y = 0;
	int step = 5;

	Color color = Color.BLACK;
	boolean isVirus = false;

	Word(String word) {
		this(word, 10, false);
	}

	Word(String word, boolean isVirus) {
		this(word, 10, isVirus);
	}

	Word(String word, int step, boolean isVirus) {
		this.word = word;
		this.step = step;
		this.isVirus = isVirus;

		if (isVirus)
			color = Color.RED;

		int strWidth = fm.stringWidth(word);

		x = (int) (Math.random() * SCREEN_WIDTH);

		if (x + strWidth >= SCREEN_WIDTH)
			x = SCREEN_WIDTH - strWidth;
	}

	public String toString() {
		return word;
	}
} // end of class Word




WordDropper Thread

// WordDropper는 WordGenerator와같은 쓰레드로
// 모든 단어의 y좌표를 변경하여
// 단어가 아래로 향하도록 한다.
class WordDropper extends Thread {
	public void run() {
		outer: while (isPlaying) { // life가 0이하가 되면 break outer;됨
			delay(speed);
			for (int i = 0; i < words.size(); i++) {
				Word tmp = (Word) words.get(i);

				tmp.y += tmp.step;

				// 화면 최하단에 도달했을때
				// 단어는 remove되고
				// Life가 감소한다
				if (tmp.y >= SCREEN_HEIGHT) {
					tmp.y = SCREEN_HEIGHT;
					words.remove(tmp);
					life--;
					lbLife.setText("Life:" + life);
					break;
				}

				// life가 0 이하가 되면 
				// while문을 빠져 나감
				if (life <= 0) {
					isPlaying = false;
					showTitle("Game Over", 0);

					break outer;
				}
			} // for

			repaint(); // AWT안에 구현되어 있기 때문에 Frame을 상속받으면 바로 사용 가능
					   // update()메소드를 자동호출
		}
	} // end of run()
}






WordGenerator Thread

// WordGenerator는 일정시간(interval)간격으로
// data배열의 한 요소를 골라서
// words(Vector인스턴스)에 저장하는 일을 수행한다
class WordGenerator extends Thread {
	public void run() {
		while (isPlaying) {
			String[] data = LEVEL[getCurLevel()].data; // 원래 LEVEL메서드의 LEVEL[getCurLevel()].data는 String data[]dlek. 접근자의 기능

			int rand = (int) (Math.random() * data.length); // 난수 발생. Virus의 data[] 배열 내부에 선언해놓은 값들을 불러오기 위함

			// 약 10번에 한번 꼴로 바이러스를 생성한다.
			boolean isVirus = ((int) (Math.random() * 10) + 1) / 10 != 0;

			// word가 발생한다
			// word는 data[]배열에서 난수로 발생
			// Word(String word, boolean isVirus)
			Word word = new Word(data[rand], isVirus);
			words.add(word); // Vector words = new Vector();
							 // Vector는 필요에 따라 길이를 증감할 수 있음
							 // remove할때는 Vector가 ArrayList나 LinkedList보다 빠름
			delay(interval);
		}
	} // end of run()
}




VirusThread

class VirusThread extends Thread {
	public VirusThread(ThreadGroup group, String name) {
		super(group, name);
	}

	public void run() {
		int rand = (int) (Math.random() * 5);

		int oldValue = 0;
		int virusTime = 10 * 1000; // 바이러스 동작시간을 10초로 설정한다.

		switch (rand) {
		case 0:
			speed = speed / 2;
			break;
		case 1:
			interval = interval / 2;
			break;
		case 2:
			speed = speed * 2;
			break;
		case 3:
			interval = interval * 2;
			break;
		case 4:
			words.clear();
			break;
		} // swtich

		delay(virusTime);

		int curLevel = getCurLevel();
		speed = LEVEL[curLevel].speed;
		interval = LEVEL[curLevel].interval;
	} // end of run()
}




EventHandler ( ActionListener - actionPerformed() )

// 사용자가 textfield에 타이핑한 값을 받아오는 클래스 및 메서드
// WindowAdapter는 윈도우 이벤트를 받기위한 추상클래스이다.
class MyEventHandler extends WindowAdapter implements ActionListener {
	// ActionListener 객체를 등록하는데 필수 구현함수인 actionPerformed()를 등록
	// 최종적으로 이벤트가 오게되면 이 함수의 인수로 actionEvent가 넘어오면서 실행되는 것
	// ActionEvent 객체로 어떤 곳에서 이벤트가 발생했는지 알 수 있음(마우스인지 윈도우 이벤트인지 등)
	public void actionPerformed(ActionEvent ae) {
		String input = tf.getText().trim(); // 문자열의 앞뒤 공백 제거
		tf.setText("");

		System.out.println(input);

		if (!isPlaying)
			return;

		// words의 크기를 받는다
		for (int i = 0; i < words.size(); i++) {
			// Class Word
			Word tmp = (Word) words.get(i);

			// words가 일치하면, 
			if (input.equals(tmp.word)) {
				// Vector words = new Vector();
				// Vector는 필요에 따라 길이를 증감할 수 있음
				// remove할때는 Vector가 ArrayList나 LinkedList보다 빠름
				words.remove(i); // words가 제거됨
				score += input.length() * 50; // 글자당 50점씩 획득
				lbScore.setText("Score:" + score); // Score가 갱신됨
				Toolkit.getDefaultToolkit().beep(); // Toolkit을 이용해 컴퓨터에서 비프음이 발생

				if (curLevel != MAX_LEVEL && levelUpCheck()) {
					levelUp();
				} else {
					if (tmp.isVirus) {
						new VirusThread(virusGrp, "virus").start(); // 처음 0레벨일대는 main의 call stack 내부에서 초기값으로 주어진 date[]나 virusGrp를 이용하여 스레드가 실행되지만
																	// 그 이후에는 start()메서드를 실행하여 새로운 call stack에서 새로운 스레드를 run() 한다(VirusThread)
					}
				}

				break;
			}
		} // for

		repaint(); // 변화가 있을때 마다 호출해야 바로 바로 적용된다.
	}

	public void windowClosing(WindowEvent e) {
		e.getWindow().setVisible(false);
		e.getWindow().dispose();
		System.exit(0);
	}
} // class MyEventHandler





반응형
Comments