Java 응용 | 턴제게임 만들기(3)

Dalyume·2026년 8월 7일

Java 응용

목록 보기
14/15
post-thumbnail

오늘은 아이템을 중심으로 코드를 짰다.

이전 타임에서는 아이템을 드롭하고 드롭된 아이템을 인벤토리에 넣었었다. 이번에는 그 아이템 상세를 확인 할 것이다.


Potion 클래스 및 하위 클래스 상속.

몬스터가 드롭하게될 Posion의 정보를 담을 포션 클래스를 만들었다. 그리고 이는 Item에 상속했다.

package com.practice.item;

import com.practice.chars.Character;
import com.practice.exception.PotionNotFountException;

public abstract class Potion extends Item{
    protected int statistic;

    public void use(Character character) {
        if(count <= 0){
            throw new PotionNotFountException("사용할 포션이 없습니다.");
        }

        applyEffect(character);

        count -= 1;

    }

    public abstract void applyEffect(Character character);

}

추상 클래스로 만들었다. Potion 직접 못 꺼내고, 상속된 자식 클래스만 호출이 가능하도록 만들기 위함이다.

use함수가 호출되면, 포션을 사용할 수 있도록 상속된 클래스(ex : HealingPotion) 가 character를 호출해서 statistic 변수를 넘긴다.

여기서 함수를 호출하기 전, 인벤토리에 포션이 있는지 검사부터 하는데, 없으면 예외처리된다. (저 예외처리는 새로 만들었음.) count는 Item클래스에서 받아온거다. (protected 선언이라 자식 클래스에서 사용 가능.)

statistic을 캐릭터 클래스가 받으면, 그에 맞게끔 연산한다.(상승 수치는 character 클래스가 계산함)

public void heal(int amount){
        hp += amount;

        if(hp > fullHp){
            hp = fullHp;
        }
    }

HealingPotion 클래스

package com.practice.item;

import com.practice.chars.Character;
import com.practice.exception.AlreadyFullHealthException;

public class HealingPotion extends Potion {

    public HealingPotion(int count) {
        super();
        super.name = "회복포션";
        super.count = count;
        super.statistic = 10;
    }

    @Override
    public void applyEffect(Character character) {

        if(character.getHp() >= character.getFullHp()){
            throw new AlreadyFullHealthException("이미 체력이 가득합니다.");
        }

        System.out.println(character.getName() + "은(는) " + name + "을 사용했다.");
        character.heal(statistic);
        System.out.println(character.getName() + "의 체력이 " + statistic + "회복되었다. " + character.getHp() + " / " + character.getFullHp());
    }

}

아이템 정보 확인 인터페이스

이제 인벤토리 조회, 아이템 사용까지는 만들었으니, 그 아이템에 대한 조회도 만들어보기로 했다.

package com.practice.inventory;

public interface ItemInfo {
    void itemInfo();
}
  1. 단 인터페이스를 만들어.
  2. Item 클래스에 상속해.
public abstract class Item implements ItemInfo 

보면 Item도 추상 클래스로 바뀌었다. 왜냐?

@Override
public abstract void itemInfo();

Item에 itemInfo 내용을 적을 순 없으니 상속한 자식 클래스한태 짬을 때리기 위함.

public abstract class Potion extends Item

이러면 Potion클래스와,

public abstract class Weapon extends Item

Weapon 클래스도 추상 클래스인지 감이 잡히겠지.

맞다. 짬때리기~

package com.practice.item;

import com.practice.chars.Character;
import com.practice.exception.AlreadyFullHealthException;

public class HealingPotion extends Potion {

		...
    
    @Override
    public void itemInfo() {
        System.out.println(name + "이다. 체력" + statistic + "회복");
    }
}

이제 짬맞은 HealingPotion쪽에서 아이템 정보를 알려준다.

package com.practice.item;

public class Sword extends Weapon{
    public Sword(String name) {
        super();
        super.name = name;
        super.count = 1;
        super.weapon_atk = 5;
    }

    @Override
    public void itemInfo() {
        System.out.println("기사의 검이다. " + weapon_atk + "의 공격력을 가진다.");
    }
}

물론 무기도.

몬스터 아이템 드롭

자자 아직 끝나지 않았다.

몬스터가 아이템을 떨궈야함. 이전에는 그냥 print써서 출력된 텍스트를 저장했지만, 지금은 다르지 않은가.

Monster 클래스에 dropItem을 수정했다.

public Item dropItem(){
        int randomValue = random.nextInt(1);
        int randomCount = random.nextInt(3) + 1;

        switch (randomValue){
            case 0:
                return new HealingPotion(randomCount);
        }

        return null;
    }

포션이 나중에 더 추가될 것이기에, 랜덤한 물약을 랜덤한 개수(1~3)로 드롭하게 바꿨다. 랜덤성은 최고다.

 @Override
    public void died() {
        super.died();
        int randomDrop = random.nextInt(10) + 1;

        if(randomDrop > 3){
            droppedItem = dropItem();
            System.out.println(name + "은(는) " + droppedItem.getName() + "을(를) " + droppedItem.getCount() + "개 떨궜다.");
        }
    }

죽었을 때 드롭하는 코드도 바꿔주자.

참고로 생성된 (드롭된) 아이템은 item 클래스에 저장된다. ( 마지막 MainTest에서 어떻게 호출되나보면 이해가 갈 것이다.)

전체코드

package com.practice.chars;

import com.practice.item.HealingPotion;
import com.practice.item.Item;

import java.util.Random;

public class Monster extends Character{

    private Random random = new Random();
    private Item droppedItem;

    public Item getDroppedItem() {
        return droppedItem;
    }

    public Item dropItem(){
        int randomValue = random.nextInt(1);
        int randomCount = random.nextInt(3) + 1;

        switch (randomValue){
            case 0:
                return new HealingPotion(randomCount);
        }

        return null;
    }

    @Override
    public void died() {
        super.died();
        int randomDrop = random.nextInt(10) + 1;

        if(randomDrop > 3){
            droppedItem = dropItem();
            System.out.println(name + "은(는) " + droppedItem.getName() + "을(를) " + droppedItem.getCount() + "개 떨궜다.");
        }
    }
}

테스트

이제 얼추 다 짰으니, 테스트를 해봐야지.

package com.practice;

import com.practice.chars.Ghoul;
import com.practice.chars.Warrior;
import com.practice.item.HealingPotion;
import com.practice.item.Item;
import com.practice.item.Potion;
import com.practice.item.Sword;

public class MainGameTest {
    public static void main(String[] args) {
        Warrior warrior = new Warrior("전사");
        Ghoul ghoul = new Ghoul();
        Sword sword = new Sword("검");

        warrior.equipWeapon(sword);
        sword.itemInfo();
        warrior.attack(ghoul);
        ghoul.attack(warrior);
        warrior.attack(ghoul);
        warrior.attack(ghoul);
        warrior.attack(ghoul);

        Item item = ghoul.getDroppedItem();

        if(item != null){
            warrior.pickUpItem(item);
            item.itemInfo();

            if(item instanceof Potion potion){
                potion.use(warrior);
            }
        }

    }
}

보면

    Item item = ghoul.getDroppedItem();

구울이 드롭한 아이템이 item에 저장된다.

저장된 item을 warrior가 pickupitem 함수로 호출해서 그 아이템을 인벤토리에 넣는다.

그리고 여기서 instanceof 연산자가 나오는데,

if(item instanceof Potion potion){
                potion.use(warrior);
            }

간딘히 설명하자면

객체가 특정 클래스나 생성자의 타입인지 확인하여 참(true) 또는 거짓(false)을 돌려주는 연산자

이다. (gpt 참고)

아무튼- item이 상속된 potion 타입인지를 확인하고, 참이면

warrior가 그 포션을 사용한다.

아 참고로 힐링포션 클래스에 조건을 하나걸었는데, 포션을 사용할 때 체력이 이미 풀이면, 예외처리가 된다.

if(character.getHp() >= character.getFullHp()){
            throw new AlreadyFullHealthException("이미 체력이 가득합니다.");
        }

이제 제법 그럴 듯 하게 만들어지고 있는 것 같다.

테스트 결과

전사은 검을 장착했다.
기사의 검이다. 5의 공격력을 가진다.
전사의 공격!
구울은() 13만큼의 데미지를 입었다!
구울의 남은 채력 : 37/50
구울의 맨손공격!
전사은() 3만큼의 데미지를 입었다!
전사의 남은 채력 : 97/100
전사의 공격!
구울은() 13만큼의 데미지를 입었다!
구울의 남은 채력 : 24/50
전사의 공격!
구울은() 13만큼의 데미지를 입었다!
구울의 남은 채력 : 11/50
전사의 공격!
구울은() 쓰러졌다!
구울은() 회복포션을() 2개 떨궜다.
전사는 회복포션 2개를 인벤토리에 넣었다.
==========인벤토리==========
1. 회복포션 / 2==========================
회복포션이다. 체력10회복
전사은() 회복포션을 사용했다.
전사의 체력이 10회복되었다. 100 / 100

체력이 풀피일 때

전사은 검을 장착했다.
기사의 검이다. 5의 공격력을 가진다.
전사의 공격!
구울은() 13만큼의 데미지를 입었다!
구울의 남은 채력 : 37/50
전사의 공격!
구울은() 13만큼의 데미지를 입었다!
구울의 남은 채력 : 24/50
전사의 공격!
구울은() 13만큼의 데미지를 입었다!
구울의 남은 채력 : 11/50
전사의 공격!
구울은() 쓰러졌다!
구울은() 회복포션을() 2개 떨궜다.
전사는 회복포션 2개를 인벤토리에 넣었다.
==========인벤토리==========
1. 회복포션 / 2==========================
회복포션이다. 체력10회복
Exception in thread "main" com.practice.exception.AlreadyFullHealthException: 이미 체력이 가득합니다.
	at com.practice.item.HealingPotion.applyEffect(HealingPotion.java:20)
	at com.practice.item.Potion.use(Potion.java:14)
	at com.practice.MainGameTest.main(MainGameTest.java:30)

포션이 없을 때

전사은 검을 장착했다.
기사의 검이다. 5의 공격력을 가진다.
전사의 공격!
구울은() 13만큼의 데미지를 입었다!
구울의 남은 채력 : 37/50
전사의 공격!
구울은() 13만큼의 데미지를 입었다!
구울의 남은 채력 : 24/50
전사의 공격!
구울은() 13만큼의 데미지를 입었다!
구울의 남은 채력 : 11/50
전사의 공격!
구울은() 쓰러졌다!
Exception in thread "main" com.practice.exception.PotionNotFoundException: 사용할 포션이 없습니다.
	at com.practice.item.Potion.use(Potion.java:11)
	at com.practice.MainGameTest.main(MainGameTest.java:37)
profile
신생아 개발자

0개의 댓글