더보기

쿠키런 프로젝트 시리즈
1.준비
https://ondolroom.tistory.com/297
2.전역공간
https://ondolroom.tistory.com/298
3.JPanel생성자
https://ondolroom.tistory.com/299
4.mapMove메서드
https://ondolroom.tistory.com/300
5.hit메서드
https://ondolroom.tistory.com/301
6.fall메서드
https://ondolroom.tistory.com/302
7.jump메서드
https://ondolroom.tistory.com/303
8.paintComponent 및 결과
https://ondolroom.tistory.com/304

이제 화면에 그림만 그리면 게임이 작동이 된다.

지금 사용할 방식은 화면에 바로그리는게 아닌 미리 그려놓고 출력하는

더블버퍼링 방식이다.

https://ondolroom.tistory.com/299

 

자바 스윙 쿠키런 만들기 MyPanel 생성자 수정 (프로젝트 진행)

https://ondolroom.tistory.com/298 자바 스윙 쿠키런 만들기 전역 변수 선언하기 (프로젝트 진행) 들어가기 전에... 아래 내용을 숙지하고 시작하자 https://ondolroom.tistory.com/279 자바 WindowBuilder 설치..

ondolroom.tistory.com

전역공간에서 미리 만들어둔 변수

이것들을 이제 이용하게 된다.

 

더보기를 눌러 전체코드 확인

더보기
		@Override
		protected void paintComponent(Graphics g) {

			
			// 더블버퍼는 그림을 미리그려놓고 화면에 출력한다.
			
			// 더블버퍼 관련
			if (buffg == null) {
				buffImage = createImage(this.getWidth(), this.getHeight());
				if (buffImage == null) {
					System.out.println("더블 버퍼링용 오프 스크린 생성 실패");
				} else {
					buffg = buffImage.getGraphics();
				}
			}
			
			//투명도 관련
			Graphics2D g2 = (Graphics2D)buffg; 
			
			super.paintComponent(buffg); // 이전 이미지를 지운다.
			
			// 배경이미지를 그린다
			buffg.drawImage(b11.getImage(), b11.getX(), 0, null);
			buffg.drawImage(b12.getImage(), b12.getX(), 0, null);

			// 발판을 그린다
			for (int i = 0; i < fieldList.size(); i++) {

				Field tempFoot = fieldList.get(i);

				// 사양을 덜 잡아먹게 하기위한 조치
				if (tempFoot.getX() > -90 && tempFoot.getX() < 810) { // x값이 -90~810인 객체들만 그린다.
					
					buffg.drawImage(
						tempFoot.getImage(), 
						tempFoot.getX(), 
						tempFoot.getY(), 
						tempFoot.getWidth(),
						tempFoot.getHeight(), 
						null);
				}

			}
			
			// 젤리를 그린다
			for (int i = 0; i < jellyList.size(); i++) {

				Jelly tempJelly = jellyList.get(i);

				if (tempJelly.getX() > -90 && tempJelly.getX() < 810) {
					
					buffg.drawImage(
						tempJelly.getImage(), 
						tempJelly.getX(), 
						tempJelly.getY(), 
						tempJelly.getWidth(),
						tempJelly.getHeight(), 
						null);
				}
			}
			
			// 장애물을 그린다
			for (int i = 0; i < tacleList.size(); i++) {

				Tacle tempTacle = tacleList.get(i);

				if (tempTacle.getX() > -90 && tempTacle.getX() < 810) {
					
					buffg.drawImage(
						tempTacle.getImage(), 
						tempTacle.getX(), 
						tempTacle.getY(), 
						tempTacle.getWidth(),
						tempTacle.getHeight(), 
						null);
				}
			}
			
			if(c1.isInvincible()) {
				// 쿠키의 alpha값을 받아온다
				alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)c1.getAlpha()/255);
				g2.setComposite(alphaComposite);
				
				// 쿠키를 그린다
				buffg.drawImage(c1.getImage(), c1.getX(), c1.getY(), c1.getWidth(), c1.getHeight(), null);
				
				// alpha값을 되돌린다
				alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)255/255);
				g2.setComposite(alphaComposite);
			} else {
				// 쿠키를 그린다
				buffg.drawImage(c1.getImage(), c1.getX(), c1.getY(), c1.getWidth(), c1.getHeight(), null);
			}
		    
		    buffg.setColor(Color.BLACK);
		    buffg.drawString(Integer.toString(resultScore), 700, 40); // 점수
		    
		    buffg.setColor(Color.GREEN);
		    buffg.fillRect(50, 40, c1.getHealth()/2, 30); // 체력게이지
		    
		    if(escKeyOn) { // esc키를 누를경우 화면을 흐리게 만든다
		    	
		    	// alpha값을 반투명하게 만든다
				alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)100/255);
			    g2.setComposite(alphaComposite);
			    
			    buffg.setColor(Color.BLACK);
			    
		    	buffg.fillRect(0, 0, 850, 550);
		    	
				// alpha값을 되돌린다
				alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)255/255);
			    g2.setComposite(alphaComposite);
		    }
			
			// 버퍼이미지를 화면에 출력한다
			g.drawImage(buffImage, 0, 0, this);
			
		}

 

설명

 

더블버퍼 객체 생성

buffImage는 메모리에서 미리 그려둘 이미지이고,
buffg는 그 이미지를 그리는 역할이라고 생각 하면된다.

buffg가 없다면 이미지를 생성하고 buffg도 buffgImage에서 그릴 권한을 받아온다.

 

투명도 관련이라고 적어 놓았지만, 기능이 다양할 것이다. 여기서는 투명도에서만 사용할 예정.

 

이전에 화면에 그렸던 이미지를 지운다, 지우지 않는다면 이전화면이 겹쳐서 출력될 수 있다.

 

배경이미지를 그린다.

 

발판을 그리는데

발판의 x값을 확인하여 화면에 가까이 있는 녀석들만 그려준다.
(예를들어 맵이 엄청 긴데 안보이는 맵까지 다 그리게 만든다면 엄청 느려질 것이다.
다만 자바에서 이것을 자동으로 없애주는지 모르는 상황이라 직접 제한하였다.)

 

같은 방식으로 젤리와 장애물을 그린다.

 

쿠키를 그리는데 쿠키가 무적일 경우에는 투명하게 깜빡여야 하기 때문에 조건을 추가한다.

 

점수와 체력게이지를 넣는다.

점수는 drawString기능을 사용해보았다. (더좋은 기능이 있다면 변경 예정)

 

esc메뉴를 누르면 화면이 흐려지는 효과를 내기 위한 코드이다.

 

마지막으로 buffImage에 그린 이미지를 g로 패널에 그린다.

 

결과

이런 허접한 그림을 사용한 이유는 규격화 때문이다.

캐릭터나 장애물들을 새로 추가하여도 항상 사이즈를 같게 하며, 여러명이서 작업시 기준이 될 수 있다.

간단한 이미지로 구현한 뒤 이미지만 바꾸면 된다. 

 

 

영상

이미지를 바꾸고 카드레이아웃을 이용하여 인트로 / 선택 / 게임 / 결과 화면을 구현

더보기

쿠키런 프로젝트 시리즈
1.준비
https://ondolroom.tistory.com/297
2.전역공간
https://ondolroom.tistory.com/298
3.JPanel생성자
https://ondolroom.tistory.com/299
4.mapMove메서드
https://ondolroom.tistory.com/300
5.hit메서드
https://ondolroom.tistory.com/301
6.fall메서드
https://ondolroom.tistory.com/302
7.jump메서드
https://ondolroom.tistory.com/303
8.paintComponent 및 결과
https://ondolroom.tistory.com/304

 

https://ondolroom.tistory.com/299

 

자바 스윙 쿠키런 만들기 MyPanel 생성자 수정 (프로젝트 진행)

https://ondolroom.tistory.com/298 자바 스윙 쿠키런 만들기 전역 변수 선언하기 (프로젝트 진행) 들어가기 전에... 아래 내용을 숙지하고 시작하자 https://ondolroom.tistory.com/279 자바 WindowBuilder 설치..

ondolroom.tistory.com

 

지난 생성자 파트에서 점프 메서드를 넣었던 것을 기억 할 것이다.

점프 메서드는 낙하메서드와 거의 비슷하다.

다만 이것도 실제 쿠키런 게임의 작동방식과 약간 다르게 제작되었으니 참고하길 바란다.

 

더보기를 눌러 전체코드 확인

더보기
void jump() {
		new Thread(new Runnable() {

			@Override
			public void run() {

				c1.setCountJump(c1.getCountJump()+1); // 점프 횟수 증가

				int nowJump = c1.getCountJump(); // 이번점프가 점프인지 더블점프인지 저장

				c1.setJump(true); // 점프중으로 변경

				if (c1.getCountJump() == 1) { // 점프 횟수가 1이라면
					
					System.out.println("점프");
					c1.setImage(jumpIc.getImage());
					
				} else if (c1.getCountJump() == 2) { // 점프 횟수가 2라면
					
					System.out.println("더블점프");
					c1.setImage(doubleJumpIc.getImage());
					
				}

				long t1 = Util.getTime(); // 현재시간을 가져온다
				long t2;
				int set = 8; // 점프 계수 설정(0~20) 등으로 바꿔보자
				int jumpY = 1; // 1이상으로만 설정하면 된다.(while문 조건 때문)
				
				while (jumpY >= 0) { // 상승 높이가 0일때까지 반복
					
					t2 = Util.getTime() - t1; // 지금 시간에서 t1을 뺀다
					
					jumpY = set - (int) ((t2) / 40); // jumpY 를 세팅한다.
					
					c1.setY(c1.getY()-jumpY); // Y값을 변경한다.

					if (nowJump != c1.getCountJump()) { // 점프가 한번 더되면 첫번째 점프는 멈춘다.
						break;
					}
					
					if (escKeyOn) {
						long tempT1 = Util.getTime();
						long tempT2 = 0;
						while (escKeyOn) {
							try {
								Thread.sleep(10);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						tempT2 = Util.getTime() - tempT1;
						t1 = t1 + tempT2;
					}
					
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

				if (nowJump == c1.getCountJump()) { // 점프가 진짜 끝났을 때를 확인
					c1.setJump(false); // 점프상태를 false로 변경
				}

			}
		}).start();
	}

 

 

설명

 

점프 쓰레드 생성

점프 도중에도 맵은 이동하고, 우리는 더블점프를 해야 하기 때문에

새 쓰레드를 이용한다.

 

점프 횟수를 1 늘려주고

이번 쓰레드의 점프가 1회인지 2회인지 임시변수에 저장한다. (추후점프 상태를 false로 변환하기 위함)

예를들어 1회 점프 중에 한번 더 점프하여 지금점프가 break되었는데 점프 상태를 false로 바꾸게되면 문제가 발생한다.

그것을 방지하기 위해 현재 점프횟수를 기록해두는 것.

c1(캐릭터)의 점프 상태도 true로 변경하자.

그리고 이번 점프가 1회려면 점프, 2회라면 더블점프 이미지로 변경한다.

 

내용이 길어서 아래에서 나누어서 설명하겠다.

 

Util클래스에서 타임스탬프 시간을 가져온다.

t2는 바로 뒤에 쓸예정이니 선언만 해둔다.

set은 점프 계수인데 처음 점프량을 설정한다.
(예를들어 캐릭터의 높이가 8씩 증가하다가 7 6 5 4 3 2 1 0 씩 증가하게 되는데, set은 그 초기값이다.)

 

상승은 점프량이 0이 될때까지 반복하게 된다.

t2에 현재시간 - 점프가 시작된시간을 저장하고

set에 그값을 가공한뒤 빼서 jumpY를 세팅한다. (jumpY는 상승량이라고 보면된다.)

그것으로 캐릭터의 높이를 재설정해준다.

 

캐릭터가 1회점프 도중 한번더 점프를 하면 이번 점프는 멈춰야된다. (중복되면 가속상승이 되버린다.)

 

낙하 때와 마찬가지로 esc메뉴가 뜬 상태에서는 위와 같이 시간을 저장해두었다가 t1에 넣어준다.

 

while무한반복문에는 대기시간을 넣어 리소스를 안정화 시키자

이번 쓰레드의 점프가 진짜 끝났는지 확인하고 맞다면 점프상태를 false로 변경한다.

 

다음편에 계속

더보기

쿠키런 프로젝트 시리즈
1.준비
https://ondolroom.tistory.com/297
2.전역공간
https://ondolroom.tistory.com/298
3.JPanel생성자
https://ondolroom.tistory.com/299
4.mapMove메서드
https://ondolroom.tistory.com/300
5.hit메서드
https://ondolroom.tistory.com/301
6.fall메서드
https://ondolroom.tistory.com/302
7.jump메서드
https://ondolroom.tistory.com/303
8.paintComponent 및 결과
https://ondolroom.tistory.com/304

 

https://ondolroom.tistory.com/300

 

자바 스윙 쿠키런 만들기 mapMove() 메서드 (프로젝트 진행)

https://ondolroom.tistory.com/299 자바 스윙 쿠키런 만들기 MyPanel 생성자 수정 (프로젝트 진행) https://ondolroom.tistory.com/298 자바 스윙 쿠키런 만들기 전역 변수 선언하기 (프로젝트 진행) 들어가기 전..

ondolroom.tistory.com

 

위 맵이동에서 낙하 메서드를 추가 하였다.

쿠키런에서 낙하는 점프가 끝나거나 발밑이 비었을 경우 발동 된다.

 

이번에는 낙하코드를 만들 것인데, 글쓴이가 만든 것은 실제 쿠키런게임과 차이가 있다.

실제 쿠키런에서는 게임속도가 빨라지면 점프높이는 같으면서 빠르게 올라갔다가 내려오게 되는데

미처 구현하지 못해 일단 지금 만든 것으로 글을 작성하고자 한다.

 

더보기로 전체코드 확인

더보기
	void fall() {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while (true) {
					
					foot = c1.getY() + c1.getHeight(); // 캐릭터 발 위치 재스캔
					
					// 발바닥이 발판보다 위에 있으면 작동
					if (
						!escKeyOn // 일시중지가 발동 안됐을 때
					    &&foot < nowField  // 공중에 있으며
						&& !c1.isJump() // 점프 중이 아니며
						&& !c1.isFall()) { // 떨어지는 중이 아닐 때
					
						c1.setFall(true);  // 떨어지는 중으로 전환
						System.out.println("낙하");

						if (c1.getCountJump() == 2) { // 더블점프가 끝났을 경우 낙하 이미지로 변경
							c1.setImage(fallIc.getImage());
						}

						long t1 = Util.getTime(); // 현재시간을 가져온다
						long t2;
						int set = 1; // 처음 낙하량 (0~10) 까지 테스트해보자
					
						while (foot < nowField) { // 발이 발판에 닿기 전까지 반복
							
							t2 = Util.getTime() - t1; // 지금 시간에서 t1을 뺀다
							
							int fallY = set + (int) ((t2) / 40); // 낙하량을 늘린다.
							
							foot = c1.getY() + c1.getHeight(); // 캐릭터 발 위치 재스캔
							
							if (foot + fallY >= nowField) { // 발바닥+낙하량 위치가 발판보다 낮다면 낙하량을 조정한다.
								fallY = nowField - foot;
							}

							c1.setY(c1.getY()+fallY); // Y좌표에 낙하량을 더한다

							if (c1.isJump()) { // 떨어지다가 더블 점프를 하면 낙하중지
								break;
							}
							
							if (escKeyOn) {
								long tempT1 = Util.getTime();
								long tempT2 = 0;
								while (escKeyOn) {
									tempT2 = Util.getTime() -tempT1;
									try {
										Thread.sleep(10);
									} catch (InterruptedException e) {
										e.printStackTrace();
									}
								}
								t1 = t1 + tempT2;
							}
							
							try {
								Thread.sleep(10);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}

						}
						c1.setFall(false);

						if (
							downKeyOn // 다운키를 누른상태고
							&& !c1.isJump() // 점프 상태가 아니고
							&& !c1.isFall() // 낙하 상태가 아니고
							&& c1.getImage() != slideIc.getImage()) { // 쿠키 이미지가 슬라이드 이미지가 아닐 경우
							
							c1.setImage(slideIc.getImage()); // 쿠키 이미지를 슬라이드로 변경
							
						} else if (
							!downKeyOn // 다운키를 누른상태가 아니고
							&& !c1.isJump() // 점프 상태가 아니고
							&& !c1.isFall() // 낙하 상태가 아니고
							&& c1.getImage() != cookieIc.getImage()) { // 쿠키 이미지가 기본 이미지가 아닐 경우
							
							c1.setImage(cookieIc.getImage());
						}

						if (!c1.isJump()) { // 발이 땅에 닿고 점프 중이 아닐 때 더블점프 카운트를 0으로 변경
							c1.setCountJump(0);
						}
					}
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
	}

 

 

설명

코드가 약간 복잡할 수 있으므로 사진을 조금씩 중복하여 올리도록 하겠다.

 

낙하 쓰레드 생성

낙하 중에도 맵은 움직이고 우리는 더블점프를 하여야 한다.

그렇기 때문에 쓰레드로 만들어 준다.

 

낙하 쓰레드는 게임 도중 내내 발동 해야 하기 때문에 while문을 이용하였다.

먼저 캐릭터 발위치를 재스캔한다.

그리고 조건을 확인하게되는데
esc메뉴가 안떠있을 경우(메뉴가떠있는데 계속 낙하하면 안되기 때문)
발판보다 발이 위에 있을 경우
점프가 아닌 경우 (상승량이 0이 되었을 경우다)
떨어지는 중이 아닌 경우 (지금 떨어지는 중인데 또 떨어질 수는 없지 않은가.)

떨어지는 중으로 전환된다. c1.setFall(true); 

 

쿠키런에서는 더블점프 후에만 낙하 이미지로 변경된다.

Util.getTime();으로 t1에 현재시간을 저장해준다.

Util.getTime();은 1970년 1월1일부터 현재시간까지 1/1000초씩 더하여 long값을 리턴해준다.

set값은 낙하량인데 처음에 1픽셀씩 떨어지고 점점 커지게 만드려는 의도이다.

 

위 코드는 길기 때문에 이미지를 나누어서 설명하도록 한다.

그 후 내부 while을 돌게 되면 발이 발판에 닫기 전까지 하락하게 된다.

t2에 현재시간 - 낙하시작시간 을 저장한다. (시간이 갈수록 늘어나기 때문에 낙하량으로 변환하려는 의도)

fallY에 set + (int) ((t2) / 40); 를 해주는데 t2는 1초에 1000씩 늘어나기 때문에 값을 줄이기 위해 40을 나누어주었다.

다시한번 캐릭터 발 위치을 재스캔하고

발바닥+낙하량 위치가 발판보다 낮다면 낙하량을 조절한다.

예를들어  발판이 400인데 캐릭터가 399위치에 있다.

그런데 낙하량이 10일경우 409가 되어 발판보다 아래에 있게된다.

그러한 점을 방지하기위해 발판에 닫기 직전에 낙하량을 조절한다.

그 값을 캐릭터의 Y 값에 추가하게 된다.

떨어지는 도중 점프를 하거나 더블점프를 하면 낙하를 멈춰야 하기 때문에 break문을 사용한다.

esc메뉴 관련 코드인데 , 이전의 코드들과는 약간 다른 것을 확인 했을 것이다.

대기하는 동안에도 현실시간은 계속 흐르고 있고 그렇게 되면 낙하량이 무지막지하게 늘어나게된다.

그것을 방지하기위해 임시로 지나간 시간을 저장해 두었다가 t1에 추가해주는 것이다.

마찬가지로 무한반복문에는 대기시간을 걸어둬야한다.

 

만약 발이 땅에 닿아 내부 반복문을 빠져나가게 된다면,

쿠키는 하락 상태를 벗어나게 된다.

위 코드는 단순히 캐릭터의 이미지를 변환하는 코드인데, 조건이 까다로워 보인다.

하지만 결과적으로는 캐릭터나 누웠는지 서있는지 판단하는 코드이다.
(글쓴이도 이리저리 테스트후 조건을 늘린 것이다.)

낙하 메서드가 끝났어도 점프 때문에 끝났을 경우 점프카운트를 0으로 만들면 안되기 때문에

점프 중이 아닐 때에만 카운트를 0으로 바꾼다.

외부반복문의 대기시간이다.

 

다음편에 계속

더보기

쿠키런 프로젝트 시리즈
1.준비
https://ondolroom.tistory.com/297
2.전역공간
https://ondolroom.tistory.com/298
3.JPanel생성자
https://ondolroom.tistory.com/299
4.mapMove메서드
https://ondolroom.tistory.com/300
5.hit메서드
https://ondolroom.tistory.com/301
6.fall메서드
https://ondolroom.tistory.com/302
7.jump메서드
https://ondolroom.tistory.com/303
8.paintComponent 및 결과
https://ondolroom.tistory.com/304

 

https://ondolroom.tistory.com/300

 

자바 스윙 쿠키런 만들기 mapMove() 메서드 (프로젝트 진행)

https://ondolroom.tistory.com/299 자바 스윙 쿠키런 만들기 MyPanel 생성자 수정 (프로젝트 진행) https://ondolroom.tistory.com/298 자바 스윙 쿠키런 만들기 전역 변수 선언하기 (프로젝트 진행) 들어가기 전..

ondolroom.tistory.com

이번 편에는 위 코드 중 장애물에 피격 되었을 경우 발동되는 hit메서드를 만들어 보려고 한다.

 

더보기를 눌러 전체코드 확인

더보기
void hit() {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				
				c1.setInvincible(true); // 쿠키를 무적상태로 전환
				
				System.out.println("피격무적시작");
				
				c1.setHealth(c1.getHealth() - 100); // 쿠키의 체력을 100 깎는다
				
				c1.setImage(hitIc.getImage()); // 쿠키를 부딛힌 모션으로 변경
				
				c1.setAlpha(80); // 쿠키의 투명도를 80으로 변경
				
				try { // 0.5초 대기
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				if(c1.getImage() == hitIc.getImage()) { // 0.5초 동안 이미지가 바뀌지 않았다면 기본이미지로 변경
					
					c1.setImage(cookieIc.getImage());
					
				}
				
				
				for (int j = 0; j < 11; j++) { // 2.5초간 캐릭터가 깜빡인다. (피격후 무적 상태를 인식)
					
					if(c1.getAlpha() == 80) { // 이미지의 알파값이 80이면 160으로 
						
						c1.setAlpha(160);
						
					} else { // 아니면 80으로
						
						c1.setAlpha(80);
						
					}
					try {
						Thread.sleep(250);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				c1.setAlpha(255); // 쿠키의 투명도를 정상으로 변경
				
				c1.setInvincible(false);
				System.out.println("피격무적종료");
			}
		}).start();
	}

 

설명

 

피격시 새 쓰레드 가동

피격 이펙트가 도는 동안에도 우리는 움직여야 하기 때문에 

따로 쓰레드를 만들어 줘야한다.

 

피격시 필요한 변수 수정

피격이되면, 캐릭터 상태가 무적(Invincible)이 되니 true로 바꿔주자
( 생성자에서 무적인 경우에는 낙사하지 않도록 미리 제작해 두었다는 점을 참고하자.)

부딛히게 되면 체력이 100 깎이고 부딛힌 모션으로 변경된다.

그리고 부딛힌 점을 확인하기 위해 캐릭터의 투명도를 80으로 내린다.
(0투명 ~ 반투명 ~ 255불투명)

그리고 0.5초간 피격모션이 끝나도록 다른 이미지로 변경이 되지 않았다면 달리는 모션으로 다시 바꿔준다.

 

캐릭터 깜빡이게 하기

캐릭터가 무적인 상태를 확실히 보여주기 위해 캐릭터를 깜빡이게 할 것이다.

for문으로 2.5초간 깜빡이게 되는데 투명도가 80일경우 160으로, 160일 경우 80으로 변경된다.

마지막으로 무적이 끝나기 전에 투명도를 255로 변경하고

무적을 종료한다.

 

다음편에 계속

 

 

더보기

쿠키런 프로젝트 시리즈
1.준비
https://ondolroom.tistory.com/297
2.전역공간
https://ondolroom.tistory.com/298
3.JPanel생성자
https://ondolroom.tistory.com/299
4.mapMove메서드
https://ondolroom.tistory.com/300
5.hit메서드
https://ondolroom.tistory.com/301
6.fall메서드
https://ondolroom.tistory.com/302
7.jump메서드
https://ondolroom.tistory.com/303
8.paintComponent 및 결과
https://ondolroom.tistory.com/304

 

https://ondolroom.tistory.com/299

 

자바 스윙 쿠키런 만들기 MyPanel 생성자 수정 (프로젝트 진행)

https://ondolroom.tistory.com/298 자바 스윙 쿠키런 만들기 전역 변수 선언하기 (프로젝트 진행) 들어가기 전에... 아래 내용을 숙지하고 시작하자 https://ondolroom.tistory.com/279 자바 WindowBuilder 설치..

ondolroom.tistory.com

 

패널 생성자에서 필요한 메서드들 중 맵 발판 젤리 장애물 이동 및 작동을  아래에서 구현하고자 한다.

 

더보기로 전체코드 확인

더보기
void mapMove() {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while (true) {
					
					if(runPage > 800) { // 800픽셀 이동 마다 체력이 10씩 감소한다 (추후 맵길이에 맟추어 감소량 조절)
						c1.setHealth(c1.getHealth()-10);
						runPage = 0;
					}
					
					runPage +=gameSpeed; // 화면이 이동하면 runPage에 이동한 만큼 저장된다.
					
					
					if (b11.getX() < -(b11.getWidth()-1)) { // 배경1-1 이 -(배경넓이)보다 작으면, 즉 화면밖으로 모두나가면 배경 1-2뒤에 붙음
						b11.setX(b11.getWidth());
					}
					if (b12.getX() < -(b12.getWidth()-1)) { // 배경1-2 가 -(배경넓이)보다 작으면, 즉 화면밖으로 모두나가면 배경 1-1뒤에 붙음
						b12.setX(b12.getWidth());
					}
					
					
					// 배경의 x좌표를 -1 해준다 (왼쪽으로 흐르는 효과)
					b11.setX(b11.getX()-gameSpeed/3); 
					b12.setX(b12.getX()-gameSpeed/3);
					
					
					// 발판위치를 -4 씩 해준다. (왼쪽으로 흐르는 효과)
					for (int i = 0; i < fieldList.size(); i++) {
						
						Field tempField = fieldList.get(i); // 임시 변수에 리스트 안에 있는 개별 발판을 불러오자
						
						if(tempField.getX() < -90) { // 발판의  x좌표가 -90 미만이면 해당 발판을 제거한다.(최적화)
							
							fieldList.remove(tempField);
							
						} else {
							
							tempField.setX(tempField.getX() - gameSpeed);  // 위 조건에 해당이 안되면 x좌표를 줄이자
							
						}
					}
					
					// 젤리위치를 -4 씩 해준다.
					for (int i = 0; i < jellyList.size(); i++) {
						
						Jelly tempJelly = jellyList.get(i); // 임시 변수에 리스트 안에 있는 개별 젤리를 불러오자
						
						if(tempJelly.getX() < -90) { // 젤리의 x 좌표가 -90 미만이면 해당 젤리를 제거한다.(최적화)
							
							fieldList.remove(tempJelly);
							
						} else {
							
							tempJelly.setX(tempJelly.getX() - gameSpeed); // 위 조건에 해당이 안되면 x좌표를 줄이자
							
							foot = c1.getY() + c1.getHeight(); // 캐릭터 발 위치 재스캔
							
							if( // 캐릭터의 범위 안에 젤리가 있으면 아이템을 먹는다.
								c1.getImage() != slideIc.getImage()
								&& tempJelly.getX() + tempJelly.getWidth()*20/100 >= c1.getX()
								&& tempJelly.getX() + tempJelly.getWidth()*80/100 <= face
								&& tempJelly.getY() + tempJelly.getWidth()*20/100 >= c1.getY()
								&& tempJelly.getY() + tempJelly.getWidth()*80/100 <= foot
								&& tempJelly.getImage() != jellyEffectIc.getImage()) {
								
								tempJelly.setImage(jellyEffectIc.getImage()); // 젤리의 이미지를 이펙트로 바꾼다
								resultScore = resultScore + tempJelly.getScore(); // 총점수에 젤리 점수를 더한다
								
								
							} else if( // 슬라이딩 하는 캐릭터의 범위 안에 젤리가 있으면 아이템을 먹는다.
								c1.getImage() == slideIc.getImage()
								&& tempJelly.getX() + tempJelly.getWidth()*20/100 >= c1.getX()
								&& tempJelly.getX() + tempJelly.getWidth()*80/100 <= face
								&& tempJelly.getY() + tempJelly.getWidth()*20/100 >= c1.getY() + c1.getHeight()*1/3
								&& tempJelly.getY() + tempJelly.getWidth()*80/100 <= foot
								&& tempJelly.getImage() != jellyEffectIc.getImage()) {
								
								tempJelly.setImage(jellyEffectIc.getImage()); // 젤리의 이미지를 이펙트로 바꾼다
								resultScore = resultScore + tempJelly.getScore(); // 총점수에 젤리 점수를 더한다
								
							}
						}
					}
					
					// 장애물위치를 - 4 씩 해준다.
					for (int i = 0; i < tacleList.size(); i++) {
						
						Tacle tempTacle = tacleList.get(i); // 임시 변수에 리스트 안에 있는 개별 장애물을 불러오자
						
						if(tempTacle.getX() < -90) { 
							
							fieldList.remove(tempTacle); // 장애물의 x 좌표가 -90 미만이면 해당 젤리를 제거한다.(최적화)
							
						} else {
							
							tempTacle.setX(tempTacle.getX() - gameSpeed);	// 위 조건에 해당이 안되면 x좌표를 줄이자
							
							face = c1.getX() + c1.getWidth(); // 캐릭터 정면 위치 재스캔
							foot = c1.getY() + c1.getHeight(); // 캐릭터 발 위치 재스캔
							
							if( // 무적상태가 아니고 슬라이드 중이 아니며 캐릭터의 범위 안에 장애물이 있으면 부딛힌다
									!c1.isInvincible()
									&& c1.getImage() != slideIc.getImage()
									&& tempTacle.getX() + tempTacle.getWidth()/2 >= c1.getX()
									&& tempTacle.getX() + tempTacle.getWidth()/2 <= face
									&& tempTacle.getY() + tempTacle.getHeight()/2 >= c1.getY()
									&& tempTacle.getY() + tempTacle.getHeight()/2 <= foot) {
								
								hit(); // 피격 + 무적 쓰레드 메서드
								
							} else if( // 슬라이딩 아닐시 공중장애물
									!c1.isInvincible()
									&& c1.getImage() != slideIc.getImage()
									&& tempTacle.getX() + tempTacle.getWidth()/2 >= c1.getX()
									&& tempTacle.getX() + tempTacle.getWidth()/2 <= face
									&& tempTacle.getY() <= c1.getY()
									&& tempTacle.getY() + tempTacle.getHeight()*95/100 > c1.getY()) {
								
								
								hit(); // 피격 + 무적 쓰레드 메서드
								
							}else if( // 무적상태가 아니고 슬라이드 중이며 캐릭터의 범위 안에 장애물이 있으면 부딛힌다
									!c1.isInvincible()
									&& c1.getImage() == slideIc.getImage()
									&& tempTacle.getX() + tempTacle.getWidth()/2 >= c1.getX()
									&& tempTacle.getX() + tempTacle.getWidth()/2 <= face
									&& tempTacle.getY() + tempTacle.getHeight()/2 >= c1.getY() + c1.getHeight()*2/3
									&& tempTacle.getY() + tempTacle.getHeight()/2 <= foot) {
								
								hit(); // 피격 + 무적 쓰레드 메서드
								
							} else if( // 슬라이딩시 공중장애물
									!c1.isInvincible()
									&& c1.getImage() == slideIc.getImage()
									&& tempTacle.getX() + tempTacle.getWidth()/2 >= c1.getX()
									&& tempTacle.getX() + tempTacle.getWidth()/2 <= face
									&& tempTacle.getY() < c1.getY()
									&& tempTacle.getY() + tempTacle.getHeight()*95/100 > c1.getY() + c1.getHeight()*2/3) {
								
								hit(); // 피격 + 무적 쓰레드 메서드
							}
						}
					}
					
					// 쿠키가 밟을 발판을 계산하는 코드
					int tempField; // 발판위치를 계속 스캔하는 지역변수
					int tempNowField; // 캐릭터와 발판의 높이에 따라 저장되는 지역변수, 결과를 nowField에 저장한다
					
					
					// 쿠키가 무적상태라면 낙사 하지 않기 때문에 400으로 세팅 / 무적이 아니라면 2000(낙사지점);
					if (c1.isInvincible()) {
						tempNowField = 400;
					} else {
						tempNowField = 2000;
					}

					for (int i = 0; i < fieldList.size(); i++) { // 발판의 개수만큼 반복

						int tempX = fieldList.get(i).getX(); // 발판의 x값

						if (tempX > c1.getX()-60 && tempX <= face) { // 발판이 캐릭 범위 안이라면 

							tempField = fieldList.get(i).getY(); // 발판의 y값을 tempField에 저장한다

							
							foot = c1.getY() + c1.getHeight(); // 캐릭터 발 위치 재스캔
							
							// 발판위치가 tempNowField보다 높고, 발바닥 보다 아래 있다면
							// 즉, 캐릭터 발 아래에  제일 높이 있는 발판이라면 tempNowField에 저장한다.
							if (tempField < tempNowField && tempField >= foot) {

								tempNowField = tempField;

							}
						}
					}

					nowField = tempNowField; // 결과를 nowField에 업데이트 한다.
					
					
					
					if(escKeyOn) { // esc키를 누르면 게임이 멈춘다
						while (escKeyOn) {
							try {
								Thread.sleep(10);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
					
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
				}
			}
		}).start();
	}

 

설명

 

맵이동 메서드

맵이동 메서드는 쓰레드를 하나 새로 생성한다.

 

캐릭터 체력 자연감소

맵을 일정거리 이상 이동하면 캐릭터의 체력이 깎이도록 하였다.

 

배경 이동

같은 배경을 무한 반복하는 내용이다.

1번째 배경이 -(배경넓이)가 되면 2번째 배경이 X좌표 0으로 오게될테니 2번째 배경 뒤쪽으로 옮기는 식이다.

배경은 느리게 흐르기 때문에 게임속도의 1/3만 해주었다.

 

발판 이동

발판과 젤리, 장애물은 같은량을 움직인다. 참고하자.

발판리스트에서 발판을 하나씩 가져와서 화면 밖으로 나간 발판은 최적화를 위해 제거해주고

그렇지 않은 발판은 게임속도에 맟춰서 왼쪽으로 이동시킨다.

 

젤리 이동 및 동작

젤리는 장판과 비슷하지만, 캐릭터가 젤리를 먹는 이벤트가 있다.

젤리를 먹는 조건은 위와 같다.

캐릭터의 발위치를 확인한 뒤

젤리 획득범위를 넓게하고 싶다면

20/100을 1/100 가까이로 줄이면되고, 80/100을 100/100 가까이로 늘이면된다.

슬라이딩을 하는경우 높이가 낮아지기 때문에 Y값에다가 캐릭터의 높이의 1/3을 더해주었다.

 

 

장애물 이동 및 동작

장애물은 코드가 너무 길어 접은글로 올렸다

더보기
					// 장애물위치를 - 4 씩 해준다.
					for (int i = 0; i < tacleList.size(); i++) {
						
						Tacle tempTacle = tacleList.get(i); // 임시 변수에 리스트 안에 있는 개별 장애물을 불러오자
						
						if(tempTacle.getX() < -90) { 
							
							fieldList.remove(tempTacle); // 장애물의 x 좌표가 -90 미만이면 해당 젤리를 제거한다.(최적화)
							
						} else {
							
							tempTacle.setX(tempTacle.getX() - gameSpeed);	// 위 조건에 해당이 안되면 x좌표를 줄이자
							
							face = c1.getX() + c1.getWidth(); // 캐릭터 정면 위치 재스캔
							foot = c1.getY() + c1.getHeight(); // 캐릭터 발 위치 재스캔
							
							if( // 무적상태가 아니고 슬라이드 중이 아니며 캐릭터의 범위 안에 장애물이 있으면 부딛힌다
									!c1.isInvincible()
									&& c1.getImage() != slideIc.getImage()
									&& tempTacle.getX() + tempTacle.getWidth()/2 >= c1.getX()
									&& tempTacle.getX() + tempTacle.getWidth()/2 <= face
									&& tempTacle.getY() + tempTacle.getHeight()/2 >= c1.getY()
									&& tempTacle.getY() + tempTacle.getHeight()/2 <= foot) {
								
								hit(); // 피격 + 무적 쓰레드 메서드
								
							} else if( // 슬라이딩 아닐시 공중장애물
									!c1.isInvincible()
									&& c1.getImage() != slideIc.getImage()
									&& tempTacle.getX() + tempTacle.getWidth()/2 >= c1.getX()
									&& tempTacle.getX() + tempTacle.getWidth()/2 <= face
									&& tempTacle.getY() <= c1.getY()
									&& tempTacle.getY() + tempTacle.getHeight()*95/100 > c1.getY()) {
								
								
								hit(); // 피격 + 무적 쓰레드 메서드
								
							}else if( // 무적상태가 아니고 슬라이드 중이며 캐릭터의 범위 안에 장애물이 있으면 부딛힌다
									!c1.isInvincible()
									&& c1.getImage() == slideIc.getImage()
									&& tempTacle.getX() + tempTacle.getWidth()/2 >= c1.getX()
									&& tempTacle.getX() + tempTacle.getWidth()/2 <= face
									&& tempTacle.getY() + tempTacle.getHeight()/2 >= c1.getY() + c1.getHeight()*2/3
									&& tempTacle.getY() + tempTacle.getHeight()/2 <= foot) {
								
								hit(); // 피격 + 무적 쓰레드 메서드
								
							} else if( // 슬라이딩시 공중장애물
									!c1.isInvincible()
									&& c1.getImage() == slideIc.getImage()
									&& tempTacle.getX() + tempTacle.getWidth()/2 >= c1.getX()
									&& tempTacle.getX() + tempTacle.getWidth()/2 <= face
									&& tempTacle.getY() < c1.getY()
									&& tempTacle.getY() + tempTacle.getHeight()*95/100 > c1.getY() + c1.getHeight()*2/3) {
								
								hit(); // 피격 + 무적 쓰레드 메서드
							}
						}
					}

 

장애물은 경우의 수는 아래와 같다.

1. 달리는 중 아래에 있는 장애물
2. 달리는 중 머리 위에 있는 장애물
3. 슬라이드 중 아래에 있는 장애물
4. 슬라이드 중 머리 위에 있는 장애물

캐릭터는 장애물에 부딛히면 캐릭터가 데미지를 입고 깜빡이며 3초간 무적상태가 된다.(추후 hit() 메서드 추가)

그러므로 무적상태가 아닐때 충돌이 발생해야한다.(!c1.isInvincible())
또한 이미지가 슬라이드 중인지 달리는 중인지 구분하고
(downKeyOn으로 구분하게 되면 공중에서 다운키를 눌러서 범위를 줄일 수 있게된다.)

장애물은 커보이지만 피격범위가 좁기때문에 (이용자 배려인듯)

x값은 장애물 넓이의 정중앙으로

y값도 높이의 정중앙으로 범위를 정했다.

공중장애물은 캐릭터 머리와 만나기 때문에 높이의 95퍼센트 까지로 지정하였다.

데미지를 입지 않는다면 96~99퍼센트 까지 조정해보자.

 

발판 계산

쿠키가 밟을 발판을 계산하는 코드이다.

예를들어 발판이 없고 무적이라면 최저바닥이 400으로서 낙사하지 않는다.

발판이 없고 무적이 아니라면 바닥은 2000으로 화면 밖으로 나가 낙사하게 된다.

그 경우가 아니라면 발판들을 모두 스캔해서 캐릭터가 달리고 있는 위치의 발판만 추려 낸뒤

제일 높은 곳에 있는 발판의 위치를 업데이트한다.

예를들어 Y값을 기준으로 400위치에만 발판이 하나 있고 캐릭터가  300위치에 있다면 nowField는  400

400위치 200위치 두 곳에 발판이 있는데

캐릭터가 100에 있다면 200이 발판이 되며 캐릭터가 300에 있다면 400이 발판이 된다
(머리위에 있는 발판으로 떨어질 수는 없지 않은가.)

 

esc관련 코드

esc를 누르면 위의 모든 코드들이 중지되고 esc를 다시누를 때까지 대기한다.

 

while문이기 때문에 0.01초정도 간격을 준다 (주지 않으면 리소스 사용량이 폭증한다.)

 

다음편에 계속

더보기

쿠키런 프로젝트 시리즈
1.준비
https://ondolroom.tistory.com/297
2.전역공간
https://ondolroom.tistory.com/298
3.JPanel생성자
https://ondolroom.tistory.com/299
4.mapMove메서드
https://ondolroom.tistory.com/300
5.hit메서드
https://ondolroom.tistory.com/301
6.fall메서드
https://ondolroom.tistory.com/302
7.jump메서드
https://ondolroom.tistory.com/303
8.paintComponent 및 결과
https://ondolroom.tistory.com/304

 

https://ondolroom.tistory.com/298

 

자바 스윙 쿠키런 만들기 전역 변수 선언하기 (프로젝트 진행)

들어가기 전에... 아래 내용을 숙지하고 시작하자 https://ondolroom.tistory.com/279 자바 WindowBuilder 설치 들어가기 전에... 필자는 STS툴(이클립스)를 사용하고 있으며, WindowBuilder로 스윙을 간단히 구현..

ondolroom.tistory.com

 

 

더보기를 눌러 전체코드확인

더보기

 

package Test4;

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.plaf.basic.BasicTreeUI.TreeCancelEditingAction;

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;

public class CookieRun2 {

	private JFrame frame;

	private JPanel panel; // 패널

	private Button escButton; // esc 버튼 (테스트 중)

	// 배경 이미지
	private ImageIcon backIc = new ImageIcon("testimg/backTest.png"); // 제일 뒷 배경
	private ImageIcon secondBackIc = new ImageIcon("배경이미지2"); // 2번째 배경

	// 쿠키 이미지 아이콘들
	private ImageIcon cookieIc = new ImageIcon("testimg/cookieTest.png"); // 기본모션
	private ImageIcon jumpIc = new ImageIcon("testimg/jumpTest.png"); // 점프모션
	private ImageIcon doubleJumpIc = new ImageIcon("testimg/doubleJumpTest.png"); // 더블점프모션
	private ImageIcon fallIc = new ImageIcon("testimg/fallTest.png"); // 낙하모션(더블 점프 후)
	private ImageIcon slideIc = new ImageIcon("testimg/slideTest.png"); // 슬라이드 모션
	private ImageIcon hitIc = new ImageIcon("testimg/hitTest.png"); // 부딛히는 모션

	// 젤리 이미지 아이콘들
	private ImageIcon jelly1Ic = new ImageIcon("testimg/jelly1Test.png");
	private ImageIcon jelly2Ic = new ImageIcon("testimg/jelly2Test.png");
	private ImageIcon jelly3Ic = new ImageIcon("testimg/jelly3Test.png");
	private ImageIcon jellyHPIc = new ImageIcon("testimg/jellyHPTest.png");

	private ImageIcon jellyEffectIc = new ImageIcon("testimg/effectTest.png");

	// 발판 이미지 아이콘들
	private ImageIcon field1Ic = new ImageIcon("testimg/footTest.png"); // 발판
	private ImageIcon field2Ic = new ImageIcon("testimg/footTest2.png"); // 공중발판

	// 장애물 이미지 아이콘들
	private ImageIcon tacle10Ic = new ImageIcon("testimg/tacleTest10.png"); // 1칸 장애물
	private ImageIcon tacle20Ic = new ImageIcon("testimg/tacleTest20.png"); // 2칸 장애물
	private ImageIcon tacle30Ic = new ImageIcon("testimg/tacleTest30.png"); // 3칸 장애물
	private ImageIcon tacle40Ic = new ImageIcon("testimg/tacleTest40.png"); // 3칸 장애물

	// 리스트 생성
	private List<Jelly> jellyList = new ArrayList<>(); // 젤리 리스트

	private List<Field> fieldList = new ArrayList<>(); // 발판 리스트

	private List<Tacle> tacleList = new ArrayList<>(); // 장애물 리스트

	private int runPage = 0; // 한 화면 이동할때마다 체력을 깎기 위한 변수

	private int runStage = 1; // 스테이지를 확인하는 변수이다. (미구현)

	private int resultScore = 0; // 결과점수를 수집하는 변수

	private int gameSpeed = 3; // 게임 속도

	private int nowField = 2000; // 발판의 높이를 저장.

	private boolean escKeyOn = false; // 일시정지를 위한 esc키 확인

	private boolean downKeyOn = false; // 다운키 눌렀는지 여부

	int face; // 쿠키의 정면
	int foot; // 쿠키의 발

	// 이미지 파일로 된 맵을 가져온다.
	private int[] sizeArr; // 이미지의 넓이와 높이를 가져오는 1차원 배열
	private int[][] colorArr; // 이미지의 x y 좌표의 픽셀 색값을 저장하는 2차원배열

	private Image buffImage; // 더블버퍼 이미지
	private Graphics buffg; // 더블버퍼 g

	private AlphaComposite alphaComposite; // 투명도 관련 오브젝트

	Cookie c1; // 쿠키 오브젝트
	Back b11; // 배경1-1 오브젝트
	Back b12; // 배경1-2 오브젝트

	class MyPanel extends JPanel {

		public MyPanel() {

			setFocusable(true);

			// 쿠키 인스턴스 생성 / 기본 자료는 클래스안에 내장 되어 있기 때문에 이미지만 넣었다.
			c1 = new Cookie(cookieIc.getImage());

			// 쿠키의 정면 위치 / 쿠키의 x값과 높이를 더한 값
			face = c1.getX() + c1.getWidth();

			// 쿠키의 발밑 위치 / 쿠키의 y값과 높이를 더한 값
			foot = c1.getY() + c1.getHeight();

			// 배경1-1 인스턴스 생성
			b11 = new Back(backIc.getImage(), 0, 0, // y 값 (조정 필요)
					backIc.getImage().getWidth(null), backIc.getImage().getHeight(null));

			// 배경1-2 인스턴스 생성
			b12 = new Back(backIc.getImage(), backIc.getImage().getWidth(null), 0, // y 값 (조정 필요)
					backIc.getImage().getWidth(null), backIc.getImage().getHeight(null));

			// 맵 정보 불러오기
			try {
				sizeArr = Util.getSize("testimg/firstMap1.png"); // 맵 사이즈를 배열에 저장
				colorArr = Util.getPic("testimg/firstMap1.png"); // 맵 픽셀값을 배열에 저장
			} catch (Exception e1) {
				e1.printStackTrace();
			}

			int maxX = sizeArr[0]; // 맵의 넓이
			int maxY = sizeArr[1]; // 맵의 높이

			for (int i = 0; i < maxX; i += 1) { // 젤리는 1칸을 차지하기 때문에 1,1사이즈로 반복문을 돌린다.
				for (int j = 0; j < maxY; j += 1) {
					if (colorArr[i][j] == 16776960) { // 색값이 16776960일 경우 기본젤리 생성
						// 좌표에 40을 곱하고, 넓이와 높이는 30으로 한다.
						jellyList.add(new Jelly(jelly1Ic.getImage(), i * 40, j * 40, 30, 30, 1234));

					} else if (colorArr[i][j] == 13158400) { // 색값이 13158400일 경우 노란젤리 생성
						// 좌표에 40을 곱하고, 넓이와 높이는 30으로 한다.
						jellyList.add(new Jelly(jelly2Ic.getImage(), i * 40, j * 40, 30, 30, 2345));

					} else if (colorArr[i][j] == 9868800) { // 색값이 9868800일 경우 노란젤리 생성
						// 좌표에 40을 곱하고, 넓이와 높이는 30으로 한다.
						jellyList.add(new Jelly(jelly3Ic.getImage(), i * 40, j * 40, 30, 30, 3456));

					} else if (colorArr[i][j] == 16737280) { // 색값이 16737280일 경우 피 물약 생성
						// 좌표에 40을 곱하고, 넓이와 높이는 30으로 한다.
						jellyList.add(new Jelly(jellyHPIc.getImage(), i * 40, j * 40, 30, 30, 4567));
					}

				}
			}

			for (int i = 0; i < maxX; i += 2) { // 발판은 4칸을 차지하는 공간이기 때문에 2,2사이즈로 반복문을 돌린다.
				for (int j = 0; j < maxY; j += 2) {
					if (colorArr[i][j] == 0) { // 색값이 0 일경우 (검은색)
						// 좌표에 40을 곱하고, 넓이와 높이는 80으로 한다.
						fieldList.add(new Field(field1Ic.getImage(), i * 40, j * 40, 80, 80));

					} else if (colorArr[i][j] == 6579300) { // 색값이 6579300 일경우 (회색)
						// 좌표에 40을 곱하고, 넓이는 80으로 높이는 40 한다.
						fieldList.add(new Field(field1Ic.getImage(), i * 40, j * 40, 80, 40));
					}
				}
			}

			for (int i = 0; i < maxX; i += 2) { // 장애물은 4칸 이상을 차지한다. 추후 수정
				for (int j = 0; j < maxY; j += 2) {
					if (colorArr[i][j] == 16711680) { // 색값이 16711680일 경우 (빨간색) 1칸
						// 좌표에 40을 곱하고, 넓이와 높이는 80으로 한다.
						tacleList.add(new Tacle(tacle10Ic.getImage(), i * 40, j * 40, 80, 80, 0));

					} else if (colorArr[i][j] == 16711830) { // 색값이 16711830일 경우 (분홍) 2칸
						// 좌표에 40을 곱하고, 넓이와 높이는 160으로 한다.
						tacleList.add(new Tacle(tacle20Ic.getImage(), i * 40, j * 40, 80, 160, 0));

					} else if (colorArr[i][j] == 16711935) { // 색값이 16711830일 경우 (핫핑크) 3칸
						// 좌표에 40을 곱하고, 넓이와 높이는 240으로 한다.
						tacleList.add(new Tacle(tacle30Ic.getImage(), i * 40, j * 40, 80, 240, 0));
					}
				}
			}

			// 리페인트 전용 쓰레드
			new Thread(new Runnable() {

				@Override
				public void run() {
					while (true) {
						repaint();

						if (escKeyOn) { // esc 키를 누를경우 리페인트를 멈춘다
							while (escKeyOn) {
								try {
									Thread.sleep(10);
								} catch (Exception e) {
									e.printStackTrace();
								}
							}
						}

						try {
							Thread.sleep(10);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
			}).start();

			mapMove(); // 배경 발판 장애물 작동 메서드

			fall(); // 낙하 스레드 발동 메서드

			addKeyListener(new KeyAdapter() { // 키 리스너 추가

				@Override
				public void keyPressed(KeyEvent e) {
					if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { // esc키를 눌렀을 때
						if (!escKeyOn) {
							escKeyOn = true;
							add(escButton);
							repaint(); // 화면을 어둡게 하기위한 리페인트
						} else {
							remove(escButton);
							escKeyOn = false;
						}
					}

					if (!escKeyOn) {
						if (e.getKeyCode() == KeyEvent.VK_SPACE && c1.getCountJump() < 2) { // 스페이스 키를 누르고 더블점프가 2가 아닐때

							jump(); // 점프 메서드 가동

						}
						if (e.getKeyCode() == KeyEvent.VK_DOWN) { // 다운키를 눌렀을 때

							downKeyOn = true; // downKeyOn 변수를 true로

							if (c1.getImage() != slideIc.getImage() // 쿠키이미지가 슬라이드 이미지가 아니고
									&& !c1.isJump() // 점프 중이 아니며
									&& !c1.isFall()) { // 낙하 중도 아닐 때

								c1.setImage(slideIc.getImage()); // 이미지를 슬라이드이미지로 변경

							}
						}
					}
				}

				@Override
				public void keyReleased(KeyEvent e) {

					if (e.getKeyCode() == KeyEvent.VK_DOWN) { // 다운키를 뗐을 때

						downKeyOn = false; // downKeyOn 변수를 false로

						if (c1.getImage() != cookieIc.getImage() // 쿠키이미지가 기본이미지가 아니고
								&& !c1.isJump() // 점프 중이 아니며
								&& !c1.isFall()) { // 낙하 중도 아닐 때

							c1.setImage(cookieIc.getImage()); // 이미지를 기본이미지로 변경
						}
					}
				}
			});
		}

		@Override
		protected void paintComponent(Graphics g) {

		}

	}

	void mapMove() {

	}

	void hit() {

	}

	void fall() {

	}

	void jump() {

	}

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					CookieRun2 window = new CookieRun2();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public CookieRun2() {
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frame = new JFrame();
		frame.setBounds(100, 100, 800, 500);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		panel = new MyPanel();
		frame.getContentPane().add(panel, BorderLayout.CENTER);
		panel.setLayout(null);

		escButton = new Button("재시작");
		escButton.setBounds(350, 240, 50, 30);
		escButton.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				panel.remove(escButton);
				escKeyOn = false;
			}
		});
	}
}

 

설명

 

setFocusable 키입력 우선권획득

내가 만든 패널에 키보드나 마우스 리스너의 우선권을 준다.

 

인스턴스 생성

쿠키와 배경 인스턴스를 생성하자

 

맵정보 로딩

게임을 플레이할 맵 정보를 로딩한다.

Util클래스 안의 getSize와 getPic메서드를 이용하여 사이즈와 색값을 가져온다.

 

아래는 맵 예시이다.

 

색값을 이용하여 맵을 불러오자.

 

젤리 객체 생성

저장한 픽셀 정보를 이용하여 색값별로 젤리 객체 생성하여를 jellyList에 저장한다.

 

발판 객체 생성

발판은 바닥에 있는 발판과 공중에 있는 발판 2가지이다.

색깔로 구분하여 로딩한다.

 

장애물 객체 생성

장애물 객체를 생성한다.

장애물은 1칸 2칸 3칸 (4단도 있으나 미구현)이 있으니 각각 색값을 달리하여 리스트에 추가한다.

 

리페인트 전용 쓰레드

리페인트만 전담하는 쓰레드이다. 

추가 사항으로 esc를 누르면 해제될 때까지 그림을 그리지 않도록 하였다.

 

맵이동, 낙하 쓰레드를 실행하는 메서드 (추후 제작)

원래는 맵이동과 낙하 쓰레드를 직접 적었으나 메서드로 따로 분리하였다.

추후 블로깅에서 알아보도록 하자.

 

키리스너 추가

키리스너를 추가한다.

내용은 아래와 같다.

 

키를 눌렀을 때

키를 눌렀을 경우,

esc키를 누르면 escKeyOn 변수가 true가 되고 버튼을 화면에 보이게한다.

스페이스 와 다운키 등은 esc메뉴가 없을 때에만 작동하도록 하였고,

스페이스 키를 누르면 점프 메서드가 작동,

다운키를 누르면 슬라이드 이미지로 변경 되도록 하였다.

 

키를 뗐을 때

키 릴리즈 메서드이다.

다운키를 떼었을 때 슬라이드 이미지를 달리는 이미지로 다시 바꿀 것이다.

 

다음편에 계속

더보기

쿠키런 프로젝트 시리즈
1.준비
https://ondolroom.tistory.com/297
2.전역공간
https://ondolroom.tistory.com/298
3.JPanel생성자
https://ondolroom.tistory.com/299
4.mapMove메서드
https://ondolroom.tistory.com/300
5.hit메서드
https://ondolroom.tistory.com/301
6.fall메서드
https://ondolroom.tistory.com/302
7.jump메서드
https://ondolroom.tistory.com/303
8.paintComponent 및 결과
https://ondolroom.tistory.com/304

 

들어가기 전에...

아래 내용을 숙지하고 시작하자

https://ondolroom.tistory.com/279

 

자바 WindowBuilder 설치

들어가기 전에... 필자는 STS툴(이클립스)를 사용하고 있으며, WindowBuilder로 스윙을 간단히 구현하고, 자세한 코드는 직접 짜는 것을 추천한다. 이클립스 마켓에서 윈도우빌더를 설치하자. WindowBuilder생성..

ondolroom.tistory.com

 

더보기를 눌러서 전체 코드를 확인하자.

더보기
package Test4;

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.plaf.basic.BasicTreeUI.TreeCancelEditingAction;

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;

public class CookieRun2 {

	private JFrame frame;
	
	JPanel panel; // 패널
	
	Button escButton; // esc 버튼 (테스트 중)
	
	
	// 배경 이미지
	private ImageIcon backIc = new ImageIcon("testimg/backTest.png"); // 제일 뒷 배경
	private ImageIcon secondBackIc = new ImageIcon("배경이미지2"); // 2번째 배경

	
	// 쿠키 이미지 아이콘들
	private ImageIcon cookieIc = new ImageIcon("testimg/cookieTest.png"); // 기본모션
	private ImageIcon jumpIc = new ImageIcon("testimg/jumpTest.png"); // 점프모션
	private ImageIcon doubleJumpIc = new ImageIcon("testimg/doubleJumpTest.png"); // 더블점프모션
	private ImageIcon fallIc = new ImageIcon("testimg/fallTest.png"); // 낙하모션(더블 점프 후)
	private ImageIcon slideIc = new ImageIcon("testimg/slideTest.png"); // 슬라이드 모션
	private ImageIcon hitIc = new ImageIcon("testimg/hitTest.png"); // 부딛히는 모션
	
	
	// 젤리 이미지 아이콘들
	private ImageIcon jelly1Ic = new ImageIcon("testimg/jelly1Test.png");
	private ImageIcon jelly2Ic = new ImageIcon("testimg/jelly2Test.png");
	private ImageIcon jelly3Ic = new ImageIcon("testimg/jelly3Test.png");
	private ImageIcon jellyHPIc = new ImageIcon("testimg/jellyHPTest.png");
	
	private ImageIcon jellyEffectIc = new ImageIcon("testimg/effectTest.png");
	
	
	// 발판 이미지 아이콘들
	private ImageIcon field1Ic = new ImageIcon("testimg/footTest.png"); // 발판
	private ImageIcon field2Ic = new ImageIcon("testimg/footTest2.png"); //공중발판
	
	
	// 장애물 이미지 아이콘들
	private ImageIcon tacle10Ic = new ImageIcon("testimg/tacleTest10.png"); // 1칸 장애물
	private ImageIcon tacle20Ic = new ImageIcon("testimg/tacleTest20.png"); // 2칸 장애물
	private ImageIcon tacle30Ic = new ImageIcon("testimg/tacleTest30.png");	// 3칸 장애물
	private ImageIcon tacle40Ic = new ImageIcon("testimg/tacleTest40.png");	// 3칸 장애물
	
	
	// 리스트 생성
	private List<Jelly> jellyList = new ArrayList<>(); // 젤리 리스트

	private List<Field> fieldList = new ArrayList<>(); // 발판 리스트

	private List<Tacle> tacleList = new ArrayList<>(); // 장애물 리스트
	
	private int runPage = 0; // 한 화면 이동할때마다 체력을 깎기 위한 변수
	
	private int runStage = 1; // 스테이지를 확인하는 변수이다. (미구현)
	
	private boolean escKeyOn = false; // 일시정지를 위한 esc키 확인
	
	private int resultScore = 0; // 결과점수를 수집하는 변수
	
	private int gameSpeed = 3; // 게임 속도
	
	private int nowField = 2000; // 발판의 높이를 저장.
	
	private boolean downKeyOn = false; // 다운키 눌렀는지 여부
	
	
	// 이미지 파일로 된 맵을 가져온다.
	private int[] sizeArr; // 이미지의 넓이와 높이를 가져오는 1차원 배열
	private int[][] colorArr; // 이미지의 x y 좌표의 픽셀 색값을 저장하는 2차원배열
	
	
	private Image buffImage; // 더블버퍼 이미지
	private Graphics buffg; // 더블버퍼 g
	
	
	private AlphaComposite alphaComposite; // 투명도 관련 오브젝트
	
	Cookie c1; // 쿠키 오브젝트
	Back b11; // 배경1-1 오브젝트
	Back b12; // 배경1-2 오브젝트
	
	int face; // 쿠키의 정면
	int foot; // 쿠키의 발

	
	class MyPanel extends JPanel{
		
		public MyPanel() {
			
		}

		
		@Override
		protected void paintComponent(Graphics g) {
			
		}
		
	}
	
	void mapMove(){

	}
	
	void hit() {

	}
	
	void fall() {

	}
	
	void jump() {

	}
	
	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					CookieRun2 window = new CookieRun2();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public CookieRun2() {
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frame = new JFrame();
		frame.setBounds(100, 100, 800, 500);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		panel = new MyPanel();
		frame.getContentPane().add(panel, BorderLayout.CENTER);
		panel.setLayout(null);
		
		escButton = new Button("재시작");
		escButton.setBounds(350, 240, 50, 30);
		escButton.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				panel.remove(escButton);
				escKeyOn = false;
			}
		});
	}	
}

 

설명

 

프레임과 패널 버튼 등을 전역공간에 배치

프레임과 패널 버튼 등을 전역공간에 두는 것은, 클래스 내의 다른객체나 메서드들이 이용 할 수 있도록 하기 위함이다.

 

이미지 배치

게임에 이용할 이미지들을 배치한다

 

리스트 배치

젤리와 발판, 장애물 객체들을 저장할 리스트를 배치한다.

 

게임관련 변수 배치

 

 

paintComponent 관련 레퍼런스 배치

 

 

기타 레퍼런스들

클래스 내부 어디서든 사용할 수 있도록 전역공간에 선언해둔다

 

커스텀 JPanel

내가 직접 커스텀한 패널을 사용하기 위해 클래스 안에 inner클래스로 만들어준다.

 

메서드

메서드 들을 채워나갈 예정이다.

 

기타

위 코드 대부분은 windowBuilder에서 만들어 준 것이고

수정한 것은

new JPanel을 new MyPanel로 내가 커스텀한 패널로 교체 해주었다.

그리고 재시작 버튼은 미리 만들어 두었다. (esc키를 누르면 생성 되었다가 다시 사라지게 할 예정)

더보기

쿠키런 프로젝트 시리즈
1.준비
https://ondolroom.tistory.com/297
2.전역공간
https://ondolroom.tistory.com/298
3.JPanel생성자
https://ondolroom.tistory.com/299
4.mapMove메서드
https://ondolroom.tistory.com/300
5.hit메서드
https://ondolroom.tistory.com/301
6.fall메서드
https://ondolroom.tistory.com/302
7.jump메서드
https://ondolroom.tistory.com/303
8.paintComponent 및 결과
https://ondolroom.tistory.com/304

 

준비물

이미지 파일이다.

그림판으로 만든 테스트용 이미지임.

testimg.zip
0.01MB

 

아래는 기본 클래스 파일들이다. 더보기로 볼 수 있다.
생성자 및 게터 세터는 lombok 라이브러리를 이용하였다.(모르시는 분들은 인터넷 검색 )

더보기

배경

package Test4;

import java.awt.Image;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Back {
	private Image image;
	private int x;
	private int y;
	private int width;
	private int height;
}

 

쿠키

package Test4;

import java.awt.Image;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Cookie {
	
	private Image image; // 쿠키 이미지
	
	// 쿠키의 좌표와 넓이 높이
	private int x = 160;
	private int y = 0;
	private int width = 80;
	private int height = 120;
	
	// 쿠키의 투명도 (0이 투명, 255가 완전 불투명)
	private int alpha = 255;
	
	// 쿠키의 체력
	private int health = 1000;
	
	// 쿠키의 상태 (거대화와 가속화 미구현)
	private int big = 0; // 거대화 남은 시간
	private int fast = 0; // 가속화 남은 시간
	private int countJump = 0; // 점프 횟수
	private boolean invincible = false; // 무적 여부
	private boolean fall = false; // 낙하 여부
	private boolean jump = false; // 점프 여부
	
	public Cookie(Image image){ // 이미지만 넣어주자
		this.image = image;
	}
}

 

발판

package Test4;

import java.awt.Image;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Field {
	
	private Image image; // 발판 이미지
	
	// 발판의 좌표와 넓이 높이
	private int x;
	private int y;
	private int width;
	private int height;
}

 

젤리

package Test4;

import java.awt.Image;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Jelly {
	private Image image; // 젤리 이미지
	
	// 젤리들의 좌표와 크기
	private int x;
	private int y;
	private int width;
	private int height;
	
	// 젤리의 점수
	private int score;
}

 

장애물

package Test4;

import java.awt.Image;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Tacle {
	
	private Image image; // 장애물 이미지
	
	// 장애물의 좌표와 넓이 높이
	private int x;
	private int y;
	private int width;
	private int height;
	
	// 장애물 상태
	private int state;
}

 

유틸

package Test4;

import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.io.File;
import java.sql.Timestamp;
import java.time.LocalDateTime;

import javax.imageio.ImageIO;

public class Util {

	// 이미지의 사이즈 가져오기
	public static int[] getSize(String src) throws Exception {
		File imgf = new File(src);
		BufferedImage img = ImageIO.read(imgf);
		int width = img.getWidth();
		int height = img.getHeight();
		int[] tempSize = {width, height};
		return tempSize;
	}
	
	// 이미지의 픽셀값 가져오기
	public static int[][] getPic(String src) throws Exception{
		File imgf = new File(src);
		BufferedImage img = ImageIO.read(imgf);
		int width = img.getWidth();
		int height = img.getHeight();
		int[] pixels=new int[width*height];
		PixelGrabber grab = new PixelGrabber(img, 0, 0, width, height, pixels, 0,width);
		grab.grabPixels();
		
		int[][] picture=new int[width][height];
		for(int i=0;i<pixels.length;i++)
		      picture[i%width][i/width]=pixels[i] + 16777216;
		return picture;
	}
	
	// 현재시간 가져오기
	public static long getTime() {
		return Timestamp.valueOf(LocalDateTime.now()).getTime();
	}
	
	// substring으로 발판 정보 검색
	// 배열로 장판 및 젤리 배치 할 때 사용 (현재 미사용)
//	public static int getGround(String ground, int index) {
//		return Integer.parseInt(ground.substring(index, index + 1));
//	}
	
}

 

 

+ Recent posts