개요
개발 일지를 적기에 앞서 저번 글을 적은 이유는 블로그 유입에 대한 고민이아닌 말 그대로 블로그를 쓰는 방향성에 대한 것이였습니다.
그래도 값진 피드백을 들을 수 있었으니 괜찮은 경험이였죠.
글을 적다가 한번 날아가기도 했고 지금 개발 상황은 상당히 진행된 상탠데 어디부터 적어야 할지 조금 막막합니다.
그러니 대충 적는것 처럼 보여도 이해 부탁드립니다.
아이디어 정리
전 글에서 적었듯이 Ally는 각각의 공격 사거리를 가집니다.
보통 이 사거리는 공격자를 기준으로 사거리만큼의 반지름을 가진 원을 그려 그 안에 있는 적을 공격하는 식으로 작동합니다.
근데 여긴 OverlapCircle을 제공해주는 유니티가 아닌 콘솔창입니다.
픽셀이랑 유사한 칸 구조를 갖고 있는것과는 별개로 원을 어떻게 그리는지 조차 모릅니다.
찾아보면 할 수는 있겠지만... 별로 내키진 않았습니다.
그렇기 때문에 조금 불합리 할 순 있어도 공격 사거리 만큼의 모서리 길이를 가진 정사각형을 그려 판단하기로 했습니다.
짝수일때는요?
음... 저희 공격 사거리는 다 홀수로 합시다. 짝수여도 작동은 하겠지만..
...크기가 좀 안맞는거 같은데 양해 부탁드립니다.
Ally Attack
먼저 적어뒀던 Ally 클래스를 수정합니다.
class Ally : public Entity
{
public:
Ally();
virtual ~Ally();
protected:
int _attackTime = 500;
int _attackRange = 5;
int _damage = 5;
int _price = 20;
clock_t _lastAttackTime = 0;
public:
void tryAttack();
virtual void attack();
virtual vector<Enemy*> defineTargets() abstract;
};
일단 attack이 추상 함수일 이유가 없습니다. 어짜피 target 공격하는건 전부 똑같지 않습니까?
그래도 혹시 모를 상황을 위해 가상 함수로 두어 나중에 재정의 할 가능성도 남겨놓았습니다.
그리고 update에서 계속 실행시켜줄 tryAttack(),
범위공격을 상정해 vector<Enemy*>를 반환하는 defineTarget()을 적어줍니다.
attack은 쉬우니까 빠르게 먼저 적어보겠습니다.
//Ally.cpp
void Ally::attack()
{
vector<Enemy*> targets = defineTargets();
for(auto* enemy : targets)
{
enemy->modifyHP(-_damage);
}
}
이제 문제는 defineTargets입니다.
defineTargets는 추상함수기 때문에 일단 대충 미리 만들어둔 Archer에서 정의해보겠습니다.
일단 정사각형을 그려서 그 안에 있는 Enemy를 가져와야 합니다.
네... 그러니까 이런식으로 탐색해야 하니 일단 저 왼쪽 위 Cell의 좌표가 필요합니다.
그건 이런식으로 구하면 되죠.
//Archer::defineTargets()
int x = _currentPos.x - _attackRange / 2;
int y = _currentPos.y - _attackRange / 2;
그리고 저 왼쪽 위부터 오른쪽 아래까지 쭉 탐색 돌아주면 됩니다.
//Archer::defineTargets()
vector<Enemy*> targetVec;
Enemy* target = nullptr;
int x = _currentPos.x - (_attackRange / 2);
int y = _currentPos.y - (_attackRange / 2);
for (int i = y; i < y + _attackRange; i++)
{
for (int j = x; j < x + _attackRange; j++)
{
Vector2 pos = Vector2(j, i);
if (pos.x < 0 || pos.y < 0) continue;
Cell* cell = GET_SINGLETON(MapManager)->getCell(pos);
if (cell->type == MAP_TYPE::ROAD)
{
vector<Enemy*> vec = cell->getEntities<Enemy>(ENTITY_TYPE::ENEMY);
for (auto i : vec)
{
if (target == nullptr)
{
target = i;
continue;
}
if (target->getMoveCount() < i->getMoveCount())
{
target = i;
}
}
}
}
}
if (target != nullptr)
targetVec.push_back(target);
return targetVec;
예외처리같은건 간단합니다.
x나 y 둘중 하나라도 음수면 그곳에 존재하는 cell이 없으니까 예외처리,
ROAD가 아닐경우 Enemy가 존재할리가 없으니 예외처리하고
만약 target을 못찾았을경우엔 그냥 빈 vector를 리턴합니다.
Enemy의 getMoveCount는 뭐냐구요?
말 그대로 움직인 횟수인데 왜 이걸 토대로 비교하냐면... 이렇게 구불구불하게 생긴 곳에서 선두를 우선으로 공격하는 기믹을 위해 만들어 놓았습니다.
물론 완벽하진 않지만요.
그리고 이제 InGameScene에서 update로 돌려주면 됩니다.
//InGameScene::update()
for(auto& ally : GET_SINGLETON(EntityManager)->getAlly())
{
ally->tryAttack();
}
Enemy 타격 이펙트
근데 이러면 문제가 하나 있습니다.
코드가 작동하지 않는게아니라 그냥 적이 사라져요.
안그래도 아군 투사체가 없어서 별론데 적이 맞는 티는 내야할거 아닙니까?
그래서 그냥 간단하게 빨간색으로 바뀌었다가 다시 돌아오는 느낌으로 만들었습니다.
..솔직히 이건 그냥 넘길게요 굳이 이거까지 설명할 필요는 못느꼈습니다.
Ally의 tryAttack나 Enemy의 tryMove과 비슷한 구조를 사용합니다.
그냥 맞은 시간 구해서 특정 시간동안 빨갛게 변했다가 지나면 다시 원래색으로 돌아오는 그런 코드입니다.
말로만 해도 대충 알아들으시겠죠?
결과물
잘되죠?
그나마 공격하는 것 같긴 합니다.
투사체가 없는건 아쉽지만요.
마치는 말
글이 한동안 안올라왔죠?
왜냐하면 바빴기 떄문입니다.
오늘이 이 프로젝트 마감일입니다.
그러니까 블로그글 후딱 그냥 써버리겠습니다.
이제 학교내에서 중요한 프로젝트는 끝났어요.
이제 제가 따로 하는게 많이 남았죠.
으악
'개발 > Default Defense' 카테고리의 다른 글
Default Defense | 다섯번째 프로젝트 # 8 : Wave, InGameState (1) | 2024.07.04 |
---|---|
Default Defense | 다섯번째 프로젝트 # 7 : 구조 수정 (0) | 2024.06.30 |
Default Defense | 다섯번째 프로젝트 # 5 : Enemy Movement (1) | 2024.06.21 |
Default Defense | 다섯번째 프로젝트 # 4 : Entity (2) | 2024.06.15 |
Default Defense | 다섯번째 프로젝트 # 3 : 맵 (2) | 2024.06.15 |