이번 팀플은 포켓몬스터 게임을 만드는 것이었다.
▼요구사항
class 포켓몬
String 타입 // 모든 포켓몬은 타입이 반드시 존재한다. ex) 전기, 물, ...
String 이름 // 모든 포켓몬은 이름이 반드시 존재한다. ex) 피카츄, 꼬부기, ...
int 레벨 // 새로 잡은 포켓몬의 레벨은 1~5 랜덤한 값을 가진다.
int 경험치 // 새로 잡은 포켓몬의 경험치는 0이다.
void attack() // 50%의 확률로 성공 -> 성공시 50~500 사이의 경험치 획득
void hello() // 피카츄는 삐까삐까, 꼬부기는 꼬북꼬북 등의 울음소리를 출력한다.
void levelUp() // 경험치가 100을 채울때마다 레벨은 +1
class 피카츄
attack() // 전기타입 -> 전기충격
hello() // 삐까삐까
class 꼬부기
attack() // 물타입 -> 물대포
hello() // 꼬북꼬북
syso(포켓몬객체); 수행시 [피카츄 전기타입 Lv5 exp10]으로 출력
1. 게임하기 2. 전체상태출력 3. 울음소리듣기 4. 포켓몬잡기 0. 게임종료
# 피카츄,꼬부기 대신 다른 포켓몬들로 구현할 것!
# 요구사항을 벗어나지않는 조건에서 기능 및 설정 추가 가능!
우리 조는 총 4명으로 포켓몬을 하나씩 선택해 총 4마리의 포켓몬을 설정했다.
처음 변수명과 함수명을 통일하여 사용하기 위해 이름을 통일시키고, 함수의 경우 input과 output 부분도 정리하였다.
그다음 한글코딩 및 대략적인 코딩을 진행하고 파트 분배를 진행하였다.
내가 담당한 부분은 클래스 쪽에서는 hello(), attackVoice() 함수, 자식클래스 /
그리고 main 안쪽에서는 메뉴 3번_울음소리듣기 파트를 담당하게 되었다.
처음 작성한 내용
<클래스 부분>
package pokemongame;
import java.util.Random;
import java.util.Scanner;
class Pokemon{ // 부모 클래스
// 멤버 변수
String type; // 타입 (강제)
String name; // 이름 (강제)
int level; // 레벨
int exp; // 경험치
// 클래스 변수
static Random rand = new Random();
static final int maxLevel =30; // 최대레벨
// 생성자
Pokemon(String type,String name) { //초기화
this.type = type;
this.name = name;
this.level = rand.nextInt(5)+1; // 1에서 5까지 레벨
this.exp = 0;
this.attackExp = 0;
}
void hello(){ // hello 메서드, 울음소리 출력
System.out.println("울음소리 출력");
}
void attackVoice(){ // 공격할 때 기술명
System.out.print("attackVoice를 출력");
}
} // 포켓몬 클래스 닫기
class Ditto extends Pokemon{ // 자식클래스, 메타몽
// 생성자, 타입/이름 고정
Ditto(){
super("노말", "메타몽"); // 부모생성자 호출
}
// 함수
@Override
void hello() { // 울음소리 출력, 오버라이딩
System.out.println("어라?😭");
}
@Override
void attackVoice() { // 공격시 출력되는 attackVoice, 오버라이딩
System.out.print("변신!! ");
}
}
class Zorua extends Pokemon{ // 자식클래스, 조로아
// 생성자, 타입/이름 고정
Zorua(){
super("악", "조로아"); // 부모생성자 호출
}
// 함수
@Override
void hello() { // 울음소리 출력, 오버라이딩
System.out.println("조르륵😭");
}
@Override
void attackVoice() { // 공격시 출력되는 attackVoice, 오버라이딩
System.out.print("물기!! ");
}
}
class Cubone extends Pokemon{ // 자식클래스, 탕구리
// 생성자, 타입/이름 고정
Cubone(){
super("땅", "탕구리"); // 부모생성자 호출
}
// 함수
@Override
void hello() { // 울음소리 출력, 오버라이딩
System.out.println("구리😭");
}
@Override
void attackVoice() { // 공격시 출력되는 attackVoice, 오버라이딩
System.out.print("탕!탕! ");
}
}
class Oddish extends Pokemon{ // 자식클래스, 뚜벅쵸
// 생성자, 타입/이름 고정
Oddish(){
super("풀", "뚜벅쵸"); // 부모생성자 호출
}
// 함수
@Override
void hello() { // 울음소리 출력, 오버라이딩
System.out.println("뚜벅😭");
}
@Override
void attackVoice() { // 공격시 출력되는 attackVoice, 오버라이딩
System.out.print("잎날가르기!! ");
}
}
Pokemon 클래스 안쪽에서의 void hello()와 void attackVoice() 의 경우
자식클래스에서 오버라이딩하여 수정 사용하기 때문에, 부모 클래스인 Pokemon 클래스에서는 대략적으로 내용을 알 수 있게 println을 사용하여 작성하였다.
각 포켓몬마다 클래스를 생성하여 상속받아 사용할 수 있게 extends Pokemon 을 붙여주었고,
자식클래스의 생성자 경우 super를 사용해서 부모클래스에서 호출하도록 했다.
hello()와 attackVoice()는 위에 얘기한대로 오버라이딩하여 각 포켓몬 클래스(자식클래스)마다 울음소리와 attack 실행 시 발생되는 공격멘트를 작성해 주었다.
public class Pokemongame{
public static void menu(){ // 메뉴판 출력
System.out.println("====== 메 뉴 ======");
System.out.println("1. 게임하기");
System.out.println("2. 전체 상태 출력");
System.out.println("3. 울음소리 듣기");
System.out.println("4. 포켓몬 잡기");
System.out.println("0. 게임종료");
System.out.println("==================");
}
public static int printMenu() { // 메뉴판 출력, 값받는 메서드
System.out.println("(∩^o^)⊃━☆ 포켓몬스터 게임을 시작합니다.");
//메뉴판 출력
menu();
Scanner sc = new Scanner(System.in);
// 유효성 검사 추가
// 메뉴 입력
System.out.print("메뉴를 선택해주세요. >> ");
int menu = sc.nextInt();
return menu;
}
// 샘플 데이터
/*
public static void sampleData(){
Pokemon[] datas=new Pokemon[3];
datas[0] = new Ditto(); // 메타몽 생성
}
*/
public static boolean isNull(int cnt){
if(cnt<=0) { // 아직 저장된 포켓몬 데이터가 전혀 없을때
System.out.println("저장된 포켓몬이 없습니다!");
return true;
}
return false;
}
public static boolean isFull(int cnt,Pokemon[] datas) {
if(cnt>=datas.length) { // 더 이상 포켓몬 데이터를 저장할 수 없을때
System.out.println("저장가능한 공간이 없습니다!");
return true;
}
return false;
}
public static int pokemonMenu(int cnt, Pokemon[] datas) {
Scanner sc = new Scanner(System.in);
while(true) {
System.out.println("===== 포켓몬 목록 =====");
for(int i=0; i<cnt; i++) {
System.out.println((i+1) +". " + datas[i].name);
}
System.out.println("======================");
System.out.print("포켓몬을 선택해주세요! >>");
int num = sc.nextInt();
return num;
}
}
public static void main(String[] args) {
Pokemon[] datas=new Pokemon[3]; // 포켓몬도감 객체 생성?
// 샘플데이터
// sampleData();
datas[0] = new Ditto();
int cnt = 1;
Scanner sc=new Scanner(System.in);
while(true){
int menu=printMenu();
if(menu==0) { // 0. 게임종료
break;
}
else if(menu==3) { // 3. 울음소리 듣기
// 보유한 포켓몬이 있는지 유효성 검사(isNull)
if(isNull(cnt)) {
continue;
}
//포켓몬 선택
int num;
while(true) {
num=pokemonMenu(cnt, datas); // 포켓몬 선택 메뉴 출력 및 번호 받아오기
// 선택한 번호 유효성 검사
if(num<=0 || num>cnt) { //선택한 번호가 0보다 작거나 같거나, cnt보다 크다면
System.out.println("잘못된 번호를 선택하셨습니다. 다시 선택해주세요");
continue;
}
// 울음소리 출력
System.out.print(datas[num-1].name + " : ");
datas[num-1].hello();
break;
}
}
}
}
}
콘솔창에서 실행해서 확인해 보기 위해서 임의로 포켓몬메뉴 등 작성하였고, 그중 selectOne 함수에 대한 부분을 누락하게 되었다.
내가 작성한 부분만 놓고 보자면 메뉴 3번을 실행했을 때 먼저 isNull 함수를 실행하여 배열 속에 저장된 포켓몬이 있는지 유효성 검사를 먼저 실행하게 했고,
그다음 while문을 사용하여 안쪽에
1. num에 pokemonMenu(내가 보유한 포켓몬 목록을 보여주고, 번호를 선택하는 함수) 에서 받아온 return 값을 저장
2. 유효성 검사 : 선택한 번호가 0보다 작거나 같거나, cnt값(포켓몬 추가 시 ++) 보다 선택한 번호가 크다면 잘못 선택했다는 안내문구 출력
3. 맞는 번호를 선택했다면 datas[num-1]에 있는 이름 : 울음소리를 실행한다.
: 배열의 경우 0번부터 실행하고, 포켓몬 메뉴의 경우 1번부터 보여주기 때문에 실제 datas[0]은 1번과 같다.
--> 다른 조원분들과 코드를 합쳤을 때 오류가 발생되었다.[ NullPointerException ] 오류였는데, 실제 값이 아닌 null을 가지고 있는 객체/변수를 호출할 때 발생하는 예외라고 한다.
내가 작성한 메뉴 3번 안쪽에서 출력 시에 num-1 로 출력을 하고, pokemonMenu에서도 리턴값으로 num-1 을 받아와서 -1이 2회 발생되어 발생한 오류였다. 따라서 3번 안쪽에서의 num-1을 num 으로 코드 수정을 하니 정상적으로 가동되었다.
▼팀원들과 모두 합치고 수정한 최종본 코드 내역
package pokemongame;
import java.util.Random;
import java.util.Scanner;
class Pokemon{
/*멤버변수선언 */
String type; // 타입 (강제)
String name; // 이름 (강제)
int level; // 레벨
int exp; // 경험치
/*클래스변수 선언*/
static final int attackExpMin = 50; // 공격 성공 시 추가되는 최소 경험치
static final int attackExpMax = 500; // 공격 성공 시 추가되는 최대 경험치
static Random rand = new Random();
static final int maxLevel =30;
// 생성자
Pokemon(String type,String name) { //초기화
this.type = type;
this.name = name;
this.level = rand.nextInt(5)+1; // 1에서 5까지 레벨
this.exp = 0;
}
// 함수
boolean attack(){ // attack 메서드
// 공격 50% 확률로 성공
attackVoice(); // 기술명 외침 ex ) 전기공격!
hello(); // 울음소리 ex) 삐까삐까!
if(rand.nextDouble() < 0.5 ){// 공격에 성공했을 경우 - 50%확률
int attackExp = rand.nextInt(attackExpMax - attackExpMin + 1) + attackExpMin;
// attackExpMin에서 attackExpMax 사이의 랜덤한 경험치 획득
this.exp += attackExp; // 현재 경험치에 방금 획득한 경험치를 증가시킴
if(this.level >= maxLevel) { // 이미 만렙인 경우
System.out.println(this.name+"은(는) 만렙을 달성해서 더이상 경험치를 획득할 수 없습니다!");
this.exp = 0; // 만렙은 exp = 0 으로 고정함
return true; // 만렙을 달성했기 때문에 true 반환
}
System.out.println(this.name + " 공격 성공! " + attackExp + " 의 경험치 획득 !");
while (this.exp >= 100) { // 경험치가 100 이상이 되면
levelUp();// 레벨업 메서드 실행
}
return true;// 레벨업이 없는경우
}
else{ // 공격에 실패했을 경우
System.out.println("공격에 실패했습니다.");
}
return false;
}
void hello(){ // hello 메서드, 울음소리
System.out.println(this.name + "의 울음소리 출력");
}
boolean levelUp(){ // levelUp 메서드
int additionalLevels = this.exp / 100; // 현재 경험치를 100으로 나눈 몫을 추가 레벨로 설정
this.level += additionalLevels; // 포켓몬의 레벨을 추가 레벨만큼 증가
this.exp %= 100; // 남은 경험치를 100으로 나눈 나머지로 설정하여 경험치 초기화
if (this.level >= maxLevel) { // 레벨이 만렙에 도달한 경우
System.out.println("축하합니다!" + this.name + " 만렙 달성!"); // 만렙 달성 문구 출력
System.out.println("만렙을 달성해서 더이상 경험치를 획득할 수 없습니다!");
this.level = maxLevel; // 레벨을 최대 레벨(maxLevel)로 설정
this.exp = 0; // 추가 경험치 초기화
return true; // 더 이상 경험치를 처리할 필요 없으므로 true 반환
}
// 레벨업 메시지 출력
System.out.println(this.name + "의 레벨 업! 레벨 " + additionalLevels + " 증가! 현재 레벨은" + this.level+" 입니다!");
return false;
}
void attackVoice(){ // 공격할 때 기술명
System.out.println("attackVoice 출력");
}
@Override
public String toString(){//[포켓몬이름,포켓몬타입,포켓몬레벨,포켓몬경험치가 출력되게 설정
return "["+this.name+","+this.type+", 현재 레벨 : "+this.level+", 현재 경험치 : "+this.exp+"]";
}
} // 포켓몬 클래스 닫기
//자식클래스들//
class Ditto extends Pokemon{ // 자식클래스, 메타몽
// 생성자, 타입/이름 고정
Ditto(){
super("노말", "메타몽"); // 부모생성자 호출
}
// 함수
@Override
void hello() { // 울음소리 출력, 오버라이딩
System.out.println("어라?😭");
}
@Override
void attackVoice() { // 공격시 출력되는 attackVoice, 오버라이딩
System.out.print("변신!! ");
}
}
class Zorua extends Pokemon{ // 자식클래스, 조로아
// 생성자, 타입/이름 고정
Zorua(){
super("악", "조로아"); // 부모생성자 호출
}
// 함수
@Override
void hello() { // 울음소리 출력, 오버라이딩
System.out.println("조르륵😭");
}
@Override
void attackVoice() { // 공격시 출력되는 attackVoice, 오버라이딩
System.out.print("물기!! ");
}
}
class Cubone extends Pokemon{ // 자식클래스, 탕구리
// 생성자, 타입/이름 고정
Cubone(){
super("땅", "탕구리"); // 부모생성자 호출
}
// 함수
@Override
void hello() { // 울음소리 출력, 오버라이딩
System.out.println("구리😭");
}
@Override
void attackVoice() { // 공격시 출력되는 attackVoice, 오버라이딩
System.out.print("탕!탕! ");
}
}
class Oddish extends Pokemon{ // 자식클래스, 뚜벅쵸
// 생성자, 타입/이름 고정
Oddish(){
super("풀", "뚜벅쵸"); // 부모생성자 호출
}
// 함수
@Override
void hello() { // 울음소리 출력, 오버라이딩
System.out.println("뚜벅😭");
}
@Override
void attackVoice() { // 공격시 출력되는 attackVoice, 오버라이딩
System.out.print("잎날가르기!! ");
}
}
////////////////
public class Pokemongame{
public static int menu(){ // 메뉴판 출력
System.out.println("★★포켓몬 게임★★");
System.out.println("====== 메 뉴 ======");
System.out.println("1. 게임하기");
System.out.println("2. 전체 상태 출력");
System.out.println("3. 울음소리 듣기");
System.out.println("4. 포켓몬 잡기");
System.out.println("0. 게임종료");
System.out.println("==================");
System.out.print("메뉴를 선택해주세요 >> ");
// 메뉴 선택
int num = selectOne(0,4);
return num;
}
public static boolean isNull(int cnt){
/*보유포켓몬 없으면*/
if(cnt<=0) {
System.out.println("소유한 포켓몬이 없습니다!");
return true;
}
return false;
}
public static boolean isFull(int cnt, Pokemon[] datas){
/*포켓몬 가득차면*/
if(cnt>=datas.length) {
System.out.println("더이상 포켓몬을 소유할수없습니다");
return true;
}
return false;
}
public static int selectOne(int start, int end){
/*포켓몬 선택, 유효성 검사*/
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
if(start<=n && n <= end) {
return n;
}
else {
System.out.print("잘못된 입력입니다. 다시 선택해주세요 >> ");
return selectOne(start, end);
}
}
public static int pokemonMenu(int cnt, Pokemon[] datas) { //
System.out.println("포켓몬을 선택해주세요.");
for (int i = 0; i < cnt; i++) {
System.out.println((i+1)+". " +datas[i]);
}
// 출력된 포켓몬 중 선택
int num = selectOne(1, cnt);
return num-1; // 배열의 위치를 돌려줌
}
public static void main(String[] args) {
// 포켓몬을 담을 배열 생성
Pokemon[] datas = new Pokemon[3];
// 샘프 데이터 안의 포켓몬 수를 담는 변수 생성(cnt)
int cnt = 0;
Scanner sc = new Scanner(System.in);
// 게임을 끝내기 전까지 무한 반복
while(true) {
// 메뉴 출력 ,선택 함수를 불러 정수값을 받음
int menu = menu();
// 메뉴값에 따른 if문
// 만약 menu가 0이면
if(menu == 0) { // 게임 종료 (맡은 부분)
System.out.println("게임을 종료합니다.");
break;
}
// 만약 menu가 1이면
else if(menu == 1) { // 게임 시작하기 (맡은 부분)
// 지니고 있는 포켓몬이 있는지 확인
if(isNull(cnt)) { // 만약 비어있다면
continue;
}
// Y/N 답을 담을 문자열 변수 answer
String answer;
// 야생 포켓몬이 나타났습니다 출력
// 공격할 것인지 도망갈 것인지 출력
System.out.println("야생 포켓몬이 나타났습니다.");
System.out.print("공격하시겠습니까? (Y/N)");
answer = sc.next();
// 선택할 포켓몬 위치를 담을 변수
int num;
// 만약 공격한다면
if(answer.equals("Y")) {
// 포켓몬을 고름, pokemonMenu
num = pokemonMenu(cnt, datas);
}
// 아니라면 도망
else {
System.out.println("무사히 도망쳤습니다.");
continue;
}
// 실패시 다시 도전하는 프로그램
// 전투 반복
while(true) {
// 공격 시도 attack() 함수
// attack 함수 return 값이 boolean으로 바꿔야 함
// 공격 실패시
if(!(datas[num].attack())) {
// 다시 공격하겠습니까?(answer)
System.out.print("다시 공격하시겠습니까? (Y/N)");
answer = sc.next();
// Y면 다시 while문 처음의 attack 다시 실행
if(answer.equals("Y")) {
System.out.println("다시 공격을 시도합니다.");
}
// N 또는 그 외의 답이면 전투에서 벗어남
else {
System.out.println("무사히 도망쳤습니다.");
break;
}
}
// 성공했을 때
else {
break;
}
}
}
// 만약 menu가 2이면
else if(menu==2) {//전체상태 출력
if(isNull(cnt)) {//소유한 포켓몬이 없는지 유효성 검사
continue;
}
System.out.println("================");
for(int i=0;i<cnt;i++) {
System.out.println(datas[i].toString());
}
System.out.println("================");
}
else if(menu == 3) { // 3. 울음소리 듣기
// 보유한 포켓몬이 있는지 유효성 검사(isNull)
if(isNull(cnt)) {
continue;
}
//포켓몬 선택
int num;
num=pokemonMenu(cnt, datas); // 포켓몬 선택 메뉴 출력
// 울음소리 출력
System.out.print(datas[num].name + " : ");
datas[num].hello();
}
// 만약 menu가 4이면
else if(menu == 4) { // 포켓몬 잡기
// 포켓몬을 더 잡을 수 있는지 확인
if(isFull(cnt, datas)) { //만약 공간이 꽉 찼다면
System.out.println("더 이상 포켓몬을 잡을 수 없습니다!");
continue;
}
// 포켓몬 이름 배열
String[] pokemonName = {"메타몽", "조로아", "탕구리", "뚜벅초"};
// 잡을 수 있는 포켓몬 출력
System.out.println("잡을 수 있는 포켓몬");
// 포켓몬 이름 배열을 차례로 출력(for each문)
int i = 1; // 번호 출력용
for(String name : pokemonName) {
// 이름들 차례로 출력
System.out.println(i+". "+ name);
i++;
}
// 포켓몬을 선택 받음
System.out.print("잡을 포켓몬을 선택해주세요 >> ");
int num = selectOne(1, 4);
// 포켓몬을 잡음
if(num == 1) { // 메타몽, 1이라면
// 메타몽 객체 생성
datas[cnt] = new Ditto();
System.out.println("메타몽을 잡았습니다!");
}
else if(num == 2) { // 조로아, 2라면
// 조로아 객체 생성
datas[cnt] = new Zorua();
System.out.println("조로아를 잡았습니다!");
}
else if(num == 3) { // 탕구리, 3이라면
// 탕구리 객체 생성
datas[cnt] = new Cubone();
System.out.println("탕구리를 잡았습니다!");
}
else if(num == 4) { // 뚜벅초, 4라면
// 뚜벅초 객체 생성
datas[cnt] = new Oddish();
System.out.println("뚜벅초를 잡았습니다!");
}
// 소지 포켓몬 추가
cnt++;
}
else { // 잘못 입력시
System.out.println("잘못 입력하셨습니다.");
}
}
}
}
우리 조에서 작성한 설계, 회의록 등은 아래의 링크에서 확인할 수 있다.
https://ivory-turkey-66a.notion.site/TeamProject-e27a469100534f86843b034b4dca48ad?pvs=4
TeamProject | Notion
멤버 구성
ivory-turkey-66a.notion.site
좋은 조원들을 만나서 초기 설계과정부터 즐겁게 작업할 수 있었던 것 같다.
다음 팀플도 모두모두 화이팅!!
'Project' 카테고리의 다른 글
[JSP] 중간 팀 프로젝트 : View 테이블 생성 (0) | 2024.10.01 |
---|---|
[JSP/FrontController/DB] 팀 프로젝트_웹 사이트 만들기(중중프!) (14) | 2024.09.02 |
[JAVA]팀 프로젝트_MVC 패턴을 이용한 쇼핑몰 프로그램_3(최종) (3) | 2024.07.24 |
[JAVA]팀 프로젝트_MVC 패턴을 이용한 쇼핑몰 프로그램_2 (2) | 2024.07.22 |
[JAVA]팀 프로젝트_MVC 패턴을 이용한 쇼핑몰 프로그램_1 (6) | 2024.07.22 |