언리얼

C++) TRPG #03: 무기 및 스킬 클래스 생성

eclipse2 2026. 3. 27. 21:10

오늘은 추가적으로 게임에 컨셉에 맞게 weapon 클래스 와 skill 클래스 을 추가적으로 구현했다.

아이디어를 계속 교환 하던 중에 스킬이 있으면 좋을 것 같다고 해서 총 세가지 스킬을 구상했다.

  1. 데미지 세배 공격(TrippleDamage)
  2. cc기인 속박 스킬(BindSkill)
  3. 적의 공격을 카운터 치는 스킬(counterstrike)

2번 스킬은 적의 속도를 50% 낮추며 선제 공격을 할 수 있게 해주는 디버프 스킬이며

3번 카운터 스킬은 적보다 speed가 높을 시에 적의 공격력 + 나의 공격력을 적에게 받아치는 스킬이다. 하지만, 적 보다 속도가 낮을 시에는 체력이 깎인다. 따라서, pressure 압력이 100이 됐을 때 속도가 낮아지는 데 이때는 이 스킬이 무용지물이다.

이런 기획을 가지고 코드를 작성하게 됐다.

 

#pragma once
#include <string>

class Monster;

enum class WeaponType { FLASH, VANTA_BLACK, EXPLOSIVE_GUN, NONE };

class Weapon {
protected:
	std::string name;
	WeaponType type;
	int addDamage;

public:
	Weapon(std::string n, WeaponType t, int d, int bc)
		: name(n)
		, type(t)
		, addDamage(d) {
	}

	virtual ~Weapon() {} // 상속 시
	

	// 공통 정보 조회
	std::string getName() const { return name; }
	WeaponType getType() const { return type; }
	int getAddDamage() const { return addDamage; } 
};
class Flashbang : public Weapon {
public:
	Flashbang();
};

class Vantablack : public Weapon {
public:
	Vantablack();
};

class WaterGun : public Weapon {
public:
	WaterGun();
};
#include "Weapon.h"
#include "Monster.h"
#include "GameLogger.h"
#include <iostream>

//// 섬광탄 (Flashbang)
Flashbang::Flashbang() : Weapon("섬광탄", WeaponType::FLASH, 30) {}
//int Flashbang::calculateDamage(int baseAttack, const Monster* target) const {
//	int total = baseAttack + addDamage;
//	// MonsterStat의 type이 Hidden(은닉형)이면 2배 데미지
//	if (target && target->getMonsterType() == AttributeType::Hidden) {
//		std::cout << "\n 섬광이 은닉한 적을 밝혀냅니다! (데미지 2배)" << std::endl;
//		return total * 2;
//	}
//	return total;
//}
//
//// 반타블랙 (Vantablack)
Vantablack::Vantablack() : Weapon("반타블랙 검", WeaponType::VANTA_BLACK, 35) {}
//int Vantablack::calculateDamage(int baseAttack, const Monster* target) const {
//	int total = baseAttack + addDamage;
//	// Luminous(발광형)에게 2배
//	if (target && target->getMonsterType() == AttributeType::Luminous) {
//		std::cout << "\n 반타블랙이 적의 빛을 흡수합니다! (데미지 2배)" << std::endl;
//		return total * 2;
//	}
//	return total;
//}
//
//// 폭발형 수총 (WaterGun)
WaterGun::WaterGun() : Weapon("폭발형 수총", WeaponType::EXPLOSIVE_GUN, 50) {}
//int WaterGun::calculateDamage(int baseAttack, const Monster* target) const {
//	int total = baseAttack + addDamage;
//	// Giant(거대형)에게 2배
//	if (target && target->getMonsterType() == AttributeType::Giant) {
//		std::cout << "\n 압축 폭발이 거대한 외피를 뚫습니다! (데미지 2배)" << std::endl;
//		return total * 2;
//	}
//	return total;
//}

먼저 본래 상성 관계의 데미지 강화 로직을 무기에 세팅을 했다가 gamesystem으로 옮기게 됐다.

 

#pragma once
#include <string>
#include <memory>
#include <iostream>

class Player;
class Monster;

class Skill
{
protected:
	std::string name;
public:
	Skill(std::string n);
	virtual ~Skill();

	virtual void execute(Player* player, Monster* target) = 0;
	std::string getName() const { return name; }
};
class TripleDamageSkill : public Skill {
public:
	TripleDamageSkill();
	void execute(Player* player, Monster* target) override;
};
class BindSkill : public Skill {
public:
	BindSkill();
	void execute(Player* player, Monster* target) override;
};
class CounterSkill : public Skill {
public:
	CounterSkill();
	void execute(Player* player, Monster* target) override;
};
#include "Skill.h"
#include "Player.h"
#include "Monster.h"

Skill::Skill(std::string n) : name(n) {}
Skill::~Skill() {}

TripleDamageSkill::TripleDamageSkill() : Skill("파워 스트라이크") {}
BindSkill::BindSkill() : Skill("그물 속박") {}
CounterSkill::CounterSkill() :Skill("크로스 카운터") {}

void TripleDamageSkill::execute(Player* player, Monster* target) {
	if (!player || !target) return;

	int enhancedDamage = player->getAttack() * 3;
	std::cout << "[스킬] " << name << " 발동!" << std::endl;
	std::cout << "강력한 일격이 " << target->getName() << " 에게 " << enhancedDamage << "의 피해를 입힙니다!" << std::endl;
	target->takeDamage(enhancedDamage);

}
void BindSkill::execute(Player* player, Monster* target) {
	if (!player || !target) return;

	// 몬스터 속도 감소
	int currentMonsterSpeed = target->getSpeed();
	int reducedSpeed = currentMonsterSpeed / 2;

	target->setSpeed(reducedSpeed);
	std::cout << "[스킬] " << name << " 발동!!" << std::endl;
	std::cout << target->getName() << "의 움직임이 둔해졌습니다! (속도: " << currentMonsterSpeed << " -> " << reducedSpeed << ")" << std::endl;

}
void CounterSkill::execute(Player* player, Monster* target) {
	if (!player || !target) return;

	if (player->getSpeed() > target->getSpeed()) {
		int counterDamage = target->getAttack() + player->getAttack();

		std::cout << "[스킬] " << name << " 발동!" << std::endl;
		std::cout << "적의 공격을 읽고 카운터를 날립니다!" << std::endl;
		std::cout << "데미지 계산: [적 ATK " << target->getAttack()
			<< "] + [내 ATK " << player->getAttack() << "]" << std::endl;

		target->takeDamage(counterDamage);
	}
	else {
		std::cout << "적의 속도가 너무 빠릅니다! 카운터에 실패했습니다..... 역습이 허용됐습니다" << std::endl;
		//실패 시 체력을 깍음
		player->takeDamage(20);
	}
}

작성 한거 와 같이 스킬을 구현 했다.

스킬을 얻는 방식은 기존에 levelUp 함수와 연동해서 3, 6, 9렙이 됐을 때 렌덤한 스킬을 배울 수 있고 총 1개의 스킬만 배울 수 있도록 만들었다.

void Player::levelUp() {
	level++;
	int hpBonus = level * 20;
	int atkBonus = level * 5;

	maxHp += hpBonus;
	baseAttack += atkBonus;
	hp = maxHp;
	exp -= maxExp;
	if (exp < 0) exp = 0;

	cout << "=============================" << endl;
	cout << "Level UP!!! 현재 레벨: " << level << endl;
	cout << "최대 체력 " << hpBonus << " 증가 / 공격력 " << atkBonus << " 증가" << endl;
	cout << "=============================" << endl;

	if (level == 3 || level == 6 || level == 9) {
		cout << "특정 레벨 도달! 고대의 기술 중 하나를 연마할 수 있습니다." << endl;

		int skillIdx = Random::getRandomValue(1, 3);
		unique_ptr<Skill> discoveredSkill;

		switch (skillIdx) {
		case 1:
			discoveredSkill = make_unique<TripleDamageSkill>();
			break;

		case 2:
			discoveredSkill = make_unique<BindSkill>();
			break;

		case 3:
			discoveredSkill = make_unique<CounterSkill>();
			break;
		}
		cout << "새롭게 발견한 기술 : [" << discoveredSkill->getName() << "] " << endl;

		this->learnSkill(move(discoveredSkill));
	}
}
//무기 장착 구현
void Player::setWeapon(std::unique_ptr<Weapon> newWeapon) {
	if (!newWeapon) return;
	equippedWeapon = std::move(newWeapon);
	cout << name << " 대원이 " << equippedWeapon->getName() << "무기를 장착했습니다";
}
//스킬 배우기
void Player::learnSkill(unique_ptr<Skill> newSkill) {
	if (!newSkill) return;
	if (currentSkill) {
		cout << "---------------------------------------------" << endl;
		cout << "이미 스킬[" << currentSkill->getName() << "]을 보유 중입니다." << endl;
		cout << "새 스킬 [" << newSkill->getName() << "]로 교체하시겠습니까?" << endl;
		cout << "1. 교체한다 (기존 기술 삭제)  2. 유지한다 (새 기술 버림)" << endl;

		if (InputSystem::getInputInt(1, 2) == 1) {
			currentSkill = move(newSkill);
			cout << "새로운 스킬을 배웠습니다!" << endl;
		}
		else {
			cout << "기존 스킬을 유지합니다." << endl;
		}
	}
	else {
		currentSkill = move(newSkill);
		cout << "\n스킬 [" << currentSkill->getName() << "]을 습득했습니다!\n";
	}
}
// 스킬 사용
void Player::useSkill(Monster* target) {
	if (currentSkill) {
		currentSkill->execute(this, target);
	}
	else {
		std::cout << "배운 스킬이 없어 사용할 수 없습니다.\n";
	}
}

오늘 여러 문제들이 발생했다.

  1. merge 중 충돌
  2. visual studio 솔루션 탐색기에 파일이 안뜨는 문제(이것 또한 잘 못된 머지 문제)

여러 사람이 작업을 동시에 하기 때문에 여러 프로젝트가 동시에 pull request 하는 경우도 있고 제일 최근의 main branch를 본인 branch 에 update 안해놓은 경우도 있어 고통에 시간을 보냈습니다.

하지만, 이번 기회에 깃 관리를 담당하며 많이 배우게 됐고 제가 충돌을 알아서 해결하며 merge하는 법도 배우며 이로운 시간 이였습니다.

'언리얼' 카테고리의 다른 글

C++) TRPG 최종  (0) 2026.04.01
C++) TRPG#04 : 패치 및 수정  (0) 2026.03.30
C++) TRPG #02 : 플레이어 클래스 생성  (0) 2026.03.26
C++) TRPG #01 : 기획  (0) 2026.03.25
TRPG C++  (0) 2026.03.23