SlideShare a Scribd company logo
1 of 29
Download to read offline
프로젝트 기획서 1/29
Java를 이용한 게임 만들기
※ INDEX
게임 설명
코드설명
Ÿ Server 생성과 Client접속
Ÿ 송수신(Send-Receive 코드)
Ÿ 게임의 정보를 담고있는 Tetris.java
Ÿ 블록의 정보를 담고있는 Block.java
Ÿ 게임을 시작하는 Game.java
Ÿ 게임보드를 그리는 DrawGame.java
Ÿ 블럭을 생성한는 BlockFactory.java
Ÿ 블럭을 떨어뜨리는 BlockDown.java
Ÿ 채워진 줄을 삭제하는 Clear.java
구현까지의 과정
느낀점
2018. 11. 26 / 정보통신공학과 김민태, 손정호, 김수현
다인용 테트리스 게임 만들기
프로젝트 기획서 2/29
게임 설명
서버 ­ 클라이언트간의 통신 테트리스 게임을 만들었습니다.
서버생성 후 클라이언트가 접속하면 게임화면이 나오고, 서버에서 start버튼을 통해 게임이 시작됩니다.
왼쪽에는 자신의 게임보드가 나오고, 오른쪽에는 상대방의 게임 보드가 나옵니다.
블록이 위쪽까지 올라와서 더 이상 생성이 불가능해지면 게임이 멈춥니다.
방향키와 스페이스바를 이용해 게임을 진행하며 스페이스 바는 블록을 바로 떨어뜨리는 기능, 방향키의 UP은
블록을 회전시킵니다. LEFT, DOW, RIGHT는 자신들의 방향으로 1칸씩 이동하는 기능을 합니다.
충돌하는 순간 멈추는 것이 아니라 충돌한 직후 0.5초간은 회전이나 좌, 우 방향으로 이동이 가능합니다.
줄을 한칸 지울시 마다 100점씩 점수가 올라갑니다.
코드 설명
서버-클라이언트 모두 송수신과 테트리스 게임에 관한 코드를 가지고 있지만 게임에 관한 코드와 송수신에 관한 코
드가 거의 동일합니다. 통신에 관한 코드는 Server가 가지고 있는 코드이고, 게임에 관한 코드는 Client의 코드입니
다. Server와 Client의 전체 코드와 통신이 아닌 테트리스 게임코드는 Github에 올려놓겠습니다.
프로젝트 기획서 3/29
Server생성-Client 접속
Server.javaimport java.net.*;
import java.io.*;
public class Server {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(9999);
System.out.println("클라이언트를 기다리는중");
Socket socket = server.accept();
System.out.println("연결");
Tetris tetris = new Tetris();
Block block = new Block();
Game game = new Game(tetris, block);
Receive rec = new Receive(socket);
rec.SetGame(tetris);
Send sen = new Send(socket);
sen.SetGame(tetris, block);
rec.start();
sen.start();
game.start();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
Client.java
import java.net.*;
import java.io.*;
public class Client {
public static void main(String[] args) {
try {
Socket client = new Socket("localhost",9999);
Tetris tetris = new Tetris();
Block block = new Block();
Game game = new Game(tetris, block);
Receive rec = new Receive(client);
rec.SetGame(tetris);
Send sen = new Send(client);
sen.SetGame(tetris,block);
rec.start();
sen.start();
game.start();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
프로젝트 기획서 4/29
ServerSocket server = new ServerSocket(9999)
9999포트를 Client가 접속 가능한 포트로 설정 합니다.
Socket client = new Socket("localhost",9999)
Client는 Server의 ip주소와 Server가 열어둔 포트번호를 Socket에 넣어 Server와 Client를 연결합니다.
Socket socket = server.accept();
Client가 접속을 요청하면 Server는 접속을 허락하고 socket에 클라이언트의 정보를 저장합니다.
Tetris tetris = new Tetris();
Block block = new Block();
Game game = new Game(tetris, block);
Receive rec = new Receive(socket);
rec.SetGame(tetris);
Send sen = new Send(socket);
sen.SetGame(tetris, block);
Server와 Client의 통신과 게임을 위해 필요한 클래스를 생성합니다.
rec.start();
sen.start();
game.start();
통신과 게임시작을 동시에 합니다.
프로젝트 기획서 5/29
Receice.javaimport java.io.*;
import java.net.*;
public class Receive extends Thread{
private Socket server;
private Tetris tetris;
String[] Code; //클라이언트가 보낸 정보
public Receive(Socket socket) {
this.server = socket;
}
public void SetGame(Tetris tetris) {
this.tetris = tetris;
}
public void run() {
try {
BufferedReader in = new BufferedReader(new
InputStreamReader(server.getInputStream()));
String inputMessage;
while(true) {
inputMessage = in.readLine();
Code = inputMessage.split(" "); // 클라이언트가 보낸 정보를 공백으로
쪼갬
tetris.client_score = Integer.parseInt(Code[0]); // 정보의 두번째값은
클라이언트의 점수
tetris.client_width = Integer.parseInt(Code[1]); // 정보의 세번째 값은
클라이언트보드의 넓이
tetris.client_height = Integer.parseInt(Code[2]); // 정보의 네번째 값은
클라이언트보드의 높이
tetris.client_block_num = Integer.parseInt(Code[3]); // 클라이언트의
벽돌 개수
tetris.client_next_blocktype = Integer.parseInt(Code[4]); // 클라이언트의
다음 블ㅓㄱ
tetris.client_block_pos = new int[tetris.client_block_num];
for(int i = 0; i < tetris.client_block_num; i++) {// 클라이언트의 벽돌
좌표
tetris.client_block_pos[i] = Integer.parseInt(Code[5+i]);
}
System.out.print("클라이언트 점수: " + tetris.client_score + "
클라이언트 보드넓이 : " + tetris.client_width + " 클라이언트 보드높이 : " +tetris.client_height + "
클라이언트 블럭 개수 : " + tetris.client_block_num + " 클라이언트의 다음 블럭타입: "+
tetris.client_next_blocktype +" 클라이언트 벽돌 좌표 :");
for(int i = 0; i<tetris.client_block_num; i++)
System.out.print(" " + tetris.client_block_pos[i]);
System.out.println();
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
프로젝트 기획서 6/29
수신기능을 하는 코드입니다.
public Receive(Socket socket) {
this.server = socket;
}
public void SetGame(Tetris tetris) {
this.tetris = tetris;
}
통신에 필요한 ip와 포트정보를 연결하고 게임에 관한 정보를 연결합니다.
String[] Code;
받은 정보를 저장하는 변수입니다.
while(true) {
inputMessage = in.readLine();
Code = inputMessage.split(" "); // 클라이언트가 보낸 정보를 공백으로
쪼갬
tetris.client_score = Integer.parseInt(Code[0]); // 정보의 두번째값은
클라이언트의 점수
tetris.client_width = Integer.parseInt(Code[1]); // 정보의 세번째 값은
클라이언트보드의 넓이
tetris.client_height = Integer.parseInt(Code[2]); // 정보의 네번째 값은
클라이언트보드의 높이
tetris.client_block_num = Integer.parseInt(Code[3]); // 클라이언트의
벽돌 개수
tetris.client_next_blocktype = Integer.parseInt(Code[4]); // 클라이언트의
다음 블럭
tetris.client_block_pos = new int[tetris.client_block_num];
for(int i = 0; i < tetris.client_block_num; i++) {// 클라이언트의 벽돌
좌표
tetris.client_block_pos[i] = Integer.parseInt(Code[5+i]);
}
정보를 보낼 때 공백을 이용해 정보를 구분하여 보냈기 때문에 split()함수를 이용해서 이 정보를 구분하여 받
습니다.
서버는 점수와 넓이, 높이, 벽돌개수, 다음 블록타입을 받지만 클라이언트는 여기에 게임의 준비상태를 추가적
으로 받습니다.
프로젝트 기획서 7/29
Send.javaimport java.io.*;
import java.net.*;
import java.util.*;
public class Send extends Thread{
private Socket server;
private Tetris tetris;
private Block block;
public Send(Socket socket) {
this.server = socket;
}
public void SetGame(Tetris tetris, Block block) {
this.tetris = tetris;
this.block = block;
}
public void run() {
try {
BufferedWriter out = new BufferedWriter(new
OutputStreamWriter(server.getOutputStream()));
String outMessage;
boolean ready; // 게임 준비상태
int score; // 서버의 점수
int size; // 서버의 벽돌개수
int[] pos; // 별돌 좌표
int [] board; // 좌표값
int width; // 테트리스 보드의 넓이
int height; // 테트리스 보드의 높이
int blocktype;
while(true) {
int j = 0;
size = 0;
for(int i = 0; i<tetris.width*tetris.height; i++) { // 벽돌 개수 파악
if(tetris.board[i/tetris.width][i%tetris.width]==1)
size++;
}
pos = new int[size]; // 벽돌 개수 만큼의 크기를 갖는 좌표배열 생성
for(int i = 0; i<tetris.width*tetris.height; i++) { // 좌표배열에
벽돌좌표 저장
if(tetris.board[i/tetris.width][i%tetris.width]==1) {
pos[j] = i;
j++;
}
}
board = new int[size];
for(int i = 0; i<size; i++)
board[i]=pos[i];
ready = tetris.ready;
score = tetris.score;
width = tetris.width;
height = tetris.height;
blocktype = block.type;
System.out.println("서버 상태 : "+ready+" 서버 점수: "+score+" 서버
보드 넓이 : " + width + " 서버 보드 높이: " + height + " 서버 블록 개수: "+ size +" 서버 다음
블럭타입: " + blocktype);
outMessage = ready + " " + score + " " + width + " " + height + "
프로젝트 기획서 8/29
" +size + " " + blocktype; // 준비상태 점수 넓이 높이 블럭개수 다음블럭타입
for(int i = 0; i<size;i++) // 좌표 보내기
outMessage = outMessage + " " +board[i];
out.write(outMessage + "n");
out.flush();
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
}
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
프로젝트 기획서 9/29
String outMessage;
boolean ready; // 게임 준비상태
int score; // 서버의 점수
int size; // 서버의 벽돌개수
int[] pos; // 별돌 좌표
int [] board; // 좌표값
int width; // 테트리스 보드의 넓이
int height; // 테트리스 보드의 높이
int blocktype;
전송할 정보를 저장하는 변수입니다.
int j = 0;
size = 0;
for(int i = 0; i<tetris.width*tetris.height; i++) { // 벽돌 개수 파악
if(tetris.board[i/tetris.width][i%tetris.width]==1)
size++;
}
pos = new int[size]; // 벽돌 개수 만큼의 크기를 갖는 좌표배열 생성
for(int i = 0; i<tetris.width*tetris.height; i++) { // 좌표배열에 벽돌좌표 저장
if(tetris.board[i/tetris.width][i%tetris.width]==1) {
pos[j] = i;
j++;
}
}
상대방에게 자신의 보드에서 블록이 쌓여있는 상태를 저장하는 코드입니다.
pos의 인덱는 몇 번째 블록인지를 나타내고, 이 인덱스에 저장될 값이 블록이 있는 좌표입니다.
for(int i = 0; i<size; i++)
board[i]=pos[i];
ready = tetris.ready;
score = tetris.score;
width = tetris.width;
height = tetris.height;
blocktype = block.type
outMessage = ready + " " + score + " " + width + " " + height + " " +size + " " + blocktype;
for(int i = 0; i<size;i++) // 좌표 보내기
outMessage = outMessage + " " +board[i];
out.write(outMessage + "n");
out.flush();
정보를 변수에 저장한후 outMessage에 넣어 주고 출력버퍼에 outMessage값을 넣어 전송합니다. 전송된 후에
버퍼는 지워집니다.
프로젝트 기획서 10/29
Tetris.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Tetris extends JFrame {
// 자신으 게임 정보
int[][] board; // 게임보드
int[][] block_board; // 블럭보드
int width; // 보드 가로
int height; // 보드 세로
int rotate=0; // 블럭 회전상태
int x; // x좌표
int y; // y좌표
int[][][] block; // 블럭
int[][][] next_block; // 다음 블럭
int blockW; // 현재 블럭 가로
int blockH; // 현재 블럭 세로
int blockR; // 현재 블럭 회전가능 횟수
int blockT; // 블럭 모양
boolean check = true;
int score;
JLabel player_score = new JLabel();
JLabel Score = new JLabel("점수");
JLabel Title = new JLabel("Tetris");
// 상대방 게임 정보
int[][] server_board; // 서버 보드
int[][] server_block_board; // 서버 블럭 보드
int[][][] server_next_block;
int server_width = width; // 서버 보드 넓이
int server_height = height; // 서버 보드 높이
int server_score; // 서버 점수
boolean server_ready = false; // 서버 게임 준비상태
int server_block_num; // 서버 블럭 개수
int[] server_block_pos; // 서버 블럭 좌표
JLabel server_score_label = new JLabel();
JLabel Score2 = new JLabel("점수");
int server_next_blocktype;
public boolean CheckCrush(int x,int y) //충돌처리
{
x = this.x;
y = this.y;
for(int i = 0;i<blockW*blockH;i++) // 이동시키려는 위치가 비어있지 않으면 이동 X
{
if(block[rotate][i/blockW][i%blockW]==1)
if(board[i/blockW+y][i%blockW+x]!=0)
return false;
}
return true;
}
}
테트리스 게임에 필요한 정보들과 충돌이 일어났는지를 판단하는 코드입니다.
프로젝트 기획서 11/29
Block.java
public class Block {
int type = -1;
int block[][][]; // 회전, 가로, 세로
public void SetType()
{
this.type = (int)(Math.random()*7); // 랜덤으로 생성
}
public void MakeBlock() // 블럭생성
{
if(this.type<0)
SetType();
switch(this.type) { // type값에 따라 블럭모양 생성
case 0:
block = new int[][][] {{{1,1},{1,1}}};
break;
case 1:
block = new int[][][] {
{
{0,0,0},
{1,1,1},
{0,1,0}
},
{
{0,1,0},
{1,1,0},
{0,1,0}
},
{
{0,1,0},
{1,1,1},
{0,0,0}
},
{
{0,1,0},
{0,1,1},
{0,1,0}
}
};
break;
case 2:
block = new int[][][] {
{
{0,1,0},
{0,1,0},
{0,1,1}
},
{
{0,0,0},
{1,1,1},
{1,0,0}
},
{
{1,1,0},
{0,1,0},
{0,1,0}
},
{
{0,0,1},
{1,1,1},
{0,0,0}
프로젝트 기획서 12/29
}
};
break;
case 3:
block = new int[][][] {
{
{0,1,1},
{0,1,0},
{0,1,0}
},
{
{0,0,0},
{1,1,1},
{0,0,1}
},
{
{0,1,0},
{0,1,0},
{1,1,0}
},
{
{1,0,0},
{1,1,1},
{0,0,0}
}
};
break;
case 4:
block = new int[][][] {
{
{1,0,0,0},
{1,0,0,0},
{1,0,0,0},
{1,0,0,0}
},
{
{1,1,1,1},
{0,0,0,0},
{0,0,0,0},
{0,0,0,0}
}
};
break;
case 5:
block = new int[][][] {
{
{0,1,0},
{0,1,1},
{0,0,1}
},
{
{0,1,1},
{1,1,0},
{0,0,0}
}
};
break;
case 6:
block = new int[][][] {
{
{0,1,0},
{1,1,0},
{1,0,0}
},
{
{1,1,0},
{0,1,1},
{0,0,0}
프로젝트 기획서 13/29
}
};
break;
}
}
}
블록의 정보를 가지고 있습니다. 랜덤으로 변하는 type값에 따라 블록이 랜덤으로 생성됩니다.
생성된 블록은 int[][][] block에 저장됩니다. 첫 번째에는 회전모양, 두 번째에는 세로, 세 번째에는 가로에
대한 정보를 담습니다.
프로젝트 기획서 14/29
Game.java
public class Game extends Thread{
private Tetris tetris;
private Block block;
public Game(Tetris tetris, Block block) {
this.tetris = tetris;
this.block = block;
}
public void run() {
DrawGame draw = new DrawGame(tetris);
Thread drawing = new Thread(draw);
DownBlock down = new DownBlock(tetris);
Clear clear = new Clear(tetris);
BlockFactory factory = new BlockFactory(tetris, block);
drawing.start();
factory.start();
down.start();
clear.start();
}
}
게임을 실행하는 코드입니다. 송수신과 동시에 실행되며 Game 실행시 게임보드를 그리고, 블록을 생성하고,
블록을 떨어뜨리고, 블록이 가득찬 줄을 삭제하는 기능이 4개의 쓰레드를 이용해 병력적으로 실행됩니다.
프로젝트 기획서 15/29
DrawGame.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DrawGame extends JFrame implements Runnable{
private Tetris tetris;
DrawServerBoard swing_server_board = new DrawServerBoard();
Next_Server_Block server_next_block = new Next_Server_Block();
GameBoard swing_board = new GameBoard();
Next_Block n_block = new Next_Block();
public DrawGame(Tetris tetris) {
this.tetris = tetris;
}
public void Frame() { // 게임화면 출력
int pos = 50;
Board(15,10);
ServerBoard();
setTitle("test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tetris.player_score.setText(Integer.toString(tetris.score));
Container c = getContentPane();
c.addKeyListener(new MyKeyListener());
c.setLayout(null);
tetris.Title.setFont(new Font("Gothic",Font.ITALIC, 30)); // 게임 이름
c.add(tetris.Title);
tetris.Title.setLocation(200,0);
tetris.Title.setSize(100,50);
tetris.Score.setFont(new Font("Gothic",Font.CENTER_BASELINE,20)); // 점수 텍스트
c.add(tetris.Score);
tetris.Score.setLocation(300,280);
tetris.Score.setSize(50,20);
tetris.player_score.setFont(new Font("Gothic",Font.CENTER_BASELINE,20)); // 현재
점수
c.add(tetris.player_score);
tetris.player_score.setLocation(300,300);
tetris.player_score.setSize(50,20);
tetris.Score2.setFont(new Font("Gothic",Font.CENTER_BASELINE,20)); // 점수 텍스트
c.add(tetris.Score2);
tetris.Score2.setLocation(800,280);
tetris.Score2.setSize(50,20);
tetris.server_score_label.setFont(new Font("Gothic",Font.CENTER_BASELINE,20)); //
현재 점수
c.add(tetris.server_score_label);
tetris.server_score_label.setLocation(800,300);
tetris.server_score_label.setSize(50,20);
c.add(n_block); // 테트리스 블럭 보드
n_block.setLocation(pos+(tetris.width-1)*20,pos);
n_block.setSize(120,120);
프로젝트 기획서 16/29
c.add(server_next_block); // 테트리스 블럭 보드
server_next_block.setLocation(500+pos+(tetris.width-1)*20,pos);
server_next_block.setSize(120,120);
c.add(swing_board); // 테트리스 보드
swing_board.setLocation(pos,pos);
swing_board.setSize(200,300);
c.add(swing_server_board);
swing_server_board.setLocation(500+pos,pos);
swing_server_board.setSize(200,300);
setSize(1000,500);
setVisible(true);
c.setFocusable(true);
c.requestFocus();
}
class MyKeyListener extends KeyAdapter { // 키 입력받기0
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == 37 && tetris.check == false && tetris.server_ready) // LEFT
{
for(int i = 0;i<tetris.blockW*tetris.blockH;i++) // 기존에 블럭 지우기
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x]
= 0;
}
tetris.x=tetris.x-1; // 왼쪽으로 좌표이동
if(tetris.CheckCrush(tetris.x,tetris.y)) // 이동한 방향에 충돌 없으면 블럭 그리기
{
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
else // 충돌하면 원래 위치에 그리기
{
tetris.x=tetris.x+1;
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
}
if(keyCode == 39 && tetris.check == false && tetris.server_ready) // RIGHT
{
for(int i = 0;i<tetris.blockW*tetris.blockH;i++) // 기존에 블럭 지우기
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x]
= 0;
}
tetris.x=tetris.x+1; // 오른쪽으로 좌표이동
if(tetris.CheckCrush(tetris.x,tetris.y)) // 이동한 방향에 충돌 없으면 블럭 그리기
{
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
프로젝트 기획서 17/29
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
else // 충동시 원래 위치로 이동
{
tetris.x=tetris.x-1;
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
}
if(keyCode == 40 && tetris.check == false && tetris.server_ready) //DOWN
{
for(int i = 0;i<tetris.blockW*tetris.blockH;i++) // 기존 위치의 블럭 지우기
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x]
= 0;
}
tetris.y=tetris.y+1; // 아래로 한칸이동
if(tetris.CheckCrush(tetris.x,tetris.y)) // 이동한 방향에 충돌 없으면 블럭 그리기
{
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
else // 충동하면 원래위치로 이동
{
tetris.y=tetris.y-1;
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
}
if(keyCode == 38 && tetris.check == false && tetris.server_ready) // UP(회전)
{
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x]
= 0;
}
tetris.rotate++;
tetris.rotate=tetris.rotate%tetris.blockR;
if(tetris.CheckCrush(tetris.x,tetris.y)) // 이동한 방향에 충돌 없으면 블럭 그리기
{
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
프로젝트 기획서 18/29
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
else{
if(tetris.rotate==0)
tetris.rotate=tetris.block.length;
else
tetris.rotate=tetris.rotate-1;
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
}
// SPACE, 벽돌 한 번에 내리기
if(keyCode == 32 && tetris.check == false && tetris.server_ready)
{
for(int i = 0;i<tetris.blockW*tetris.blockH;i++) // 기존 위치의 블럭
지우기
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] = 0;
}
for(;tetris.CheckCrush(tetris.x,tetris.y);tetris.y++);
tetris.y--;
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
repaint(); // 입력받은후 보드 다시그리기
}
}
class GameBoard extends JPanel { // 보드 그리기
public void paint(Graphics g)
{
super.paint(g);
gameboard(g);
}
public void repaint()
{
super.repaint();
}
public void gameboard(Graphics g)
{
int x_pos;
int y_pos;
프로젝트 기획서 19/29
int size=20;
for(int i = 0; i < tetris.height; i++)
{
for(int j = 0; j < tetris.width; j++)
{
x_pos = j*size;
y_pos = i*size;
if(tetris.board[i][j]==2)
{
g.setColor(Color.GRAY);
g.fillRect(x_pos, y_pos, size, size);
}
else if(tetris.board[i][j]==1)
{
g.setColor(Color.BLACK);
g.fillRect(x_pos, y_pos, size, size);
}
else
{
g.setColor(Color.WHITE);
g.fillRect(x_pos, y_pos, size, size);
}
g.setColor(Color.WHITE);
g.drawRect(x_pos, y_pos, size, size);
}
}
}
}
class Next_Block extends JPanel {
public void paint(Graphics g)
{
super.paint(g);
Draw_Block(g);
}
public void repaint()
{
super.repaint();
}
public void Draw_Block(Graphics g)
{
int x_pos;
int y_pos;
int size=20;
for(int i = 0; i < 6; i++)
{
for(int j = 0; j < 6; j++)
{
x_pos = j*size;
y_pos = i*size;
if(tetris.block_board[i][j]==2)
{
g.setColor(Color.GRAY);
g.fillRect(x_pos, y_pos, size, size);
}
else if(tetris.block_board[i][j]==1)
{
g.setColor(Color.BLACK);
g.fillRect(x_pos, y_pos, size, size);
}
프로젝트 기획서 20/29
else
{
g.setColor(Color.WHITE);
g.fillRect(x_pos, y_pos, size, size);
}
g.setColor(Color.WHITE);
g.drawRect(x_pos, y_pos, size, size);
}
}
}
}
class Next_Server_Block extends JPanel {
public void paint(Graphics g)
{
super.paint(g);
Draw_Block(g);
}
public void repaint()
{
super.repaint();
}
public void Draw_Block(Graphics g)
{
int x_pos;
int y_pos;
int size=20;
for(int i = 0; i < 6; i++)
{
for(int j = 0; j < 6; j++)
{
x_pos = j*size;
y_pos = i*size;
if(tetris.server_block_board[i][j]==2)
{
g.setColor(Color.GRAY);
g.fillRect(x_pos, y_pos, size, size);
}
else if(tetris.server_block_board[i][j]==1)
{
g.setColor(Color.BLACK);
g.fillRect(x_pos, y_pos, size, size);
}
else
{
g.setColor(Color.WHITE);
g.fillRect(x_pos, y_pos, size, size);
}
g.setColor(Color.WHITE);
g.drawRect(x_pos, y_pos, size, size);
}
}
}
}
public void Board(int height, int width) { // 보드 초기화0
tetris.width=width; // 보드 가로
tetris.height=height; // 보드 세로
tetris.board = new int[height][width];
프로젝트 기획서 21/29
tetris.score = 0; // 점수 초기화
tetris. player_score.setText(Integer.toString(tetris.score));
for(int i =0;i<height;i++)
{
for(int j = 0;j<width;j++)
{
if(i==height-1 || j==0 || j==width-1) //보드벽
tetris.board[i][j]=2;
else
tetris.board[i][j]=0;
}
}
}
public void ServerBoard() { // 보드 초기화
tetris.server_board = new int[tetris.server_height][tetris.server_width];
tetris.server_score_label.setText(Integer.toString(tetris.server_score));
for(int i =0;i<tetris.server_height;i++)
{
for(int j = 0;j<tetris.server_width;j++)
{
if(i==tetris.server_height-1 || j==0 || j==tetris.server_width-1) //보드벽
tetris.server_board[i][j]=2;
else
tetris.server_board[i][j]=0;
}
}
for(int num = 0; num < tetris.server_block_num; num++)
{
for(int i =0;i<tetris.server_height;i++)
{
for(int j = 0;j<tetris.server_width;j++)
{
if(i*tetris.server_width+j==tetris.server_block_pos[num]) //보드벽
tetris.server_board[i][j] = 1;
}
}
}
}
class DrawServerBoard extends JPanel { // 보드 그리기
public void paint(Graphics g)
{
super.paint(g);
serverboard(g);
}
public void repaint()
{
super.repaint();
}
public void serverboard(Graphics g)
{
int x_pos;
int y_pos;
int size=20;
for(int i = 0; i < tetris.server_height; i++)
{
for(int j = 0; j < tetris.server_width; j++)
{
x_pos = j*size;
y_pos = i*size;
프로젝트 기획서 22/29
if(tetris.server_board[i][j]==2)
{
g.setColor(Color.GRAY);
g.fillRect(x_pos, y_pos, size, size);
}
else if(tetris.server_board[i][j]==1)
{
g.setColor(Color.BLACK);
g.fillRect(x_pos, y_pos, size, size);
}
else
{
g.setColor(Color.WHITE);
g.fillRect(x_pos, y_pos, size, size);
}
g.setColor(Color.WHITE);
g.drawRect(x_pos, y_pos, size, size);
}
}
}
}
public void DrawingServerBoard() // 게임 보드 그리기
{
for(int i = 0;i<tetris.server_height;i++) // 세로
{
for(int j =0;j<tetris.server_width;j++) // 가로
{
if(tetris.server_board[i][j]==0) // 좌표값이 비어있으면
System.out.print("□");
else if(tetris.server_board[i][j]==1)// 블록있으면
System.out.print("■");
else // 벽
System.out.print("▩");
}
System.out.println();
}
}
public void DrawingBoard() // 게임 보드 그리기
{
for(int i = 0;i<tetris.height;i++) // 세로
{
for(int j =0;j<tetris.width;j++) // 가로
{
if(tetris.board[i][j]==0) // 좌표값이 비어있으면
System.out.print("□");
else if(tetris.board[i][j]==1)// 블록있으면
System.out.print("■");
else // 벽
System.out.print("▩");
}
System.out.println();
}
}
public void run() {
Frame();
while(true) {
ServerBoard();
super.repaint();
프로젝트 기획서 23/29
try {
Thread.sleep(500);
} catch(InterruptedException e) {
}
}
}
}
자신의 게임보드와 다음에 나올 블록, 상대방의 게임보드와 상대방의 다음블록을 그려주는 기능입니다.
swing으로 구현했습니다. 자바에서는 C#이나 C++, C와 다르게 콘솔에서 직접 키이벤트를 발생 시켜 주지
못해서 swing을 이용해 키 이벤트를 발생시켰습니다.
프로젝트 기획서 24/29
BlockFactory.java
public class BlockFactory extends Thread{
private Tetris tetris;
private Block block;
public BlockFactory(Tetris tetris, Block block) {
this.tetris = tetris;
this.block = block;
}
public void Block_Board() {
tetris.block_board = new int[6][6];
for(int i =0;i<6;i++)
{
for(int j = 0;j<6;j++)
{
if(i==0 || i==5 || j==0 || j==5) //보드벽
tetris.block_board[i][j]=2;
else
tetris.block_board[i][j]=0;
}
}
}
public void CreateBlock() // 블럭 생성
{
tetris.check=false;
block.MakeBlock();
tetris.block = block.block;// 생성된 블럭을 block에 저장
tetris.blockW = tetris.block[0][0].length; // block의 가로
tetris.blockH = tetris.block[0].length; // block의 세로
tetris.blockR = tetris.block.length; // block의 회전값
tetris.rotate = 0;
tetris.x=tetris.width/2-1; // 생성된 블럭이 나타나는 x좌표
tetris.y=0; // 생성된 블럭이 나타나는 y좌표
}
public void Next_Block()
{
block.SetType(); // 임의의 블럭 생성
block.MakeBlock();
tetris.next_block = block.block; // 생성된 블럭을 block에 저장
int next_width = tetris.next_block[0][0].length;
int next_height = tetris.next_block[0].length;
for(int i = 0; i <next_width*next_height; i++)
{
if(tetris.next_block[0][i/next_width][i%next_width]==1)
tetris.block_board[i/next_width+1][i%next_width+1]
+=tetris.next_block[0][i/next_width][i%next_width];
}
}
public void Server_Next_Block()
{
block.type = tetris.server_next_blocktype;
block.MakeBlock();
tetris.server_next_block = block.block;
int server_next_width = tetris.server_next_block[0][0].length;
int server_next_height = tetris.server_next_block[0].length;
for(int i = 0; i <server_next_width*server_next_height; i++)
프로젝트 기획서 25/29
{
if(tetris.server_next_block[0][i/server_next_width][i%server_next_width]==1)
tetris.server_block_board[i/server_next_width+1][i%server_next_width+1]
+=tetris.server_next_block[0][i/server_next_width][i%server_next_width];
}
}
public void Server_Block_Board() {
tetris.server_block_board = new int[6][6];
for(int i = 0; i < 6; i++)
{
for(int j = 0; j < 6; j++)
{
if(i==0 || i==5 || j==0 || j==5) //보드벽
tetris.server_block_board[i][j]=2;
else
tetris.server_block_board[i][j]=0;
}
}
}
public void Add()
{
for(int i = 0; i < tetris.blockW*tetris.blockH; i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
public void run() {
while(true) {
if(tetris.check) {
CreateBlock();
Server_Block_Board();
if(tetris.server_next_blocktype>0)
Server_Next_Block();
Block_Board();
Next_Block();
if(tetris.CheckCrush(tetris.x,tetris.y) && tetris.server_ready) // 서버의
게임준비 상태에 따라 블럭 생성
{
Add();
}
}
try {
Thread.sleep(500);
} catch(InterruptedException e) {
}
}
}
}
블록을 생성시키는 코드입니다. 테트리스를 처음 시작하면 Tetris에 check변수가 true로 초기화 되어있습니다.
check값이 true일 때, 블록을 생성 가능 하게 만들었다가 블록이 생성되는 순간 false로 값을 바꿔 줍니다.
블록이 충돌하는 순간에 다시 check 값이 true가 됩니다. 여기서 충돌은 벽에 충돌하는 것과 옆에 있는
블록과 충돌하는 경우가 아닌 아랫방향에 있는 블록과 바닥에 충돌할 시 변화됩니다.
블록의 생성된 후 보드에 추가되려면 블록이 생성되는 위치에 다른 충돌물체가 없어야 하고, 게임의 준비
상태가 true일 때 푸가됩니다.
프로젝트 기획서 26/29
DownBlock.java
public class DownBlock extends Thread{
private Tetris tetris;
public DownBlock(Tetris tetris) {
this.tetris = tetris;
}
public void run() {
while(true) {
if(tetris.server_ready) { // 서버의 게임준비 상태에 따라 블럭 멈춤
for(int i = 0;i<tetris.blockW*tetris.blockH;i++) // 기존 위치의 블럭 지우기
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x]
= 0;
}
tetris.y=tetris.y+1; // 아래로 한칸이동
if(tetris.CheckCrush(tetris.x,tetris.y)) // 이동한 방향에 충돌 없으면 블럭 그리기
{
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
}
else // 충동하면 원래위치로 이동
{
tetris.y=tetris.y-1;
for(int i = 0;i<tetris.blockW*tetris.blockH;i++)
{
if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1)
tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] +=
tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW];
}
tetris.check=true;
}
}
tetris.repaint();
try {
Thread.sleep(500);
} catch(InterruptedException e) {
}
}
}
}
블록을 떨어뜨리는 기능입니다. 기존의 블록위치를 지우고 한칸 밑에다가 다시 생성하는 방식으로
기능합니다. 생성될 위치에 충돌체가 있을경우에는 원래 위치로 생성됩니다. 또한 블록을 생성하기 위한
check를 true값으로 바꿔줍니다.
프로젝트 기획서 27/29
Clear.java
public class Clear extends Thread // 한줄 채우면 삭제0
{
private Tetris tetris;
public Clear(Tetris tetris) {
this.tetris = tetris;
}
public void run() {
int count = 0; // 줄이 가득 차있는지 카운트
int clear = 0; // 삭제된 줄이 몇줄인지,
while(true) {
if(tetris.check) {
for(int i = 0; i < tetris.height; i++)
{
for(int j = 1; j < tetris.width-1; j++)
{
if(tetris.board[i][j]==1) //i번째 줄에 블록이 있으면 값증가
count++;
}
if(count==tetris.width-2) // 보드의 가로 길이에서 벽을 뺀것
{
for(int delete = 1;delete<tetris.width-1;delete++)
{
tetris.board[i][delete]=0;
}
clear++;
for(int findH = i; findH>0;findH--) // 삭제된 줄 위의
블럭을 아래로 이동 시킴
{
for(int findW = 1;
findW<tetris.width-1;findW++)
if(tetris.board[findH][findW]==1)
{
tetris.board[findH+clear][findW]=1;
tetris.board[findH][findW]=0;
}
}
}
tetris.score += clear*100;
clear = 0;
count=0; // 다음 카운트를 위해 0으로
}
tetris.player_score.setText(Integer.toString(tetris.score));
}
try {
Thread.sleep(500);
} catch(InterruptedException e) {
}
}
}
}
가로줄이 블록으로 가득차면 그 줄을 지워주고 점수를 올려주는 기능입니다. 줄을 지우고 지워진 줄의 위쪽에
위치한 블록들을 지워진 줄만큼 밑으로 이동시켜 줍니다.
프로젝트 기획서 28/29
구현까지의 과정
1. 계획 → 다인용 테트리스를 만들자.
2. 구현 순서
테트리스 게임 구현 → 채팅 프로그램 구현 → 통신 테트리스 게임 구현
3. 테트리스 게임 구현
콘솔로 보드를 만들고, 블록 생성하고, 떨어지게 만듬. 그러나, 콘솔자체에 키이벤트 발생하지 않음
→ swing으로 구현. 픽셀을 이용해서 그리기 때문에 블록의 유무 판단 불가
→ 콘솔로 구현한후 swing을 이용해 키이벤트를 받고, swing으로 그림출력. 반복문으로 구현
4. 채팅 프로그램 구현
교재(명품 java 프로그래밍)를 이용해 소켓통신에 대해 학습 → 교재에있는 채팅예제 이용해서 채팅
프로그램 구현. 단일 쓰레드이기 때문에 송수신이 직렬적으로 기능함. 쓰레드에 대해 공부.
→ 송수신이 동시에 되는 채팅 프로그램 구현. 서버-클라이언트 통신.
→ 서버가 중개역할을 하고 여러명의 클라이언트끼리 통신하는 채팅 프로그램 구현.
→ 로컬로 테스트 결과 성공. 외부 접속시 실패. 원인 분석
→ 공유기 사용시 같은 공유기로는 접속 가능. 외부에서 접속할 시 공유기까지 접속 후 길을 잃음
→ 포트포워딩을 이용해 해결 가능 하지만, 학교라는 환경에서는 제한됨.
→ 같은 Wifi 사용하여 외부접속 테스트 결과 성공.
5. 테트리스 게임 쓰레드 이용해서 재구현
6. 다인용 통신 테트리스 게임 구현 시도
서버에서 각 클라이언트마다 id 부여, 게임의 정보를 전달 받음. 입ㆍ출력에 관해 공부할 필요성 느 낌. 남
은 시간 부족.
7. 서버-클라이언트 통신 테트리스 게임 구현.
느낀 점
김민태 : 방학 때 Unity를 이용해서 게임을 구현했을 때 간단했다. 또, 1학년 때 C, C++을 이용해서 오목이나 꼬
리잡기 게임 등을 만들어 본 경험도 있었다. 그래서 조금 난이도 있는 게임을 만들어 보자는 욕심에 통신게임을 만
들게 되었다. 시작하고 보니 쉽게 생각했던 게임 구현도 어려웠고, 배우지 않은 소켓통신과 쓰레드도 공부하며 구현
해야 했다. 12월이 시작되었을 때 진행 상태는 반복문으로 구현된 테트리스가 끝이었고 디자인에 집중해서 그럴듯
해 보이는 게임을 제출할까 하는 고민도 했다. 다행히 빨리 종강한 강의 덕분에 공부할 시간이 늘어났고, 어느 순간
푹 빠져서 남은 수업 가는 것도 잊어버리기도 했지만 시험을 위한 공부가 아닌 내 공부를 찾아서 했다는 점이 뿌듯
하고, 이 프로젝트를 하던 시간이 대학교에 와서 가장 의미 있게 보낸 시간이었다.
김수현 : 테트리스 코드에 대해서는 큰 걱정을 하지 않았었다. 그래서 디자인 계획을 잡고, 책과 블로그, 영상들을
보면서 연습을 했었다. 그저 사진만 넣기보다는 포토샵도 추가로 배우고, 음원까지 넣는 방법을 생각해보는 등의 구
성을 했는데 실전에서 코드를 조합해보니 수많은 오류가 발생했고 이를 고치기에는 이미 남은 시간이 촉박했던 터
라 새로운 방법과 디자인을 구현하지 못하고 작품을 제출했다. 내가 했던 노력이 허사가 되었다는 자책감과 아쉬움
이 들기도 한다. 그러나 Java를 이용해서 간단한 게임뿐만 아니라 실생활에 필요한 프로그램을 만들 수 있겠다는
생각이 들었고, 겨울 방학 때 프로그래밍을 좀 더 깊숙하게 다가가겠다는 다짐을 했다. 예를 들어, 자바 프로그램
제작 영상을 보면서 직접 만들어보면 그저 개념으로만 보던 자바가 다르게 다가올 거라는 생각이 든다. 또한 다음
학기나 학교에서 진행하는 각종 프로그래밍 수업을 참여해 프로그래밍 실력을 쌓아야겠다. 프로젝트를 하면서 많은
고민과 내가 의도했던 대로 성공하면서 느끼는 희열과 실패한 쓰라린 경험을 동시에 얻어가는 의미 있는 시간을 보
냈다고 생각한다.
프로젝트 기획서 29/29
손정호 : 프로젝트를 진행하면서 시간의 야속함을 많이 느꼈다. 우리가 원래 계획했던 프로젝트에 조금 미치지 못한
부분이 있었기 때문이다. 많이 아쉬웠다. 다른 조원들의 프로젝트와 우리가 만든 프로젝트를 보면서 우리가 배운 것
을 이정도로 활용 할 수 있는 것에 대해서 많이 놀랍고 뿌듯했다. 우리가 배운 것을 무엇인가 결과물로 남길 수 있
다는 일은 참 뜻 깊은 일인 것 같다. 솔직히 배운 것에 대해서 뭔가 직접 느끼지 못했었는데 이런 프로젝트를 진행
하며 실제로 배운 것을 만들어보니 말이다. 나중에도 이 프로젝트를 진행한 것이 기억에 많이 남을 것 같고 같이
수고해준 우리조원 민태 수현이에게 정말 감사의 인사를 하고 싶다.

More Related Content

What's hot

Blockchain Study(4) - Geth & Smart Contract
Blockchain Study(4) - Geth & Smart ContractBlockchain Study(4) - Geth & Smart Contract
Blockchain Study(4) - Geth & Smart ContractFermat Jade
 
Erc721 token & crypto kitties analysis
Erc721 token & crypto kitties analysisErc721 token & crypto kitties analysis
Erc721 token & crypto kitties analysisSoobok Jin
 
Ethereum Basics Part 2
Ethereum Basics Part 2Ethereum Basics Part 2
Ethereum Basics Part 2Soobok Jin
 
[ETHCon Korea 2019] Park joohyung 박주형
[ETHCon Korea 2019] Park joohyung 박주형[ETHCon Korea 2019] Park joohyung 박주형
[ETHCon Korea 2019] Park joohyung 박주형ethconkr
 
Blockchain Study(3) - 이더리움(Geth)
Blockchain Study(3) - 이더리움(Geth)Blockchain Study(3) - 이더리움(Geth)
Blockchain Study(3) - 이더리움(Geth)Fermat Jade
 
CUBRID(큐브리드) 트랜잭션 처리 원리
CUBRID(큐브리드) 트랜잭션 처리 원리CUBRID(큐브리드) 트랜잭션 처리 원리
CUBRID(큐브리드) 트랜잭션 처리 원리경오 이
 
GCGC- CGCII 서버 엔진에 적용된 기술 (1)
GCGC- CGCII 서버 엔진에 적용된 기술 (1)GCGC- CGCII 서버 엔진에 적용된 기술 (1)
GCGC- CGCII 서버 엔진에 적용된 기술 (1)상현 조
 
Multithread design pattern
Multithread design patternMultithread design pattern
Multithread design pattern종빈 오
 
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with ExceptionGCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception상현 조
 
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server SampleGCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample상현 조
 
Blockchain Study(2) - POW&POS
Blockchain Study(2) - POW&POSBlockchain Study(2) - POW&POS
Blockchain Study(2) - POW&POSFermat Jade
 

What's hot (12)

Blockchain Study(4) - Geth & Smart Contract
Blockchain Study(4) - Geth & Smart ContractBlockchain Study(4) - Geth & Smart Contract
Blockchain Study(4) - Geth & Smart Contract
 
Erc721 token & crypto kitties analysis
Erc721 token & crypto kitties analysisErc721 token & crypto kitties analysis
Erc721 token & crypto kitties analysis
 
Ethereum Basics Part 2
Ethereum Basics Part 2Ethereum Basics Part 2
Ethereum Basics Part 2
 
[ETHCon Korea 2019] Park joohyung 박주형
[ETHCon Korea 2019] Park joohyung 박주형[ETHCon Korea 2019] Park joohyung 박주형
[ETHCon Korea 2019] Park joohyung 박주형
 
Blockchain Study(3) - 이더리움(Geth)
Blockchain Study(3) - 이더리움(Geth)Blockchain Study(3) - 이더리움(Geth)
Blockchain Study(3) - 이더리움(Geth)
 
Blockchain
BlockchainBlockchain
Blockchain
 
CUBRID(큐브리드) 트랜잭션 처리 원리
CUBRID(큐브리드) 트랜잭션 처리 원리CUBRID(큐브리드) 트랜잭션 처리 원리
CUBRID(큐브리드) 트랜잭션 처리 원리
 
GCGC- CGCII 서버 엔진에 적용된 기술 (1)
GCGC- CGCII 서버 엔진에 적용된 기술 (1)GCGC- CGCII 서버 엔진에 적용된 기술 (1)
GCGC- CGCII 서버 엔진에 적용된 기술 (1)
 
Multithread design pattern
Multithread design patternMultithread design pattern
Multithread design pattern
 
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with ExceptionGCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
GCGC- CGCII 서버 엔진에 적용된 기술 (5) - Executor with Exception
 
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server SampleGCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample
GCGC- CGCII 서버 엔진에 적용된 기술 (6) - CGCII Server Sample
 
Blockchain Study(2) - POW&POS
Blockchain Study(2) - POW&POSBlockchain Study(2) - POW&POS
Blockchain Study(2) - POW&POS
 

Similar to Java term project final

파이썬+네트워크 20160210
파이썬+네트워크 20160210파이썬+네트워크 20160210
파이썬+네트워크 20160210Yong Joon Moon
 
Network programming report
Network programming reportNetwork programming report
Network programming reportJongwon
 
Startup JavaScript 9 - Socket.IO 실시간 통신
Startup JavaScript 9 - Socket.IO 실시간 통신Startup JavaScript 9 - Socket.IO 실시간 통신
Startup JavaScript 9 - Socket.IO 실시간 통신Circulus
 
XNA2.0 Network Programming
XNA2.0 Network ProgrammingXNA2.0 Network Programming
XNA2.0 Network ProgrammingSangJin Kang
 
리눅스 소켓 프로그래밍 기초
리눅스 소켓 프로그래밍 기초리눅스 소켓 프로그래밍 기초
리눅스 소켓 프로그래밍 기초Yu Yongwoo
 
유니티 + Nodejs를 활용한 멀티플레이어 게임 개발하기
유니티 + Nodejs를 활용한 멀티플레이어 게임 개발하기유니티 + Nodejs를 활용한 멀티플레이어 게임 개발하기
유니티 + Nodejs를 활용한 멀티플레이어 게임 개발하기Kiyoung Moon
 
G+ Summer C Study 20130717(7일차)
G+ Summer C Study 20130717(7일차)G+ Summer C Study 20130717(7일차)
G+ Summer C Study 20130717(7일차)Jake Yoon
 
코드 생성을 사용해 개발 속도 높이기 NDC2011
코드 생성을 사용해 개발 속도 높이기 NDC2011코드 생성을 사용해 개발 속도 높이기 NDC2011
코드 생성을 사용해 개발 속도 높이기 NDC2011Esun Kim
 
Python socket programming
Python socket programmingPython socket programming
Python socket programmingTae Young Lee
 
[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기Sang Heon Lee
 

Similar to Java term project final (10)

파이썬+네트워크 20160210
파이썬+네트워크 20160210파이썬+네트워크 20160210
파이썬+네트워크 20160210
 
Network programming report
Network programming reportNetwork programming report
Network programming report
 
Startup JavaScript 9 - Socket.IO 실시간 통신
Startup JavaScript 9 - Socket.IO 실시간 통신Startup JavaScript 9 - Socket.IO 실시간 통신
Startup JavaScript 9 - Socket.IO 실시간 통신
 
XNA2.0 Network Programming
XNA2.0 Network ProgrammingXNA2.0 Network Programming
XNA2.0 Network Programming
 
리눅스 소켓 프로그래밍 기초
리눅스 소켓 프로그래밍 기초리눅스 소켓 프로그래밍 기초
리눅스 소켓 프로그래밍 기초
 
유니티 + Nodejs를 활용한 멀티플레이어 게임 개발하기
유니티 + Nodejs를 활용한 멀티플레이어 게임 개발하기유니티 + Nodejs를 활용한 멀티플레이어 게임 개발하기
유니티 + Nodejs를 활용한 멀티플레이어 게임 개발하기
 
G+ Summer C Study 20130717(7일차)
G+ Summer C Study 20130717(7일차)G+ Summer C Study 20130717(7일차)
G+ Summer C Study 20130717(7일차)
 
코드 생성을 사용해 개발 속도 높이기 NDC2011
코드 생성을 사용해 개발 속도 높이기 NDC2011코드 생성을 사용해 개발 속도 높이기 NDC2011
코드 생성을 사용해 개발 속도 높이기 NDC2011
 
Python socket programming
Python socket programmingPython socket programming
Python socket programming
 
[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기
 

Java term project final

  • 1. 프로젝트 기획서 1/29 Java를 이용한 게임 만들기 ※ INDEX 게임 설명 코드설명 Ÿ Server 생성과 Client접속 Ÿ 송수신(Send-Receive 코드) Ÿ 게임의 정보를 담고있는 Tetris.java Ÿ 블록의 정보를 담고있는 Block.java Ÿ 게임을 시작하는 Game.java Ÿ 게임보드를 그리는 DrawGame.java Ÿ 블럭을 생성한는 BlockFactory.java Ÿ 블럭을 떨어뜨리는 BlockDown.java Ÿ 채워진 줄을 삭제하는 Clear.java 구현까지의 과정 느낀점 2018. 11. 26 / 정보통신공학과 김민태, 손정호, 김수현 다인용 테트리스 게임 만들기
  • 2. 프로젝트 기획서 2/29 게임 설명 서버 ­ 클라이언트간의 통신 테트리스 게임을 만들었습니다. 서버생성 후 클라이언트가 접속하면 게임화면이 나오고, 서버에서 start버튼을 통해 게임이 시작됩니다. 왼쪽에는 자신의 게임보드가 나오고, 오른쪽에는 상대방의 게임 보드가 나옵니다. 블록이 위쪽까지 올라와서 더 이상 생성이 불가능해지면 게임이 멈춥니다. 방향키와 스페이스바를 이용해 게임을 진행하며 스페이스 바는 블록을 바로 떨어뜨리는 기능, 방향키의 UP은 블록을 회전시킵니다. LEFT, DOW, RIGHT는 자신들의 방향으로 1칸씩 이동하는 기능을 합니다. 충돌하는 순간 멈추는 것이 아니라 충돌한 직후 0.5초간은 회전이나 좌, 우 방향으로 이동이 가능합니다. 줄을 한칸 지울시 마다 100점씩 점수가 올라갑니다. 코드 설명 서버-클라이언트 모두 송수신과 테트리스 게임에 관한 코드를 가지고 있지만 게임에 관한 코드와 송수신에 관한 코 드가 거의 동일합니다. 통신에 관한 코드는 Server가 가지고 있는 코드이고, 게임에 관한 코드는 Client의 코드입니 다. Server와 Client의 전체 코드와 통신이 아닌 테트리스 게임코드는 Github에 올려놓겠습니다.
  • 3. 프로젝트 기획서 3/29 Server생성-Client 접속 Server.javaimport java.net.*; import java.io.*; public class Server { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(9999); System.out.println("클라이언트를 기다리는중"); Socket socket = server.accept(); System.out.println("연결"); Tetris tetris = new Tetris(); Block block = new Block(); Game game = new Game(tetris, block); Receive rec = new Receive(socket); rec.SetGame(tetris); Send sen = new Send(socket); sen.SetGame(tetris, block); rec.start(); sen.start(); game.start(); } catch (IOException e) { System.out.println(e.getMessage()); } } } Client.java import java.net.*; import java.io.*; public class Client { public static void main(String[] args) { try { Socket client = new Socket("localhost",9999); Tetris tetris = new Tetris(); Block block = new Block(); Game game = new Game(tetris, block); Receive rec = new Receive(client); rec.SetGame(tetris); Send sen = new Send(client); sen.SetGame(tetris,block); rec.start(); sen.start(); game.start(); } catch (IOException e) { System.out.println(e.getMessage()); } } }
  • 4. 프로젝트 기획서 4/29 ServerSocket server = new ServerSocket(9999) 9999포트를 Client가 접속 가능한 포트로 설정 합니다. Socket client = new Socket("localhost",9999) Client는 Server의 ip주소와 Server가 열어둔 포트번호를 Socket에 넣어 Server와 Client를 연결합니다. Socket socket = server.accept(); Client가 접속을 요청하면 Server는 접속을 허락하고 socket에 클라이언트의 정보를 저장합니다. Tetris tetris = new Tetris(); Block block = new Block(); Game game = new Game(tetris, block); Receive rec = new Receive(socket); rec.SetGame(tetris); Send sen = new Send(socket); sen.SetGame(tetris, block); Server와 Client의 통신과 게임을 위해 필요한 클래스를 생성합니다. rec.start(); sen.start(); game.start(); 통신과 게임시작을 동시에 합니다.
  • 5. 프로젝트 기획서 5/29 Receice.javaimport java.io.*; import java.net.*; public class Receive extends Thread{ private Socket server; private Tetris tetris; String[] Code; //클라이언트가 보낸 정보 public Receive(Socket socket) { this.server = socket; } public void SetGame(Tetris tetris) { this.tetris = tetris; } public void run() { try { BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream())); String inputMessage; while(true) { inputMessage = in.readLine(); Code = inputMessage.split(" "); // 클라이언트가 보낸 정보를 공백으로 쪼갬 tetris.client_score = Integer.parseInt(Code[0]); // 정보의 두번째값은 클라이언트의 점수 tetris.client_width = Integer.parseInt(Code[1]); // 정보의 세번째 값은 클라이언트보드의 넓이 tetris.client_height = Integer.parseInt(Code[2]); // 정보의 네번째 값은 클라이언트보드의 높이 tetris.client_block_num = Integer.parseInt(Code[3]); // 클라이언트의 벽돌 개수 tetris.client_next_blocktype = Integer.parseInt(Code[4]); // 클라이언트의 다음 블ㅓㄱ tetris.client_block_pos = new int[tetris.client_block_num]; for(int i = 0; i < tetris.client_block_num; i++) {// 클라이언트의 벽돌 좌표 tetris.client_block_pos[i] = Integer.parseInt(Code[5+i]); } System.out.print("클라이언트 점수: " + tetris.client_score + " 클라이언트 보드넓이 : " + tetris.client_width + " 클라이언트 보드높이 : " +tetris.client_height + " 클라이언트 블럭 개수 : " + tetris.client_block_num + " 클라이언트의 다음 블럭타입: "+ tetris.client_next_blocktype +" 클라이언트 벽돌 좌표 :"); for(int i = 0; i<tetris.client_block_num; i++) System.out.print(" " + tetris.client_block_pos[i]); System.out.println(); } } catch (IOException e) { System.out.println(e.getMessage()); } } }
  • 6. 프로젝트 기획서 6/29 수신기능을 하는 코드입니다. public Receive(Socket socket) { this.server = socket; } public void SetGame(Tetris tetris) { this.tetris = tetris; } 통신에 필요한 ip와 포트정보를 연결하고 게임에 관한 정보를 연결합니다. String[] Code; 받은 정보를 저장하는 변수입니다. while(true) { inputMessage = in.readLine(); Code = inputMessage.split(" "); // 클라이언트가 보낸 정보를 공백으로 쪼갬 tetris.client_score = Integer.parseInt(Code[0]); // 정보의 두번째값은 클라이언트의 점수 tetris.client_width = Integer.parseInt(Code[1]); // 정보의 세번째 값은 클라이언트보드의 넓이 tetris.client_height = Integer.parseInt(Code[2]); // 정보의 네번째 값은 클라이언트보드의 높이 tetris.client_block_num = Integer.parseInt(Code[3]); // 클라이언트의 벽돌 개수 tetris.client_next_blocktype = Integer.parseInt(Code[4]); // 클라이언트의 다음 블럭 tetris.client_block_pos = new int[tetris.client_block_num]; for(int i = 0; i < tetris.client_block_num; i++) {// 클라이언트의 벽돌 좌표 tetris.client_block_pos[i] = Integer.parseInt(Code[5+i]); } 정보를 보낼 때 공백을 이용해 정보를 구분하여 보냈기 때문에 split()함수를 이용해서 이 정보를 구분하여 받 습니다. 서버는 점수와 넓이, 높이, 벽돌개수, 다음 블록타입을 받지만 클라이언트는 여기에 게임의 준비상태를 추가적 으로 받습니다.
  • 7. 프로젝트 기획서 7/29 Send.javaimport java.io.*; import java.net.*; import java.util.*; public class Send extends Thread{ private Socket server; private Tetris tetris; private Block block; public Send(Socket socket) { this.server = socket; } public void SetGame(Tetris tetris, Block block) { this.tetris = tetris; this.block = block; } public void run() { try { BufferedWriter out = new BufferedWriter(new OutputStreamWriter(server.getOutputStream())); String outMessage; boolean ready; // 게임 준비상태 int score; // 서버의 점수 int size; // 서버의 벽돌개수 int[] pos; // 별돌 좌표 int [] board; // 좌표값 int width; // 테트리스 보드의 넓이 int height; // 테트리스 보드의 높이 int blocktype; while(true) { int j = 0; size = 0; for(int i = 0; i<tetris.width*tetris.height; i++) { // 벽돌 개수 파악 if(tetris.board[i/tetris.width][i%tetris.width]==1) size++; } pos = new int[size]; // 벽돌 개수 만큼의 크기를 갖는 좌표배열 생성 for(int i = 0; i<tetris.width*tetris.height; i++) { // 좌표배열에 벽돌좌표 저장 if(tetris.board[i/tetris.width][i%tetris.width]==1) { pos[j] = i; j++; } } board = new int[size]; for(int i = 0; i<size; i++) board[i]=pos[i]; ready = tetris.ready; score = tetris.score; width = tetris.width; height = tetris.height; blocktype = block.type; System.out.println("서버 상태 : "+ready+" 서버 점수: "+score+" 서버 보드 넓이 : " + width + " 서버 보드 높이: " + height + " 서버 블록 개수: "+ size +" 서버 다음 블럭타입: " + blocktype); outMessage = ready + " " + score + " " + width + " " + height + "
  • 8. 프로젝트 기획서 8/29 " +size + " " + blocktype; // 준비상태 점수 넓이 높이 블럭개수 다음블럭타입 for(int i = 0; i<size;i++) // 좌표 보내기 outMessage = outMessage + " " +board[i]; out.write(outMessage + "n"); out.flush(); try { Thread.sleep(1000); } catch(InterruptedException e) { } } } catch (IOException e) { System.out.println(e.getMessage()); } } }
  • 9. 프로젝트 기획서 9/29 String outMessage; boolean ready; // 게임 준비상태 int score; // 서버의 점수 int size; // 서버의 벽돌개수 int[] pos; // 별돌 좌표 int [] board; // 좌표값 int width; // 테트리스 보드의 넓이 int height; // 테트리스 보드의 높이 int blocktype; 전송할 정보를 저장하는 변수입니다. int j = 0; size = 0; for(int i = 0; i<tetris.width*tetris.height; i++) { // 벽돌 개수 파악 if(tetris.board[i/tetris.width][i%tetris.width]==1) size++; } pos = new int[size]; // 벽돌 개수 만큼의 크기를 갖는 좌표배열 생성 for(int i = 0; i<tetris.width*tetris.height; i++) { // 좌표배열에 벽돌좌표 저장 if(tetris.board[i/tetris.width][i%tetris.width]==1) { pos[j] = i; j++; } } 상대방에게 자신의 보드에서 블록이 쌓여있는 상태를 저장하는 코드입니다. pos의 인덱는 몇 번째 블록인지를 나타내고, 이 인덱스에 저장될 값이 블록이 있는 좌표입니다. for(int i = 0; i<size; i++) board[i]=pos[i]; ready = tetris.ready; score = tetris.score; width = tetris.width; height = tetris.height; blocktype = block.type outMessage = ready + " " + score + " " + width + " " + height + " " +size + " " + blocktype; for(int i = 0; i<size;i++) // 좌표 보내기 outMessage = outMessage + " " +board[i]; out.write(outMessage + "n"); out.flush(); 정보를 변수에 저장한후 outMessage에 넣어 주고 출력버퍼에 outMessage값을 넣어 전송합니다. 전송된 후에 버퍼는 지워집니다.
  • 10. 프로젝트 기획서 10/29 Tetris.java import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Tetris extends JFrame { // 자신으 게임 정보 int[][] board; // 게임보드 int[][] block_board; // 블럭보드 int width; // 보드 가로 int height; // 보드 세로 int rotate=0; // 블럭 회전상태 int x; // x좌표 int y; // y좌표 int[][][] block; // 블럭 int[][][] next_block; // 다음 블럭 int blockW; // 현재 블럭 가로 int blockH; // 현재 블럭 세로 int blockR; // 현재 블럭 회전가능 횟수 int blockT; // 블럭 모양 boolean check = true; int score; JLabel player_score = new JLabel(); JLabel Score = new JLabel("점수"); JLabel Title = new JLabel("Tetris"); // 상대방 게임 정보 int[][] server_board; // 서버 보드 int[][] server_block_board; // 서버 블럭 보드 int[][][] server_next_block; int server_width = width; // 서버 보드 넓이 int server_height = height; // 서버 보드 높이 int server_score; // 서버 점수 boolean server_ready = false; // 서버 게임 준비상태 int server_block_num; // 서버 블럭 개수 int[] server_block_pos; // 서버 블럭 좌표 JLabel server_score_label = new JLabel(); JLabel Score2 = new JLabel("점수"); int server_next_blocktype; public boolean CheckCrush(int x,int y) //충돌처리 { x = this.x; y = this.y; for(int i = 0;i<blockW*blockH;i++) // 이동시키려는 위치가 비어있지 않으면 이동 X { if(block[rotate][i/blockW][i%blockW]==1) if(board[i/blockW+y][i%blockW+x]!=0) return false; } return true; } } 테트리스 게임에 필요한 정보들과 충돌이 일어났는지를 판단하는 코드입니다.
  • 11. 프로젝트 기획서 11/29 Block.java public class Block { int type = -1; int block[][][]; // 회전, 가로, 세로 public void SetType() { this.type = (int)(Math.random()*7); // 랜덤으로 생성 } public void MakeBlock() // 블럭생성 { if(this.type<0) SetType(); switch(this.type) { // type값에 따라 블럭모양 생성 case 0: block = new int[][][] {{{1,1},{1,1}}}; break; case 1: block = new int[][][] { { {0,0,0}, {1,1,1}, {0,1,0} }, { {0,1,0}, {1,1,0}, {0,1,0} }, { {0,1,0}, {1,1,1}, {0,0,0} }, { {0,1,0}, {0,1,1}, {0,1,0} } }; break; case 2: block = new int[][][] { { {0,1,0}, {0,1,0}, {0,1,1} }, { {0,0,0}, {1,1,1}, {1,0,0} }, { {1,1,0}, {0,1,0}, {0,1,0} }, { {0,0,1}, {1,1,1}, {0,0,0}
  • 12. 프로젝트 기획서 12/29 } }; break; case 3: block = new int[][][] { { {0,1,1}, {0,1,0}, {0,1,0} }, { {0,0,0}, {1,1,1}, {0,0,1} }, { {0,1,0}, {0,1,0}, {1,1,0} }, { {1,0,0}, {1,1,1}, {0,0,0} } }; break; case 4: block = new int[][][] { { {1,0,0,0}, {1,0,0,0}, {1,0,0,0}, {1,0,0,0} }, { {1,1,1,1}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0} } }; break; case 5: block = new int[][][] { { {0,1,0}, {0,1,1}, {0,0,1} }, { {0,1,1}, {1,1,0}, {0,0,0} } }; break; case 6: block = new int[][][] { { {0,1,0}, {1,1,0}, {1,0,0} }, { {1,1,0}, {0,1,1}, {0,0,0}
  • 13. 프로젝트 기획서 13/29 } }; break; } } } 블록의 정보를 가지고 있습니다. 랜덤으로 변하는 type값에 따라 블록이 랜덤으로 생성됩니다. 생성된 블록은 int[][][] block에 저장됩니다. 첫 번째에는 회전모양, 두 번째에는 세로, 세 번째에는 가로에 대한 정보를 담습니다.
  • 14. 프로젝트 기획서 14/29 Game.java public class Game extends Thread{ private Tetris tetris; private Block block; public Game(Tetris tetris, Block block) { this.tetris = tetris; this.block = block; } public void run() { DrawGame draw = new DrawGame(tetris); Thread drawing = new Thread(draw); DownBlock down = new DownBlock(tetris); Clear clear = new Clear(tetris); BlockFactory factory = new BlockFactory(tetris, block); drawing.start(); factory.start(); down.start(); clear.start(); } } 게임을 실행하는 코드입니다. 송수신과 동시에 실행되며 Game 실행시 게임보드를 그리고, 블록을 생성하고, 블록을 떨어뜨리고, 블록이 가득찬 줄을 삭제하는 기능이 4개의 쓰레드를 이용해 병력적으로 실행됩니다.
  • 15. 프로젝트 기획서 15/29 DrawGame.java import java.awt.*; import java.awt.event.*; import javax.swing.*; public class DrawGame extends JFrame implements Runnable{ private Tetris tetris; DrawServerBoard swing_server_board = new DrawServerBoard(); Next_Server_Block server_next_block = new Next_Server_Block(); GameBoard swing_board = new GameBoard(); Next_Block n_block = new Next_Block(); public DrawGame(Tetris tetris) { this.tetris = tetris; } public void Frame() { // 게임화면 출력 int pos = 50; Board(15,10); ServerBoard(); setTitle("test"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); tetris.player_score.setText(Integer.toString(tetris.score)); Container c = getContentPane(); c.addKeyListener(new MyKeyListener()); c.setLayout(null); tetris.Title.setFont(new Font("Gothic",Font.ITALIC, 30)); // 게임 이름 c.add(tetris.Title); tetris.Title.setLocation(200,0); tetris.Title.setSize(100,50); tetris.Score.setFont(new Font("Gothic",Font.CENTER_BASELINE,20)); // 점수 텍스트 c.add(tetris.Score); tetris.Score.setLocation(300,280); tetris.Score.setSize(50,20); tetris.player_score.setFont(new Font("Gothic",Font.CENTER_BASELINE,20)); // 현재 점수 c.add(tetris.player_score); tetris.player_score.setLocation(300,300); tetris.player_score.setSize(50,20); tetris.Score2.setFont(new Font("Gothic",Font.CENTER_BASELINE,20)); // 점수 텍스트 c.add(tetris.Score2); tetris.Score2.setLocation(800,280); tetris.Score2.setSize(50,20); tetris.server_score_label.setFont(new Font("Gothic",Font.CENTER_BASELINE,20)); // 현재 점수 c.add(tetris.server_score_label); tetris.server_score_label.setLocation(800,300); tetris.server_score_label.setSize(50,20); c.add(n_block); // 테트리스 블럭 보드 n_block.setLocation(pos+(tetris.width-1)*20,pos); n_block.setSize(120,120);
  • 16. 프로젝트 기획서 16/29 c.add(server_next_block); // 테트리스 블럭 보드 server_next_block.setLocation(500+pos+(tetris.width-1)*20,pos); server_next_block.setSize(120,120); c.add(swing_board); // 테트리스 보드 swing_board.setLocation(pos,pos); swing_board.setSize(200,300); c.add(swing_server_board); swing_server_board.setLocation(500+pos,pos); swing_server_board.setSize(200,300); setSize(1000,500); setVisible(true); c.setFocusable(true); c.requestFocus(); } class MyKeyListener extends KeyAdapter { // 키 입력받기0 public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); if(keyCode == 37 && tetris.check == false && tetris.server_ready) // LEFT { for(int i = 0;i<tetris.blockW*tetris.blockH;i++) // 기존에 블럭 지우기 { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] = 0; } tetris.x=tetris.x-1; // 왼쪽으로 좌표이동 if(tetris.CheckCrush(tetris.x,tetris.y)) // 이동한 방향에 충돌 없으면 블럭 그리기 { for(int i = 0;i<tetris.blockW*tetris.blockH;i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } else // 충돌하면 원래 위치에 그리기 { tetris.x=tetris.x+1; for(int i = 0;i<tetris.blockW*tetris.blockH;i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } } if(keyCode == 39 && tetris.check == false && tetris.server_ready) // RIGHT { for(int i = 0;i<tetris.blockW*tetris.blockH;i++) // 기존에 블럭 지우기 { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] = 0; } tetris.x=tetris.x+1; // 오른쪽으로 좌표이동 if(tetris.CheckCrush(tetris.x,tetris.y)) // 이동한 방향에 충돌 없으면 블럭 그리기 { for(int i = 0;i<tetris.blockW*tetris.blockH;i++) {
  • 17. 프로젝트 기획서 17/29 if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } else // 충동시 원래 위치로 이동 { tetris.x=tetris.x-1; for(int i = 0;i<tetris.blockW*tetris.blockH;i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } } if(keyCode == 40 && tetris.check == false && tetris.server_ready) //DOWN { for(int i = 0;i<tetris.blockW*tetris.blockH;i++) // 기존 위치의 블럭 지우기 { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] = 0; } tetris.y=tetris.y+1; // 아래로 한칸이동 if(tetris.CheckCrush(tetris.x,tetris.y)) // 이동한 방향에 충돌 없으면 블럭 그리기 { for(int i = 0;i<tetris.blockW*tetris.blockH;i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } else // 충동하면 원래위치로 이동 { tetris.y=tetris.y-1; for(int i = 0;i<tetris.blockW*tetris.blockH;i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } } if(keyCode == 38 && tetris.check == false && tetris.server_ready) // UP(회전) { for(int i = 0;i<tetris.blockW*tetris.blockH;i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] = 0; } tetris.rotate++; tetris.rotate=tetris.rotate%tetris.blockR; if(tetris.CheckCrush(tetris.x,tetris.y)) // 이동한 방향에 충돌 없으면 블럭 그리기 { for(int i = 0;i<tetris.blockW*tetris.blockH;i++) {
  • 18. 프로젝트 기획서 18/29 if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } else{ if(tetris.rotate==0) tetris.rotate=tetris.block.length; else tetris.rotate=tetris.rotate-1; for(int i = 0;i<tetris.blockW*tetris.blockH;i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } } // SPACE, 벽돌 한 번에 내리기 if(keyCode == 32 && tetris.check == false && tetris.server_ready) { for(int i = 0;i<tetris.blockW*tetris.blockH;i++) // 기존 위치의 블럭 지우기 { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] = 0; } for(;tetris.CheckCrush(tetris.x,tetris.y);tetris.y++); tetris.y--; for(int i = 0;i<tetris.blockW*tetris.blockH;i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } repaint(); // 입력받은후 보드 다시그리기 } } class GameBoard extends JPanel { // 보드 그리기 public void paint(Graphics g) { super.paint(g); gameboard(g); } public void repaint() { super.repaint(); } public void gameboard(Graphics g) { int x_pos; int y_pos;
  • 19. 프로젝트 기획서 19/29 int size=20; for(int i = 0; i < tetris.height; i++) { for(int j = 0; j < tetris.width; j++) { x_pos = j*size; y_pos = i*size; if(tetris.board[i][j]==2) { g.setColor(Color.GRAY); g.fillRect(x_pos, y_pos, size, size); } else if(tetris.board[i][j]==1) { g.setColor(Color.BLACK); g.fillRect(x_pos, y_pos, size, size); } else { g.setColor(Color.WHITE); g.fillRect(x_pos, y_pos, size, size); } g.setColor(Color.WHITE); g.drawRect(x_pos, y_pos, size, size); } } } } class Next_Block extends JPanel { public void paint(Graphics g) { super.paint(g); Draw_Block(g); } public void repaint() { super.repaint(); } public void Draw_Block(Graphics g) { int x_pos; int y_pos; int size=20; for(int i = 0; i < 6; i++) { for(int j = 0; j < 6; j++) { x_pos = j*size; y_pos = i*size; if(tetris.block_board[i][j]==2) { g.setColor(Color.GRAY); g.fillRect(x_pos, y_pos, size, size); } else if(tetris.block_board[i][j]==1) { g.setColor(Color.BLACK); g.fillRect(x_pos, y_pos, size, size); }
  • 20. 프로젝트 기획서 20/29 else { g.setColor(Color.WHITE); g.fillRect(x_pos, y_pos, size, size); } g.setColor(Color.WHITE); g.drawRect(x_pos, y_pos, size, size); } } } } class Next_Server_Block extends JPanel { public void paint(Graphics g) { super.paint(g); Draw_Block(g); } public void repaint() { super.repaint(); } public void Draw_Block(Graphics g) { int x_pos; int y_pos; int size=20; for(int i = 0; i < 6; i++) { for(int j = 0; j < 6; j++) { x_pos = j*size; y_pos = i*size; if(tetris.server_block_board[i][j]==2) { g.setColor(Color.GRAY); g.fillRect(x_pos, y_pos, size, size); } else if(tetris.server_block_board[i][j]==1) { g.setColor(Color.BLACK); g.fillRect(x_pos, y_pos, size, size); } else { g.setColor(Color.WHITE); g.fillRect(x_pos, y_pos, size, size); } g.setColor(Color.WHITE); g.drawRect(x_pos, y_pos, size, size); } } } } public void Board(int height, int width) { // 보드 초기화0 tetris.width=width; // 보드 가로 tetris.height=height; // 보드 세로 tetris.board = new int[height][width];
  • 21. 프로젝트 기획서 21/29 tetris.score = 0; // 점수 초기화 tetris. player_score.setText(Integer.toString(tetris.score)); for(int i =0;i<height;i++) { for(int j = 0;j<width;j++) { if(i==height-1 || j==0 || j==width-1) //보드벽 tetris.board[i][j]=2; else tetris.board[i][j]=0; } } } public void ServerBoard() { // 보드 초기화 tetris.server_board = new int[tetris.server_height][tetris.server_width]; tetris.server_score_label.setText(Integer.toString(tetris.server_score)); for(int i =0;i<tetris.server_height;i++) { for(int j = 0;j<tetris.server_width;j++) { if(i==tetris.server_height-1 || j==0 || j==tetris.server_width-1) //보드벽 tetris.server_board[i][j]=2; else tetris.server_board[i][j]=0; } } for(int num = 0; num < tetris.server_block_num; num++) { for(int i =0;i<tetris.server_height;i++) { for(int j = 0;j<tetris.server_width;j++) { if(i*tetris.server_width+j==tetris.server_block_pos[num]) //보드벽 tetris.server_board[i][j] = 1; } } } } class DrawServerBoard extends JPanel { // 보드 그리기 public void paint(Graphics g) { super.paint(g); serverboard(g); } public void repaint() { super.repaint(); } public void serverboard(Graphics g) { int x_pos; int y_pos; int size=20; for(int i = 0; i < tetris.server_height; i++) { for(int j = 0; j < tetris.server_width; j++) { x_pos = j*size; y_pos = i*size;
  • 22. 프로젝트 기획서 22/29 if(tetris.server_board[i][j]==2) { g.setColor(Color.GRAY); g.fillRect(x_pos, y_pos, size, size); } else if(tetris.server_board[i][j]==1) { g.setColor(Color.BLACK); g.fillRect(x_pos, y_pos, size, size); } else { g.setColor(Color.WHITE); g.fillRect(x_pos, y_pos, size, size); } g.setColor(Color.WHITE); g.drawRect(x_pos, y_pos, size, size); } } } } public void DrawingServerBoard() // 게임 보드 그리기 { for(int i = 0;i<tetris.server_height;i++) // 세로 { for(int j =0;j<tetris.server_width;j++) // 가로 { if(tetris.server_board[i][j]==0) // 좌표값이 비어있으면 System.out.print("□"); else if(tetris.server_board[i][j]==1)// 블록있으면 System.out.print("■"); else // 벽 System.out.print("▩"); } System.out.println(); } } public void DrawingBoard() // 게임 보드 그리기 { for(int i = 0;i<tetris.height;i++) // 세로 { for(int j =0;j<tetris.width;j++) // 가로 { if(tetris.board[i][j]==0) // 좌표값이 비어있으면 System.out.print("□"); else if(tetris.board[i][j]==1)// 블록있으면 System.out.print("■"); else // 벽 System.out.print("▩"); } System.out.println(); } } public void run() { Frame(); while(true) { ServerBoard(); super.repaint();
  • 23. 프로젝트 기획서 23/29 try { Thread.sleep(500); } catch(InterruptedException e) { } } } } 자신의 게임보드와 다음에 나올 블록, 상대방의 게임보드와 상대방의 다음블록을 그려주는 기능입니다. swing으로 구현했습니다. 자바에서는 C#이나 C++, C와 다르게 콘솔에서 직접 키이벤트를 발생 시켜 주지 못해서 swing을 이용해 키 이벤트를 발생시켰습니다.
  • 24. 프로젝트 기획서 24/29 BlockFactory.java public class BlockFactory extends Thread{ private Tetris tetris; private Block block; public BlockFactory(Tetris tetris, Block block) { this.tetris = tetris; this.block = block; } public void Block_Board() { tetris.block_board = new int[6][6]; for(int i =0;i<6;i++) { for(int j = 0;j<6;j++) { if(i==0 || i==5 || j==0 || j==5) //보드벽 tetris.block_board[i][j]=2; else tetris.block_board[i][j]=0; } } } public void CreateBlock() // 블럭 생성 { tetris.check=false; block.MakeBlock(); tetris.block = block.block;// 생성된 블럭을 block에 저장 tetris.blockW = tetris.block[0][0].length; // block의 가로 tetris.blockH = tetris.block[0].length; // block의 세로 tetris.blockR = tetris.block.length; // block의 회전값 tetris.rotate = 0; tetris.x=tetris.width/2-1; // 생성된 블럭이 나타나는 x좌표 tetris.y=0; // 생성된 블럭이 나타나는 y좌표 } public void Next_Block() { block.SetType(); // 임의의 블럭 생성 block.MakeBlock(); tetris.next_block = block.block; // 생성된 블럭을 block에 저장 int next_width = tetris.next_block[0][0].length; int next_height = tetris.next_block[0].length; for(int i = 0; i <next_width*next_height; i++) { if(tetris.next_block[0][i/next_width][i%next_width]==1) tetris.block_board[i/next_width+1][i%next_width+1] +=tetris.next_block[0][i/next_width][i%next_width]; } } public void Server_Next_Block() { block.type = tetris.server_next_blocktype; block.MakeBlock(); tetris.server_next_block = block.block; int server_next_width = tetris.server_next_block[0][0].length; int server_next_height = tetris.server_next_block[0].length; for(int i = 0; i <server_next_width*server_next_height; i++)
  • 25. 프로젝트 기획서 25/29 { if(tetris.server_next_block[0][i/server_next_width][i%server_next_width]==1) tetris.server_block_board[i/server_next_width+1][i%server_next_width+1] +=tetris.server_next_block[0][i/server_next_width][i%server_next_width]; } } public void Server_Block_Board() { tetris.server_block_board = new int[6][6]; for(int i = 0; i < 6; i++) { for(int j = 0; j < 6; j++) { if(i==0 || i==5 || j==0 || j==5) //보드벽 tetris.server_block_board[i][j]=2; else tetris.server_block_board[i][j]=0; } } } public void Add() { for(int i = 0; i < tetris.blockW*tetris.blockH; i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } public void run() { while(true) { if(tetris.check) { CreateBlock(); Server_Block_Board(); if(tetris.server_next_blocktype>0) Server_Next_Block(); Block_Board(); Next_Block(); if(tetris.CheckCrush(tetris.x,tetris.y) && tetris.server_ready) // 서버의 게임준비 상태에 따라 블럭 생성 { Add(); } } try { Thread.sleep(500); } catch(InterruptedException e) { } } } } 블록을 생성시키는 코드입니다. 테트리스를 처음 시작하면 Tetris에 check변수가 true로 초기화 되어있습니다. check값이 true일 때, 블록을 생성 가능 하게 만들었다가 블록이 생성되는 순간 false로 값을 바꿔 줍니다. 블록이 충돌하는 순간에 다시 check 값이 true가 됩니다. 여기서 충돌은 벽에 충돌하는 것과 옆에 있는 블록과 충돌하는 경우가 아닌 아랫방향에 있는 블록과 바닥에 충돌할 시 변화됩니다. 블록의 생성된 후 보드에 추가되려면 블록이 생성되는 위치에 다른 충돌물체가 없어야 하고, 게임의 준비 상태가 true일 때 푸가됩니다.
  • 26. 프로젝트 기획서 26/29 DownBlock.java public class DownBlock extends Thread{ private Tetris tetris; public DownBlock(Tetris tetris) { this.tetris = tetris; } public void run() { while(true) { if(tetris.server_ready) { // 서버의 게임준비 상태에 따라 블럭 멈춤 for(int i = 0;i<tetris.blockW*tetris.blockH;i++) // 기존 위치의 블럭 지우기 { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] = 0; } tetris.y=tetris.y+1; // 아래로 한칸이동 if(tetris.CheckCrush(tetris.x,tetris.y)) // 이동한 방향에 충돌 없으면 블럭 그리기 { for(int i = 0;i<tetris.blockW*tetris.blockH;i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } } else // 충동하면 원래위치로 이동 { tetris.y=tetris.y-1; for(int i = 0;i<tetris.blockW*tetris.blockH;i++) { if(tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]==1) tetris.board[i/tetris.blockW+tetris.y][i%tetris.blockW+tetris.x] += tetris.block[tetris.rotate][i/tetris.blockW][i%tetris.blockW]; } tetris.check=true; } } tetris.repaint(); try { Thread.sleep(500); } catch(InterruptedException e) { } } } } 블록을 떨어뜨리는 기능입니다. 기존의 블록위치를 지우고 한칸 밑에다가 다시 생성하는 방식으로 기능합니다. 생성될 위치에 충돌체가 있을경우에는 원래 위치로 생성됩니다. 또한 블록을 생성하기 위한 check를 true값으로 바꿔줍니다.
  • 27. 프로젝트 기획서 27/29 Clear.java public class Clear extends Thread // 한줄 채우면 삭제0 { private Tetris tetris; public Clear(Tetris tetris) { this.tetris = tetris; } public void run() { int count = 0; // 줄이 가득 차있는지 카운트 int clear = 0; // 삭제된 줄이 몇줄인지, while(true) { if(tetris.check) { for(int i = 0; i < tetris.height; i++) { for(int j = 1; j < tetris.width-1; j++) { if(tetris.board[i][j]==1) //i번째 줄에 블록이 있으면 값증가 count++; } if(count==tetris.width-2) // 보드의 가로 길이에서 벽을 뺀것 { for(int delete = 1;delete<tetris.width-1;delete++) { tetris.board[i][delete]=0; } clear++; for(int findH = i; findH>0;findH--) // 삭제된 줄 위의 블럭을 아래로 이동 시킴 { for(int findW = 1; findW<tetris.width-1;findW++) if(tetris.board[findH][findW]==1) { tetris.board[findH+clear][findW]=1; tetris.board[findH][findW]=0; } } } tetris.score += clear*100; clear = 0; count=0; // 다음 카운트를 위해 0으로 } tetris.player_score.setText(Integer.toString(tetris.score)); } try { Thread.sleep(500); } catch(InterruptedException e) { } } } } 가로줄이 블록으로 가득차면 그 줄을 지워주고 점수를 올려주는 기능입니다. 줄을 지우고 지워진 줄의 위쪽에 위치한 블록들을 지워진 줄만큼 밑으로 이동시켜 줍니다.
  • 28. 프로젝트 기획서 28/29 구현까지의 과정 1. 계획 → 다인용 테트리스를 만들자. 2. 구현 순서 테트리스 게임 구현 → 채팅 프로그램 구현 → 통신 테트리스 게임 구현 3. 테트리스 게임 구현 콘솔로 보드를 만들고, 블록 생성하고, 떨어지게 만듬. 그러나, 콘솔자체에 키이벤트 발생하지 않음 → swing으로 구현. 픽셀을 이용해서 그리기 때문에 블록의 유무 판단 불가 → 콘솔로 구현한후 swing을 이용해 키이벤트를 받고, swing으로 그림출력. 반복문으로 구현 4. 채팅 프로그램 구현 교재(명품 java 프로그래밍)를 이용해 소켓통신에 대해 학습 → 교재에있는 채팅예제 이용해서 채팅 프로그램 구현. 단일 쓰레드이기 때문에 송수신이 직렬적으로 기능함. 쓰레드에 대해 공부. → 송수신이 동시에 되는 채팅 프로그램 구현. 서버-클라이언트 통신. → 서버가 중개역할을 하고 여러명의 클라이언트끼리 통신하는 채팅 프로그램 구현. → 로컬로 테스트 결과 성공. 외부 접속시 실패. 원인 분석 → 공유기 사용시 같은 공유기로는 접속 가능. 외부에서 접속할 시 공유기까지 접속 후 길을 잃음 → 포트포워딩을 이용해 해결 가능 하지만, 학교라는 환경에서는 제한됨. → 같은 Wifi 사용하여 외부접속 테스트 결과 성공. 5. 테트리스 게임 쓰레드 이용해서 재구현 6. 다인용 통신 테트리스 게임 구현 시도 서버에서 각 클라이언트마다 id 부여, 게임의 정보를 전달 받음. 입ㆍ출력에 관해 공부할 필요성 느 낌. 남 은 시간 부족. 7. 서버-클라이언트 통신 테트리스 게임 구현. 느낀 점 김민태 : 방학 때 Unity를 이용해서 게임을 구현했을 때 간단했다. 또, 1학년 때 C, C++을 이용해서 오목이나 꼬 리잡기 게임 등을 만들어 본 경험도 있었다. 그래서 조금 난이도 있는 게임을 만들어 보자는 욕심에 통신게임을 만 들게 되었다. 시작하고 보니 쉽게 생각했던 게임 구현도 어려웠고, 배우지 않은 소켓통신과 쓰레드도 공부하며 구현 해야 했다. 12월이 시작되었을 때 진행 상태는 반복문으로 구현된 테트리스가 끝이었고 디자인에 집중해서 그럴듯 해 보이는 게임을 제출할까 하는 고민도 했다. 다행히 빨리 종강한 강의 덕분에 공부할 시간이 늘어났고, 어느 순간 푹 빠져서 남은 수업 가는 것도 잊어버리기도 했지만 시험을 위한 공부가 아닌 내 공부를 찾아서 했다는 점이 뿌듯 하고, 이 프로젝트를 하던 시간이 대학교에 와서 가장 의미 있게 보낸 시간이었다. 김수현 : 테트리스 코드에 대해서는 큰 걱정을 하지 않았었다. 그래서 디자인 계획을 잡고, 책과 블로그, 영상들을 보면서 연습을 했었다. 그저 사진만 넣기보다는 포토샵도 추가로 배우고, 음원까지 넣는 방법을 생각해보는 등의 구 성을 했는데 실전에서 코드를 조합해보니 수많은 오류가 발생했고 이를 고치기에는 이미 남은 시간이 촉박했던 터 라 새로운 방법과 디자인을 구현하지 못하고 작품을 제출했다. 내가 했던 노력이 허사가 되었다는 자책감과 아쉬움 이 들기도 한다. 그러나 Java를 이용해서 간단한 게임뿐만 아니라 실생활에 필요한 프로그램을 만들 수 있겠다는 생각이 들었고, 겨울 방학 때 프로그래밍을 좀 더 깊숙하게 다가가겠다는 다짐을 했다. 예를 들어, 자바 프로그램 제작 영상을 보면서 직접 만들어보면 그저 개념으로만 보던 자바가 다르게 다가올 거라는 생각이 든다. 또한 다음 학기나 학교에서 진행하는 각종 프로그래밍 수업을 참여해 프로그래밍 실력을 쌓아야겠다. 프로젝트를 하면서 많은 고민과 내가 의도했던 대로 성공하면서 느끼는 희열과 실패한 쓰라린 경험을 동시에 얻어가는 의미 있는 시간을 보 냈다고 생각한다.
  • 29. 프로젝트 기획서 29/29 손정호 : 프로젝트를 진행하면서 시간의 야속함을 많이 느꼈다. 우리가 원래 계획했던 프로젝트에 조금 미치지 못한 부분이 있었기 때문이다. 많이 아쉬웠다. 다른 조원들의 프로젝트와 우리가 만든 프로젝트를 보면서 우리가 배운 것 을 이정도로 활용 할 수 있는 것에 대해서 많이 놀랍고 뿌듯했다. 우리가 배운 것을 무엇인가 결과물로 남길 수 있 다는 일은 참 뜻 깊은 일인 것 같다. 솔직히 배운 것에 대해서 뭔가 직접 느끼지 못했었는데 이런 프로젝트를 진행 하며 실제로 배운 것을 만들어보니 말이다. 나중에도 이 프로젝트를 진행한 것이 기억에 많이 남을 것 같고 같이 수고해준 우리조원 민태 수현이에게 정말 감사의 인사를 하고 싶다.