본문 바로가기
웹 프로그래밍 기초/자바기반의 웹&앱 응용SW 개발자

자바기반의 웹&앱 응용 SW개발자 양성과정 24일차 -42

by oncerun 2020. 3. 26.
반응형

전에 만들던 오목판에서 기능을 추가한다.

일단 좌상단에서 5개의 돌을 놓아 논 뒤 클릭할 때마다 추가된 돌 윗부분부터 원하는 좌표에 놓아지는 기능을 구현해보자.

 

첫 번째는 OmokCanvas의 객체가 생성될 때 5개의 돌이 좌측 상단에 생성되도록 하자.

생성될 때 고정된 좌표에 돌 5개가 놓인다면 중첩돼서 보이지 않으므로 정해진 범위 내에서 겹치지 않게 생성하는 방법은 

랜덤 한 좌표값을 입력시켜 출력하는 것이다.

int omokIndex =0;
int top =-1;  //바둑돌의 인덱스를 가지고있어야 클릭했을때 위치로 이동할것이다.
public OmokCanvas(){
Random rand = new Random()
//5개의 오목돌을 만들것이므로
for(int i =0; i<5; i++){
//좌표값 지정
int x = rand.nextInt(10); 0~9까지의 범위
int y = rand.nextInt(10); 즉 10*10의 범위안에서 랜덤한 돌이5개생성
//OmokCanvas는 omoks라는 배열에 값을담아 이미지를 출력하므로
omoks[omokIndex++] = Omok(x,y);
top++//가장 위에있는 바둑돌을 이동시켜야하므로 인덱스 :4의값 을 가지도록 한다
}
}
this.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				int x = e.getX();
				int y = e.getY();
				
				x= (int)(Math.floor(x/40)*(40))+20;
				y= (int)(Math.floor(y/40)*(40))+20;
				//Omok클래스를 참조하는변수인 omok에 아까 그려줬던 omoks[top]값을 대입해준다
                //그려주면서 top값은 -1부터 4까지증가됬기떄문에
                //처음에 가장위에있는 돌인 5번째 돌부터 원하는 좌표로 이동한다.
				Omok omok = omoks[top];
				omok.setX(x);//클릭된 좌표로 omok의 x좌표를 입력해주고
				omok.setY(y);//클릭된 좌표로 omok의 ㅛ좌표를 입력해주고
				top--;//다음 바둑돌을 출력하기위해 배열에저장된 인덱스를 -1해준다
				repaint();//그런뒤 paint호출
			}
		});

 

이제 클릭 시 즉시 나타나는 행동 말고 Thread를 사용해 초당 60 frame을 유지하면서 바둑돌이 좌측 상단에서 천천히 지정된 좌표로 오도록 해보자.

package omokgame;

import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.JOptionPane;

import com.oracle.xmlns.internal.webservices.jaxws_databinding.JavaParam;

public class OmokCanvas extends Canvas {
	private static OmokCanvas canvas;
	private Image img;// ? 이미지파일을 담기위한 변수가 필요하다.

	private Omok[] omoks;
	// 오목을 추가할 위치를 기억하기위한 변수 OmokIndex를 추가하고 초기화
	private int omokIndex;
	// 오목 캔버스에 배열을 추가하는이유는 캔버스에서 Omokclass의 값을 지정해 동시에 관리하기위함이다
	//좌상단에싸일 돌들의 인덱스를 저장하기위한 변수
	private int top;

	public static OmokCanvas getInstance() {
		return canvas;
	}

	// 생성자
	public OmokCanvas() {
		canvas = this;
		omoks = new Omok[100];
		omokIndex = 0;
		top = -1;
		
		
		Random rand = new Random();
		for(int i = 0; i <20; i++) {
			
		int x = rand.nextInt(10);
		int y = rand.nextInt(10); 
		//정확한 점이 마우스클릭이 되지않았을때 반올림해서한다.
		//입력받은 x좌표값을 한칸이 40픽셀이므로 먼저나눈뒤 반올림을해준다 
		//그뒤 40을 곱해줘서 다시좌표로 보내준뒤 빈공간값인20을 더해준다.
		// 오목객체를 생성해서 배열에 담은뒤 가르키는 인덱스에추가
		omoks[omokIndex++] = new Omok(x, y);
		//맨위에있는 애의 인덱스를 가지고이써야한다.
		top++;
		
		}
	

		// 생성자를 사용하면 객체생성동시에 작성한 로직이 실행된다.
		// 이미지를 인스턴스할때마다 만들면 여러가지면에서 좋지않고 static으로 만들면 딱한번만한다.
		// 캔버스를 여러개만들지않으므로 딱한번만 만드니까 생성자에서 만든다.
		// 이미지를 가져오는 시점이 다르다 static으로 설정시 다른 메모리에 올리기때문에 가져오는 속도면에서 차이가난다.
		// 생성자에 이미지를 가져오면 생성됬을때 가져오기때문이다.

		try {
			img = ImageIO.read(new File("./images/omokboard.png"));// 현재위치를 기반으로 이미지를 가져온다.
		} catch (IOException e) {
			e.printStackTrace();
		}

		// 이미지크기와 넓이를 가져와서 캔버스의 사이즈를 정해준다.
		this.setSize(img.getWidth(this), img.getHeight(this));

		// 이또한 마우스어뎁터가 인터페이스의 메소드를 구현해놨기때문에
		// 필요한 메소드만을 오버라이드 할 수 있다.
		this.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				int x = e.getX();
				int y = e.getY();
				
				x= (int)(Math.floor(x/40)*(40))+20;
				y= (int)(Math.floor(y/40)*(40))+20;
			try {
				Omok omok = omoks[top];
				omok.move(x,y);
				
				//omok.setX(x);
				//omok.setY(y);
				top--;
			//	repaint(); 쓰레드를 통한 리페인트함수호출해서 그려준다. 
			}catch(ArrayIndexOutOfBoundsException e1) {
				JOptionPane.showConfirmDialog(null, "오목알이 부족합니다");
				
			}
			}
			
		});

			
	}//생성자
	
	
	public void start() {
		
		
		Thread gameThread = new Thread(new Runnable() {
			//한 파일에 두개의 흐름을 가지고있다.
			@Override
			public void run() {
				//다른흐름에서 무한루프를 사용하게된다.
				while(true) {
					update();
					repaint();//여기서만 리페인트를 해야한다. 다른곳에서하면
					try {
						//화면에 이상하다. 
						//시간의 흐름을 조정한다. 
						Thread.sleep(17);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			}
		});
	gameThread.start();
		
	}
	//지속적으로 그려지는 그림을 갱신하는 역할이다.
	public void update() {
		for(int i = 0; i <omokIndex; i ++)
			omoks[i].update(); //점직적인 업데이트를위해 omok클래스의 update함수를 만들어준다.
	}
	
	@Override
//업데이트를 수정시켜 즉시 페인트를 실행시키도록한다.
	public void update(Graphics g) {
		this.paint(g);
	}
				
		


	// 캔버스의 paint를 오버라이드해서 이미지를 그려주겠다.
	//paint는 캔버스에 이미지를 그려준다.
	public void paint(Graphics g) {
		Image buf;
		Graphics bufG;
		//동일한크기의 비트맵이미지를만들고
		buf  = createImage(760,760);
		//buf는 그래픽객체를 가지고있는데 getGraphics()를 통해 그래픽스객체를 반환해서 bufG에 저장한다.
		//그 그래픽스 객체를 가지고 버퍼이미지에 그린뒤 전부그린 버퍼이미지를 한번에 캔버스에 그려준다.
		bufG = buf.getGraphics();
		//bufg를 통해 비트맵에 전부그린다.
		bufG.drawImage(img, 0, 0, this);
		// 판을그리고 오목을그린다.
		for (int i = 0; i < omokIndex; i++)
			omoks[i].draw(bufG);
		//전부그려진 buf이미지를 한번에 그리도록한다.
		g.drawImage(buf, 0, 0, this);
	}
}
package omokgame;

import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

//두개의 돌을 다 커버할 클래스.
//돌을 개체로써 흰돌과 검은돌을  속성을 가지는 Omok클래스를 한다.
public class Omok {
	// 정수형이기때문에 구체적인 변수명은 안됨.
	
	private int color; // 돌을 식별하기위한 값 , 기본값
	// static생성자안에서 일관되게 초기화하는것이 바람직함
	
	private static final int WHITE;
	private static final int BLACK;
	// 오목의 객체가 여러번 생성되기때문에 한번의 생성으로 사용하기위함
	
	private static Image imgBlack;
	private static Image imgWhite;
	//좌표
	private double locationX;
	private double locationY;
	
	
	//목적지의 좌표값
	private double dx;
	private double dy;
	
	
//거리값을 계산하기위한 벡터
	private double vx;
	private double vy;
	
	
	//모든 오목들이 사용하는값.
	//색을 지정하는 불린값이다.
	private static boolean isBlackTurn; 
	
	
	static { // 전역변수를 초기화하는 역할
		isBlackTurn = false;  //false이면 Black 턴 , ture이면 White;
		BLACK = 2;
		WHITE = 1;
		try {
			imgBlack = ImageIO.read(new File("./images/blackstone.png"));
			imgWhite = ImageIO.read(new File("./images/whitestone.png"));
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
//draw는  그래픽객체를 이용해서 그림을그린다.
	public void draw(Graphics g) {
		//이미지만 변하므로 이미지변수값만 변환시켜준다.
		Image img = imgBlack;
		
		if (color == BLACK) {//지정한 좌표에 돌을 그리기위함
			g.drawImage(img,(int)(locationX - 19), (int)(locationY - 19), OmokCanvas.getInstance());
		} else if (color == WHITE) {
			img = imgWhite;
			g.drawImage(img,(int)(locationX - 19),(int)(locationY - 19), OmokCanvas.getInstance());
		
		}
		
	}
	
	public Omok(int x, int y) {
		
		
		isBlackTurn = !isBlackTurn;
		//isBlack은 전역 변수이므로 모든 객체가 참조한다. 객체가 생성될때마다 값을 참조하기때문에
		//값은 유지되서 가져오게된다.
		if(isBlackTurn)
			color = BLACK;
		else if(!isBlackTurn)
			color = WHITE;
		
		locationX = x;  //int형을 double자동변환시켜줌
		locationY = y;
		/*
		 * if (omokIndex % 2 == 1) color = BLACK; else color = WHITE;
		 */
		// 인스턴스 변수는 생성자에 초기화해주는것을 통일하는것이 바람직하다.
	}

	/*
	 * public void setX(int x) { this.locationX =x; // TODO Auto-generated method
	 * stub
	 * 
	 * }
	 * 
	 * public void setY(int y) { this.locationY =y; // TODO Auto-generated method
	 * stub
	 * 
	 * }
	 */
//점진적인 변화를 나타내기위해 좌표값을 조정할것이다.
	
	public void update() {
		
		if((dx <= locationX+1 && dx >= locationX-1) &&
				dy <= locationY+1 && dy >= locationY-1) {
			
			vx =0;
			vy =0;
		}
		
		this.locationX +=vx;  //고정크기 vx말고 함수를 만들어서 변화한다.
		this.locationY +=vy;
		
	}
//x,y는 클릭된 dx값
	public void move(int x, int y) {
		//목표점으로 이동하기위한 좌표를 준비한다.
		this.dx =x;
		this.dy =y;
		//빗변의 길이를 구한다.
		
		double width = dx -locationX;
		double height = dy -locationY;
		double distance = Math.sqrt(width*width +height*height);
		
		vx = width/distance;
		vy = height /distance;
		
		/*
		 * 빗변의 길이를 가지고 단위벡터만큼 갈것이므로 dx의 값으로 길이를 나누면 기본 단위벡터값이 나온다.
		 */	
		}

}
반응형

댓글