Post

(TIL) FINAL-DAY2

FINAL - DUCKTOPIA: The Mustard Wars 2일차


서버에서 플레이어의 공격 처리에 대한 기능을 구현하게 되었고, 클라이언트로 공격 행위 시 공격 방향에 대한 정보만 받기로 하였고 그 후에 공격 판정 여부에 대해서는 서버에서 처리하기로 흘러가게되었다. 그래서 어떤 방식으로 몬스터의 위치를 체크하면서 검증을 통해 공격 판정을 내리고 처리해야할지 알아보게 되었고, 그 결과 벡터를 활용하여 방향 및 위치를 계산하여 판정을 내리는 방법이 보통 사용된다는 것을 알게 되어 정리해보았다.

벡터(Vector)

벡터 공간(Vector Space)을 이루는 단위 원소를 의미한다. 게임 프로그래밍에서는 주로 벡터라는 이름의 구조체를 사용해 물체의 위치를 지정하다보니 벡터가 위치를 의미하는 것으로 혼동되고 있지만 원래 벡터는 크기와 방향만 존재할 뿐 위치에 대한 정보는 없다고 한다. 프로그래밍적으로 얘기를 하자면 백터는 어느 지점에서 어느 지점으로 가는 길을 나타낸다.

벡터

즉 위 그림과 같은 벡터는 방향크기를 가진 화살표라고 생각하면 된다. 벡터의 값이 (3, 2) 라고 치면 출발 지점에 도착 지점까지 X 축으로 3, Y 축으로 2 만큼 가는 방향을 의미하는 것이다.

벡터의 내적

본래 벡터는 ‘위치, 힘, 방향’을 의미한다. 그리고 두 벡터를 내적한다 라는 말의 의미는 두 벡터가 작용한 일의 효율을 나타낸다. 내적은 벡터의 차원에 관계 없이, 동일한 차원의 벡터끼리 가능하다. 벡터 A, B의 내적은 A를 B에(또는 B를 A에) 투영시킨 후 두 벡터의 길이를 곱한 것과 같다. 이는 게임에 플레이어가 바라보는 방향과 물체의 사이 각도의 코사인 값를 구하기 위해 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const monsterVector = [monster.x - player.x, monster.y - player.y];

//대상(몬스터)의 거리 계산
const distance = Math.sqrt(Math.pow(monsterVector[0], 2) + Math.pow(monsterVector[1], 2));
if(distance <= player.getRange()){

    // 2단계: 각도 범위 내에 있는지 확인 (내적 계산)
    const vectorSize = Math.sqrt(Math.pow(monsterVector[0], 2) + Math.pow(monsterVector[1], 2));
    
    // 단위 백터 (정규화)
    const normalizedMonsterVector = [monsterVector[0] / vectorSize, monsterVector[1] / vectorSize];

    // TODO : 검증 해봐야함...
    const attackDirection = [1, 0];  // 공격 방향 (예시)

    // 내적계산
    const dotProduct = attackDirection[0] * normalizedMonsterVector[0] + attackDirection[1] * normalizedMonsterVector[1];
    // Radian 변환
    const angleInDegrees = Math.acos(dotProduct) * (180 / Math.PI);

    // 180도
    if (angleInDegrees <= player.getAttackAngle() / 2) {
        
        const monsterHp = monster.getDamge(player.getPlayerAtkDamage());

        let responsePayload = {};
        if(monsterHp <= 0){
            // 몬스터 사망
            responsePayload = {
                id : monster.id
            }
        }else{
            responsePayload = {
                id : monster.id,
                hp : monsterHp,
            }
        }

        // Broadcast - 모든 플레이어에게 전달
        game.broadcast(responsePayload);
    }
}
This post is licensed under CC BY 4.0 by the author.