728x90
반응형
- 개요

위와 같이 플레이어의 시선 방향의 단위 벡터 f`와 플레이어로부터 목표물까지의 방향의 단위 벡터 v`를 내적한 값과
플레이어의 시야각 b에 대한 cos(b/2)의 값을 비교하여 목표물을 감지하는 로직을 구성할 수 있다
- 예제
...
// 게임 로직과 렌더링 로직이 공유하는 변수
// 플레이어의 시야각 / 플레이어의 최종위치 / 기본적인 플레이어의 색 / 목표물의 최종위치 / 기본적인 목표물의 색
float fovAngle = 60.f;
Vector2 playerPosition(0.f, 0.f);
LinearColor playerColor = LinearColor::Gray;
Vector2 targetPosition(0.f, 100.f);
LinearColor targetColor = LinearColor::Blue;
// 매 프레임마다 호출하는 함수
void SoftRenderer::Update2D(float InDeltaSeconds)
{
// 사용자로부터 입력을 받기 위한 레퍼런스
auto& g = Get2DGameEngine();
const InputManager& input = g.GetInputManager();
// 목표물의 움직임에 관한 로컬 변수
// 목표물의 이동속도 / 랜덤 시드 / 랜덤 시드 기반의 랜덤 엔진
// uniform_real_distribution 클래스를 사용하여 균등한 확률로 난수를 생성
// 목표물이 이동하는 시간의 최대 범위 / 목표물이 이동하는 시간 / 목표물의 이동 시작 위치 / 목표물의 이동 최종 목적지
static float moveSpeed = 100.f;
static std::random_device rd;
static std::mt19937 mt(rd());
static std::uniform_real_distribution<float> randomPosX(-300.f, 300.f);
static std::uniform_real_distribution<float> randomPosY(-200.f, 200.f);
static float duration = 3.f;
static float elapsedTime = 0.f;
static Vector2 targetStart = targetPosition;
static Vector2 targetDestination = Vector2(randomPosX(mt), randomPosY(mt));
// 플레이어의 시야각에 대한 cos(fovAngle / 2)의 값은 최초 한번만 저장
static float halfFovCos = cosf(Math::Deg2Rad(fovAngle * 0.5f));
// 목표물이 이동하는 시간을 설정
elapsedTime = Math::Clamp(elapsedTime + InDeltaSeconds, 0.f, duration);
// 이동하는 시간이 최대 범위에 도달하면 새로운 이동 지점을 랜덤하게 설정
if (elapsedTime == duration)
{
targetStart = targetDestination;
targetPosition = targetDestination;
targetDestination = Vector2(randomPosX(mt), randomPosY(mt));
elapsedTime = 0.f;
}
// 일정하게 목표지점까지 선형보간하면서 이동
else
{
float ratio = elapsedTime / duration;
targetPosition = Vector2(
Math::Lerp(targetStart.X, targetDestination.X, ratio),
Math::Lerp(targetStart.Y, targetDestination.Y, ratio)
);
}
// 입력을 통해 플레이어의 위치 설정
Vector2 inputVector = Vector2(input.GetAxis(InputAxis::XAxis), input.GetAxis(InputAxis::YAxis)).GetNormalize();
Vector2 deltaPosition = inputVector * moveSpeed * InDeltaSeconds;
playerPosition += deltaPosition;
// 플레이어의 시야 방향의 단위벡터 / 목표물의 위치 - 플레이어의 위치의 단위벡터
Vector2 f = Vector2::UnitY;
Vector2 v = (targetPosition - playerPosition).GetNormalize();
// cos(플레이어의 시야각 / 2) >= (시야각 벡터의 단위 벡터 · 목표물의 위치 - 플레이어의 위치의 단위벡터)
// 목표물이 시야각에 들어온 경우, 플레이어와 목표물을 빨갛게 칠함
if (v.Dot(f) >= halfFovCos)
{
playerColor = LinearColor::Red;
targetColor = LinearColor::Red;
}
// cos(플레이어의 시야각 / 2) < (시야각 벡터의 단위 벡터 · 목표물의 위치 - 플레이어의 위치의 단위벡터)
// 목표물이 시야각에 들어오지 않았으므로, 플레이어와 목표물의 원래 색을 칠함
else
{
playerColor = LinearColor::Gray;
targetColor = LinearColor::Blue;
}
}
...
매 프레임마다 호출하는 Update2D 함수는 위와 같이 구성하였다
목표물은 3초 단위로 랜덤한 위치로 이동하고, 플레이어는 입력을 받아 이동한다
플레이어의 시야각에 대한 벡터는 최초 한번만 구하여 설정하고,
시야 방향에 대한 단위 벡터와 목표물까지의 단위 벡터는 매 프레임마다 계산한다
시야각에 목표물이 들어오면 플레이어 + 목표물 모두 빨간색으로 표시하고,
시야각에 들어오지 않으면 각각 원래의 색으로 표시한다
...
// 화면에 렌더링하는 함수
void SoftRenderer::Render2D()
{
// 화면에 렌더하기 위해 가져올 레퍼런스
auto& r = GetRenderer();
const auto& g = Get2DGameEngine();
// 플레이어의 시야를 표현하기 위한 로컬 변수
// 플레이어와 목표물을 표현하는 원의 반지름 / 플레이어와 목표물을 표현하는 원의 점들을 저장 / 시야각을 표현하는 보조선의 길이
static float radius = 5.f;
static std::vector<Vector2> sphere;
static float sightLength = 300.f;
// 반지름 5인 원 내부의 모든 정수 좌표 점들을 sphere 벡터에 저장
if (sphere.empty())
{
for (float x = -radius; x <= radius; ++x)
{
for (float y = -radius; y <= radius; ++y)
{
Vector2 target(x, y);
float sizeSquared = target.SizeSquared();
float rr = radius * radius;
// 원의 방정식 x² + y² < r²을 만족하는 점들만을 저장
if (sizeSquared < rr)
{
sphere.push_back(target);
}
}
}
}
// 플레이어 렌더링
// 시야각을 나타내는 보조선을 그리기 위해 sin 함수와 cos 함수를 계산
float halfFovSin = 0.f, halfFovCos = 0.f;
Math::GetSinCos(halfFovSin, halfFovCos, fovAngle * 0.5f);
// 시야각을 나타내는 2개의 보조선과 시선 방향의 벡터를 표현
r.DrawLine(playerPosition, playerPosition + Vector2(sightLength * halfFovSin, sightLength * halfFovCos), playerColor);
r.DrawLine(playerPosition, playerPosition + Vector2(-sightLength * halfFovSin, sightLength * halfFovCos), playerColor);
r.DrawLine(playerPosition, playerPosition + Vector2::UnitY * sightLength * 0.2f, playerColor);
for (auto const& v : sphere)
{
r.DrawPoint(v + playerPosition, playerColor);
}
// 목표물 렌더링
for (auto const& v : sphere)
{
r.DrawPoint(v + targetPosition, targetColor);
}
// 주요 정보 출력
r.PushStatisticText(std::string("Player Position : ") + playerPosition.ToString());
r.PushStatisticText(std::string("Target Position : ") + targetPosition.ToString());
}
...
위와 같이 Render2D 함수를 구현하여 화면에 렌더링한다
플레이어는 2개의 보조선과 한개의 시선벡터를 표현하며, 목표물은 부가적인 요소를 표현하지 않는다
- 최종 실행 결과

728x90
반응형
'수학 > 이득우의 게임 수학 - 실습 예제' 카테고리의 다른 글
| 벡터의 내적을 활용한 벡터의 투영 구현 (0) | 2025.11.19 |
|---|---|
| 벡터의 내적을 활용한 조명 모델의 구현 (0) | 2025.11.19 |
| 라인 클리핑 알고리즘의 구현 (0) | 2025.10.29 |
| 브레젠험 알고리즘의 구현 (0) | 2025.10.15 |
| 스크린 좌표계 구조 (0) | 2025.10.01 |