터널 안에서 날씨를 바꾸면 터널 제한속도가 사라짐
터널 안에서 Clear일 때 속도가 50 언저리도록 설정했는데 다른 날씨로 바꿨다가 다시 Clear로 돌아오면 속도가 70으로 변하는 오류 발견
-> 뭔가 덮어씌워지는 것 같았음

한 번 거쳐 가면 망가지는 패턴은 전형적인 상태 오염 신호
원인 추적 -> 두 시스템이 같은 변수를 만짐
터널 시스템: 터널 진입 시 MaxSpeed에 TunnelSpeedScale(0.7)을 곱한다
날씨 시스템: 날씨별로 MaxSpeed를 설정한다 (Clear 2200, Rain 1700, Snow 1400)
둘 다 같은 MaxSpeed 변수에 최종값을 씀
1. 터널 진입 (Clear)
MaxSpeed = 2200 × 0.7 = 1540 → 터널이라 느림 (속도 50)
2. 날씨를 Rain으로 (터널 안)
ApplyWeatherProfile: MaxSpeed = 1700 ← 터널의 1540을 덮어씀
3. 날씨를 Clear로 복귀 (터널 안)
ApplyWeatherProfile: MaxSpeed = 2200 ← 평지 속도로 복귀
터널 안인데 MaxSpeed 2200 → 속도 70 (버그)
날씨 코드가 MaxSpeed를 설정할 때 터널의 적용 결과를 덮어씀
그리고 터널 코드의 베이스라인 캐싱은 play 통틀어 딱 한 번만 일어남
if (!bBaselineCached) // 최초 1회만 true
{
BaselineMaxSpeed = MaxSpeed; // 최초 터널 진입 시점의 값에 영구 고정
bBaselineCached = true;
}
터널의 기준값은 최초 터널 진입 때의 2200에 영구 고정됨
단순 덮어쓰기가 아니라 두 시스템이 서로의 변경을 모른 채 각자 다른 베이스라인을 1회 캐싱하는 이중 구조였음
베이스라인 캐싱은 누적 오염을 막는 좋은 패턴이었지만 두 시스템이 각자 캐싱하기 시작하면 그 자체가 새 문제가 되는구나
MaxSpeed는 항상 날씨 기준값 × 터널 배율로 계산
날씨가 바뀌든 터널이 바뀌든 둘 다 이 공식으로 다시 계산함
핵심은 순서 독립성
누가 먼저 호출되든 같은 결과로 수렴해야 한다
날씨는 "순수 날씨 기준값"만 별도 변수(WeatherBaseMaxSpeed)에 저장
터널 상태를 기억하는 변수(bIsInTunnelNow) 도입
최종: MaxSpeed = WeatherBaseMaxSpeed × (터널 안이면 0.7)
이 합성을 날씨 콜백과 터널 콜백 양쪽 모두에서 수행
새로운 문제 발견
터널 안에서 비/눈으로 바꾸면 속도가 더 줄어듦
근데 터널 안에서 날씨는 의미가 없음!
터널 안에서 날씨가 속도를 가르는 건 현실과 맞지 않음 -> 설정 오류
날씨가 건드리는 건 MaxSpeed만이 아니고 LateralFriction, DecelRate, MinSpeed, BrakePreviewDist도 전부 날씨별로 바뀜
터널 안 = 날씨가 Clear인 것처럼 취급하는 게 제일 깔끔하다고 판단!
EffectiveWeather 생성
void ApplyWeatherProfile(EWeather Weather)
{
CurrentWeather = Weather; // 실제 날씨 기억 (터널 나갈 때 복귀용)
// 터널 안이면 날씨를 Clear로 간주 — 이 한 줄이 핵심
const EWeather EffectiveWeather = bIsInTunnelNow ? EWeather::Clear : Weather;
// 이하 모든 switch는 EffectiveWeather를 본다
}
분기는 함수 입구 한 곳
그 아래 모든 계산(마찰/속도/감속/Min/Preview)은 EffectiveWeather 하나만 봄
터널 안이면 EffectiveWeather가 Clear니까 전부 자동으로 Clear 처리됨
마지막으로 터널 진입/이탈 시 유효 날씨가 바뀐 셈이므로 OnTunnelToggled 끝에서 ApplyWeatherProfile(CurrentWeather)를 다시 호출하게 했음
터널에 들어가면 실제 날씨가 눈이어도 EffectiveWeather=Clear로 재계산되고, 나오면 기억해둔 CurrentWeather(눈)로 복원됨

알게된 것
1. 덮어쓰기 오류 없애는 건 더 잘 덮어쓰기가 아니라 덮어쓰기 없애기
각 시스템이 변수에 직접 값을 박는 게 아니라 내 기여분을 곱해서 합치는 방식으로 바꾸기
// 덮어쓰기 방식 (나쁨)
MaxSpeed = 120; // 도로
MaxSpeed = 80; // 날씨
// 곱셈 합성 (좋음)
MaxSpeed = BaseSpeed * RoadFactor * WeatherFactor;
// 도로: RoadFactor = 1.2
// 날씨: WeatherFactor = 0.67
이러면 도로가 먼저 계산되든 날씨가 먼저든 최종 곱셈 결과는 같음
-> 순서 독립성(commutativity)
2. 분기는 한 점에 모으기
같은 조건 판단(터널이면 Clear)을 다섯 군데 흩으면 다섯 개의 잠재 버그가 될 수 있음
입구에서 한 번 판단하고 그 결과를 아래가 따르게 하면 모순이 구조적으로 불가능해짐
// 한 곳에서 결정
WeatherContext currentWeather = isInTunnel ? Clear : ActualWeather;
// 아래 시스템들은 currentWeather를 그냥 따른다
friction = ComputeFriction(currentWeather);
visibility = ComputeVisibility(currentWeather);
maxSpeed = ComputeMaxSpeed(currentWeather);
3. 좋은 코드가 항상 좋은 건 아님
// 처음 한 번만 원본 값 저장
if (!cachedBaseline) {
baselineMaxSpeed = MaxSpeed;
cachedBaseline = true;
}
// 이후엔 캐시된 값에서 계산
MaxSpeed = baselineMaxSpeed * weatherFactor;
베이스라인 캐싱은 좋은 패턴이지만 두 시스템이 각자 한 번씩 캐싱하면 문제가 됨
날씨 시스템이 t=0에 캐싱: baselineMaxSpeed = 100 (당시 원본)
그 직후 도로 시스템이 t=0.1에 들어옴: baselineMaxSpeed = 80 (날씨가 이미 한 번 만진 후의 값)
즉, 베이스라인 캐싱은 분명 누적 오염을 막는 좋은 패턴이지만 특정 부분에선 아닐 수 있음
패턴의 안전성은 그 패턴이 사용되는 경계에 따라 달라질 수 있다
'프로젝트 > 자율주행 위험구간 분석 시뮬레이터' 카테고리의 다른 글
| [트러블슈팅] 캐싱·결합·잘못 짚은 변수·가중 지수 (0) | 2026.05.19 |
|---|---|
| 주행 위험 감지 시스템 로직 구성 (0) | 2026.05.19 |
| [트러블슈팅] 날씨 별 속도가 동일, 마찰력 줄어들면 코너에서 추락 (0) | 2026.05.18 |
| 날씨 시스템 구현하기 (0) | 2026.05.17 |
| 터널 시스템 구현하기 (0) | 2026.05.13 |
