트랜잭션 - 일을 처리할 때의  최소 단위(송금)

서비스 - 트랜잭션을 포함한 관련된 로직들이 묶여 있는 것(송금 후 잘들어갔는지 리턴하여 메시지를 띄우는 등)

ex)기능: 입금(서비스)
1 update()

1이 트랜잭션
ex)기능: 송금(서비스)
1 update() 나의 돈을 줄인다
2 update() 상대방의 돈을 늘인다

1~2가 트랜잭션

 

하나의 트랜잭션이 끝나면 롤백을 하거나 커밋을 해야한다.

데이터베이스에 변경이 일어나는 것(dml) - UPDATE, INSERT, DELETE 이 묶여있다면 트랜잭션이다.

 

ex)기능: 입금(서비스)
1 update() - 트랜잭션

2-1 업데이트가 실패했을 경우 메시지를 띄운다
2-2 업데이트가 성공했을 경우 성공관련 창을 띄운다

 

위의 모든 내용을 완수하면 서비스가 된다

테이블이 나오면 테이블에 대한 모델을 만들어야하고,

모델에 대한 DAO(데이터 엑세스 오브젝트)를 만들어 줘야한다.

롬복을 설치한 후

 

아래와 같은 생성자로 new를 하려면 값의 순서가 햇갈릴 수가 있다.

이런걸 방지하기위한 방법 중 하나가 빌더 패턴이다.

 

메인 프레임에서 데이터를 넣는 부분을 아래와 같이 만들어보자.

 

아래와 같이 잘 나온다.

 

모든 경우의 수의 생성자를 사용할 수 있다.

즉, 생성자에 필요한 데이터를 몇가지 빼먹어도 인스턴스가 생성된다

* 다만 기본생성자가 필요하다면 그것은 직접 만들어 줘야한다

 

빌더패턴에 대해 더 알아보기

https://johngrib.github.io/wiki/builder-pattern/#%EB%B9%8C%EB%8D%94-%ED%8C%A8%ED%84%B4effective-java-%EC%8A%A4%ED%83%80%EC%9D%BC

 

빌더 패턴(Builder Pattern)

객체의 생성 방법과 표현 방법을 분리한다

johngrib.github.io

 

필수인자를 세팅하는법.

개요

 

 

*설계할때에는 먼저 테이블을 만들어야하는데 최근에는 자바에서 클래스로 모델을 먼저 만드는 경우도 있다고 한다.

*모델마다 dao가 필요하다는 점 꼭 참고하자

 

 

그룹을 스트링으로하면 도메인설정이 되지 않는다.

아래에서 enum으로 설정하는 법을 알아보자.

 

 

enum을 쓰면 강제성이 생긴다.

 

타입을 지정해버리면 다른 타입을 넣을 수 없다.

 

group타입을 이넘으로 바꾸자

 

싱글톤으로 MemberDao 제작

 

빌드패스

 

 

테스트를 위한 내부라이브러리 빌드패스

 

JUnit은 일부 메서드를 테스트하기위한 기능이다.

 

일단 4번을 쓰자

 

스태틱이면 안되고 void여야한다고 에러가남(참고만하자)

 

테스트 파일을 하나 만들어서 아까 것을 테스트 해본다.

 

 

정상이면 빨강이다.

 

 

DBUtils에는 DB에 필요한 부가기능들을 넣자

 

gui

package address.gui;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import address.model.GroupType;
import address.model.Member;

public class MainFrame extends JFrame {
	
	
	private JFrame mainFrame = this;
	
	private Container backgroundPanel;
	
	private JPanel topPanel, menuPanel, listPanel;
	
	private JButton homeButton, frButton, coButton, scButton, faButton, addButton;
	
	private JList<Member> userList;
	
	private DefaultListModel<Member> listModel; // Jlist에 이것을 넣어야 한다.
	
	private JScrollPane jsPane;
	
	public MainFrame() {
		initObject();
		initData();
		initDesign();
		initListener();
		setVisible(true);
	}
	
	// new
	private void initObject() {
		backgroundPanel = getContentPane();
		
		topPanel = new JPanel();
		menuPanel = new JPanel();
		listPanel = new JPanel();
		
		homeButton = new JButton("주소록 전체");
		frButton = new JButton("친구");
		coButton = new JButton("회사");
		scButton = new JButton("학교");
		faButton = new JButton("가족");
		addButton = new JButton("추가");
		
		listModel = new DefaultListModel<>();
		userList = new JList<>(listModel);
		
		jsPane = new JScrollPane(userList); // 뒤의 패널색깔을 유지하려면 그냥 ScrollPane을 이용하자
	}
	
	// 데이터 초기화
	private void initData() {
		for (int i = 1; i < 31; i++) {
			listModel.addElement(new Member(i, "홍길동", "0102222", "부산시", GroupType.친구));
		}
	}
	
	// 디자인
	private void initDesign() {
		// 1. 기본세팅
		setTitle("주소록 메인");
		setSize(400, 500);
		setLocationRelativeTo(null); // 모니터 중앙에 배치
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		// 2. 패널세팅
		backgroundPanel.setLayout(new BorderLayout());
		topPanel.setLayout(new GridLayout(2,1));
		menuPanel.setLayout(new GridLayout(1,4));
		listPanel.setLayout(new BorderLayout());
		
		// 3. 디자인
		userList.setFixedCellHeight(50); // 리스트각각의 높이
		topPanel.setPreferredSize(new Dimension(0, 100)); // 그리드레이아웃이라 가로를 0으로해도 자동으로 꽉차게된다.
		
		// 4. 패널에 컴포넌트 추가
		menuPanel.add(frButton);
		menuPanel.add(coButton);
		menuPanel.add(scButton);
		menuPanel.add(faButton);
		
		listPanel.add(jsPane);
		
		topPanel.add(homeButton);
		topPanel.add(menuPanel);
		
		backgroundPanel.add(topPanel, BorderLayout.NORTH);
		backgroundPanel.add(listPanel, BorderLayout.CENTER);
		backgroundPanel.add(addButton, BorderLayout.SOUTH);
	}
	
	// 리스너 등록
	private void initListener() {
		
	}
}

 

 

카드레이아웃의 기본은 아래 블로그에서 이해하자

https://m.blog.naver.com/PostView.nhn?blogId=shimchan2&logNo=70108791279&proxyReferer=https:%2F%2Fwww.google.com%2F

 

자바 GUI - CardLayout

import java.awt.*; import java.awt.event.*; public class CardLayoutTest {  /** * 카드레이아웃 이용...

blog.naver.com

 

이 글에서는 카드레이아웃과 그것을 사용한 리스너 컨트롤에 대해서 설명할 것이다.

 

기본 프레임 제작은 windowBuider를 사용하였으니 참고하자.

모든 파일은 따로 만들었다.

아래에 파일이 있으니 참고로 사용하자.

listenAdapter.java
0.00MB
CookieRun.java
0.00MB
IntroPanel.java
0.00MB
LoginPanel.java
0.00MB

 

 

리스너 어댑터 파일

리스너 어댑터는 필요없는 리스너들을 쓰지 않을 수 있도록 하는 중간 다리 역할이다.

키리스너 마우스리스너 액션리스너 인터페이스를 받아서 어댑터를 만들었다.

 

인트로 패널 파일

다른 파일로 만든 인트로 클래스이다.

이미지를 그리는 기능 밖에 없다.

 

로그인패널 파일

더보기를 눌러 로그인패널 코드 확인

더보기
package Test5;

import java.awt.AlphaComposite;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;

public class LoginPanel extends JPanel {
	
	JLabel idLabel;
	JLabel pwLabel;
	
	JTextField idField;
	JPasswordField pwField;
	
	JButton loginBtn;
	JButton signBtn;
	
	ImageIcon loginIc = new ImageIcon("img/out/intro.png");
	private AlphaComposite alphaComposite;
	
	public LoginPanel(Object o) {
		idLabel = new JLabel("ID : ");
		Font f1 = new Font("Arial", Font.PLAIN, 20);
		idLabel.setFont(f1);
		idLabel.setBounds(550, 50, 100, 20);
		add(idLabel);
		
		pwLabel = new JLabel("PW : ");
		pwLabel.setFont(f1);
		pwLabel.setBounds(550, 80, 100, 20);
		add(pwLabel);
		
		idField = new JTextField();
		idField.setBounds(650, 50, 110, 20);
		add(idField);
		
		pwField = new JPasswordField();
		pwField.setBounds(650, 80, 110, 20);
		add(pwField);
		
		loginBtn = new JButton("login");
		loginBtn.setName("loginBtn");
		loginBtn.addMouseListener((MouseListener)o);
		loginBtn.setBounds(660, 110, 100, 20);
		add(loginBtn);
		
		signBtn = new JButton("sign");
		signBtn.setName("signBtn");
		signBtn.setBounds(550, 110, 100, 20);
		signBtn.addMouseListener((MouseListener)o);
		add(signBtn);
	}
	
	public String getId(){
		return idField.getText();
	}
	
	public String getPw(){
		return pwField.getText();
	}
	
	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D)g;
		
		alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)100/255);
		g2.setComposite(alphaComposite);
		
		g.drawImage(loginIc.getImage(), -60, 0,/* this.getWidth(), this.getHeight(),*/ null);
		
		alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)255/255);
		g2.setComposite(alphaComposite);
		
	}
}

 

코드가 길어보이지만 크게 어려울 것은 없다.

라벨 2개

텍스트필드, 패스워드필드 하나씩

버튼 2개 선언

생성자에서 각 컴포넌트를 생성하고 위치를 정해주고 추가한다.

생성자가 Object 매개변수를 받는데,

메인 클래스가 리스너 역할을 하기 때문에 메인에서 자기자신을 로그인패널에 넣어주면

리스너를 이용할 수 있기 때문이다.

지금 이해가 바로 안되더라도 계속 진행해보자.

아이디와 패스워드를 리턴하는 메서드를 생성

 

 

메인 클래스 파일

메인클래스는 맨위에서 만들어둔 listenAdapter를 상속받는다.

그리고 전역공간에 각종 패널들의 레퍼런스만 선언해두고,

카드레이아웃 레퍼런스도 선언해두자.

 

 

초기화 메서드

초기화 메서드에서 카드레이아웃 레퍼런스(cl)에  카드레이아웃 객체를 연결하고

프레임의 레이아웃을 카드로 변경한다.

로그인패널에서 Object를 매개변수로 받았다는 점을 기억할 것이다.

위 사진을보면 LoginPanel생성자 뒤에 this를 넘겨준다.

this는 이 메인클래스 자체를 의미하며, 메인클래스는 리스너를 상속받고 있으니 리스너 역할을 하게된다.

인트로패널에 마우스리스너를 넣어주자. 마찬가지로 this를 넘겨준다.

카드레이아웃은 컨텐트페인에 여러개의 패널을 추가할 수 있다.

만들어둔 패널들을 모두 추가하자. 추가할 시 이름을 정해줘야한다.

마지막으로 리스너 내용들이다.

메인 클래스는 리스너를 상속받았기때문에 클래스 내부에 리스너들을 넣을 수 있다.

인트로 패널에서 사용될 기능이다.

화면을 누르면 로그인페이지로 넘어가도록 하였다.

 

 

더보기

쿠키런 프로젝트 시리즈
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으로 바꾼다.

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

 

다음편에 계속

+ Recent posts