충돌을 하기에 앞서 각 버튼의 위치를 받아서 비교하는 알고리즘을 공부해야 했어요.
처음에는 하나하나 계산을 해서, 총알의 크기가 20,20이니까 19만큼의 여유를 양쪽으로 더 줘야겠다 해서 충돌 범위를 x축에 19, 69를 더해 그 안에 들어왔을 때 충돌로 인정한다, 라는 if문과 전쟁을 했습니다.
그런데 하다보니, 각 객체의 크기에 따라 제가 다 계산해서 붙이면 게임 회사 이미 문 닫았겠다는 생각이 확 들더라고요. 비효율적이라서.
그래서 찾아보니 역시 갓 개발자님들 사각형 충돌 올려놓으셨더라고요! (사실 강사님이 공수해주심 ㅎㅎ)
그걸 긁어다가 붙여도 되긴 하지만, 이해를 하지 않고 붙이면 나중에 조립만 하는 개발자가 될 수 있다는 말이 생각나서 한참을 씨름했습니다. 너무 힘들어서 구구절절 서론이 길었네요...
전제 : 총알과 몬스터가 충돌했을 때 몬스터와 총알이 사라진다.
(오늘은 몬스터만 지우는 것으로 한다)
오늘 할 일 : 알고리즘을 이해하고 몬스터 class에 method로 넣어 활용하기
총알의 위치받아오기
private boolean isDead(int monX, int monY, JButton bullet){ //충돌 알고리즘
if (Math.abs((monX+this.getWidth()/2)-(bullet.getX()+bullet.getWidth()/2))
< (bullet.getWidth()/2+this.getWidth()/2)
&& Math.abs((monY+this.getHeight()/2)-(bullet.getY()+bullet.getHeight()/2))
< (bullet.getHeight()/2+this.getHeight()/2)){
return true;
}else{
return false;
}
}
비전공자에 수포자로서 이해하는 방법입니다.
같은 식을 봐도 사고의 흐름이 다르다는 게 확 느껴지시나요?
1. 연두랑 보라가 가까워질수록 숫자가 적어진다.
2. 연두는 별의 중앙점을 찾아내는 역할, 보라는 총알의 중앙점을 찾아내는 역할을 한다.
둘 사이의 값을 빼주면 남는 건 바로 절반의 크기와 반대쪽 거리이다.
3. (반대쪽 거리의 값은 필요 없으니) 남은 절반들_총알, 몬스터_을 합친 수와 비교한다.
4. 합친 수 보다 값이 크다는 의미는 둘 사이에 남은 게 더 있다는 뜻으로 겹치지 않는다는 것이고
반대라면 저 반쪽들 외에는 남은 값이 없다는 뜻이니 겹친다는 것과 같다.
제 의식의 흐름을 적어보았습니다.
혹시나 저처럼 수학적 사고가 어려운 사람들에게 도움이 되었으면 합니다.
//더 효율적으로 이해하는 방법이 있다면 알려주세요!
알고리즘을 이해했다면 저 속에 필요한 것이 무엇인지 아시겠죠?
바로, 비교할 '기준'위치와 상대의 위치, 이 두 가지입니다.
문제는 메인(Play_Phase1)에 있는 스레드로 몬스터 class와 총알 class를 만드는데,
같은 버튼을 시간마다 계속해서 사용하기 때문에 ['그'버튼의 위치를 가지고 온다]는게 너무 어려웠습니다.
그래서 이름을 주기 위해 key값이 있는 덩어리 Map에 총알을 담기로 했습니다.
- 메인(Play_Phase1)에 전역 변수로 총알 Map을 만들어 해당 클래스를 담아줌
public HashMap<Integer,BtnBullet> listBullet = new HashMap<Integer,BtnBullet>(); //총알 묶음
private int idxBullet;
- 메인(Play_Phase1)에 있는 run()의 내용을 갱신
- key값이 무한정 늘어나지 않도록 넉넉히 값을 주되 재활용
/**Bullet count*/
if(countTime%500 == 0&& isFire == true) {
isFire = false;
idxBullet += 1; //Map의 Key값 설정
if (idxBullet == 30) { //key값이 너무 커지지 않게 재활용
idxBullet = 0;
}
listBullet.put(idxBullet, new BtnBullet(this, idxBullet));
(new Thread(listBullet.get(idxBullet))).start();
}
- 총알 class(BtnBullet)를 수정
- idx 값을 메인(Play_Phase1)에서 받아오도록 생성자를 수정
BtnBullet(Play_Phase1 play1, int idx){
this.play1 = play1;
this.idx = idx;
//btnMe의 위치에 따라 총알의 초기값을 조정해주기
if(play1.poMe != null) {//btnMe의 중간에서 총알이 나가게 함
bulletX = play1.poMe.x+15;
bulletY = play1.poMe.y;
} else{ //만약 움직이지 않으면 Point 메소드에 값이 전달되지 않아 null이 됨
bulletX = 240;
bulletY = 720;
}
ImageIcon icon = new ImageIcon("총알.png"); //이미지 처리
this.setIcon(icon);
this.setBorderPainted(false);
this.setFocusPainted(false);
this.setContentAreaFilled(false);
this.setBounds(bulletX, bulletY, 20,20);
/**add Bullet*/
play1.add(this);
}
- run()에 약간의 변화를 줌
>> 당장 총알을 없애지는 않겠지만 배열에 맞춰 해당 총알이 없어질 수 있도록 idx값을 가진 버튼을 사라지게 작업.
public void run() {
while(bulletY > 30) { //끝에 닿을 때 까지 움직이기
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**run Bullet*/
this.setLocation(bulletX, bulletY-= 30);
//System.out.println(this.getLocation());//테스트 코드
}
play1.remove(this);
play1.listBullet.remove(idx);
play1.repaint();
play1.revalidate();
}
자 이제, 사전 작업은 끝났습니다. 끝이 보입니다.
2020/12/11 - [Java_Study/Code_Memo] - JButton으로 슈팅게임 2-3 몬스터 1 만들기
2020/12/11 - [분류 전체보기] - JButton으로 슈팅게임 2-4 몬스터 2,3 만들기
이전에 만들었던 몬스터 버튼을 변경해주겠습니다.
- isDead 메소드를 추가한다.
- for문을 돌려 map에 있는 총알을 긁어옴
- isDead Method에 현재 JButton의 위치와 긁어온 총알의 위치를 하나하나 비교해줌
- true값이 나오는 총알이 있다면 break를 사용
break를 만나면 무조건 나오기 때문에 while문 break까지 두 번을 연달아 사용할 수 없음
거쳐가는 값 boolean타입의 inMonsterOver를 이용해 충돌하면 true값을 넣고,
if문을 바로 넣어서 while문을 break시킴
package Game1;
import java.util.Map;
import javax.swing.ImageIcon;
import javax.swing.JButton;
public class Monster1 extends JButton implements Runnable {
Play_Phase1 play1;
private int monX, monY;
private boolean isMonsterOver;
Monster1(Play_Phase1 play1){
this.play1 = play1;
monX =(int)((Math.random()*420)+20); //위치 설정
monY = -50;
ImageIcon icmon = new ImageIcon("별_빨강.png"); //이미지 설정
this.setIcon(icmon);
this.setBorderPainted(false);
this.setFocusPainted(false);
this.setContentAreaFilled(false);
this.setBounds(monX, monY, 50, 50);
/**add Monster*/
play1.add(this);
}
private boolean isDead(int monX, int monY, JButton bullet){ //충돌 알고리즘
if (Math.abs((monX+this.getWidth()/2)-(bullet.getX()+bullet.getWidth()/2))
< (bullet.getWidth()/2+this.getWidth()/2)
&& Math.abs((monY+this.getHeight()/2)-(bullet.getY()+bullet.getHeight()/2))
< (bullet.getHeight()/2+this.getHeight()/2)){
return true;
}else{
return false;
}
}
@Override
public void run() {
while(monY<700){
try {
Thread.sleep(100);//적이 떨어지는 시간
} catch (InterruptedException e) {
e.printStackTrace();
}
/**set Location*/
this.setLocation(monX, monY+=15);
/**'Monster' is crashed with 'Bullet'*/
for(Map.Entry<Integer, BtnBullet> bullet:play1.listBullet.entrySet()){
if(isDead(monX,monY,play1.listBullet.get(bullet.getKey()))){
//System.out.println("맞췄다!Monster1"); //테스트 코드
isMonsterOver=true;
break;
}
}
if(isMonsterOver){
break;
}
}
play1.remove(this);
play1.repaint();
play1.revalidate();
}
}
package Game1;
import java.util.Map;
import javax.swing.ImageIcon;
import javax.swing.JButton;
public class Monster2 extends JButton implements Runnable {
Play_Phase1 play1;
private int monX, monY;
private boolean direction;
private boolean isMonsterOver;
Monster2(Play_Phase1 play1){
this.play1 = play1;
monX =(int)((Math.random()*420)+20); //위치 설정
monY = -50;
ImageIcon icmon = new ImageIcon("별_빨강.png"); //이미지 설정
this.setIcon(icmon);
this.setBorderPainted(false);
this.setFocusPainted(false);
this.setContentAreaFilled(false);
this.setBounds(monX, monY, 50, 50);
/**add Monster*/
play1.add(this);
}
private boolean isDead(int monX, int monY, JButton bullet){ //충돌 알고리즘
if (Math.abs((monX+this.getWidth()/2)-(bullet.getX()+bullet.getWidth()/2))
< (bullet.getWidth()/2+this.getWidth()/2)
&& Math.abs((monY+this.getHeight()/2)-(bullet.getY()+bullet.getHeight()/2))
< (bullet.getHeight()/2+this.getHeight()/2)){
return true;
}else{
return false;
}
}
@Override
public void run() {
while(monY<700){
try {
Thread.sleep(100);//적이 떨어지는 시간
} catch (InterruptedException e) {
e.printStackTrace();
}
/**set Location*/
if (direction == false) { //오른쪽으로
this.setLocation(monX+=10, monY+=15);
if (monX > 430) {
direction = true;
}
} else if (direction == true) { //왼쪽으로
this.setLocation(monX-=10, monY+=15);
if (monX < 20) {
direction = false;
}
}
/**'Monster' is crashed with 'Bullet'*/
for(Map.Entry<Integer, BtnBullet> bullet:play1.listBullet.entrySet()){
if(isDead(monX,monY,play1.listBullet.get(bullet.getKey()))){
//System.out.println("맞췄다!Monster2"); //테스트 코드
isMonsterOver=true;
break;
}
}
if(isMonsterOver){
break;
}
}
play1.remove(this);
play1.repaint();
play1.revalidate();
}
}
package Game1;
import java.util.Map;
import javax.swing.ImageIcon;
import javax.swing.JButton;
public class Monster3 extends JButton implements Runnable {
Play_Phase1 play1;
private int monX, monY;
private boolean isMonsterOver;
Monster3(Play_Phase1 play1){
this.play1 = play1;
monX = -50;
monY = (int)((Math.random()*500)+20); //위치 설정
ImageIcon icmon = new ImageIcon("별_주황.png"); //이미지 설정
this.setIcon(icmon);
this.setBorderPainted(false);
this.setFocusPainted(false);
this.setContentAreaFilled(false);
this.setBounds(monX, monY, 50, 50);
/**add Monster*/
play1.add(this);
}
private boolean isDead(int monX, int monY, JButton bullet){ //충돌 알고리즘
if (Math.abs((monX+this.getWidth()/2)-(bullet.getX()+bullet.getWidth()/2))
< (bullet.getWidth()/2+this.getWidth()/2)
&& Math.abs((monY+this.getHeight()/2)-(bullet.getY()+bullet.getHeight()/2))
< (bullet.getHeight()/2+this.getHeight()/2)){
return true;
}else{
return false;
}
}
@Override
public void run() {
while(monX<500){
try {
Thread.sleep(100);//적이 떨어지는 시간
} catch (InterruptedException e) {
e.printStackTrace();
}
/**set Location*/
this.setLocation(monX+=30, monY);
/**'Monster' is crashed with 'Bullet'*/
for(Map.Entry<Integer, BtnBullet> bullet:play1.listBullet.entrySet()){
if(isDead(monX,monY,play1.listBullet.get(bullet.getKey()))){
//System.out.println("맞췄다!Monster3"); //테스트 코드
isMonsterOver=true;
break;
}
}
if(isMonsterOver){
break;
}
}
play1.remove(this);
play1.repaint();
play1.revalidate();
}
}
이렇게 하면 무적 총알로 몹들을 다 죽일 수 있게 됩니다.
오늘 여러 class를 왔다 갔다 하며 수정 작업을 하느라 내용이 누락됐을 수도 있다는 생각이 들어요.
문제가 있거나 더 나은 방법이 있다면 언제든지 조언 부탁드립니다.
'Coding_Study > Java_Code_Memo' 카테고리의 다른 글
JButton으로 슈팅게임 3-3 (충돌) 주인공 죽이기_Game Over (0) | 2020.12.12 |
---|---|
JButton으로 슈팅게임 3- 2 (충돌) 총알 (0) | 2020.12.12 |
JButton으로 슈팅게임 2-3 몬스터 1 만들기 (0) | 2020.12.11 |
JButton으로 슈팅게임 2-2 총알 만들기 (0) | 2020.12.11 |
JButton으로 슈팅게임 2-1 주인공 만들기 (0) | 2020.12.11 |