반응형
전에 만들던 오목판에서 기능을 추가한다.
일단 좌상단에서 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의 값으로 길이를 나누면 기본 단위벡터값이 나온다.
*/
}
}
반응형
'웹 프로그래밍 기초 > 자바기반의 웹&앱 응용SW 개발자' 카테고리의 다른 글
자바기반의 웹&앱 응용 SW개발자 양성과정 25일차 -44 (0) | 2020.04.06 |
---|---|
자바기반의 웹&앱 응용 SW개발자 양성과정 25일차 -43 (0) | 2020.04.01 |
자바기반의 웹&앱 응용 SW개발자 양성과정 24일차 -41 (0) | 2020.03.26 |
자바기반의 웹&앱 응용 SW개발자 양성과정 23일차 -40 (0) | 2020.03.25 |
자바기반의 웹&앱 응용 SW개발자 양성과정 23일차 -39 (0) | 2020.03.25 |
댓글