@@ -123,10 +123,18 @@ function updateWormRotation(worm: Worm, targetDirection: { x: number; y: number
123123 }
124124}
125125
126+ /**
127+ * 지렁이의 경로 기록을 초기화합니다.
128+ */
129+ export function initializePositionHistory ( worm : Worm ) : { x : number ; y : number } [ ] {
130+ // 세그먼트 위치를 역순으로 저장 (과거 → 최신 순서)
131+ return worm . segments . map ( ( s ) => ( { x : s . x , y : s . y } ) ) . reverse ( ) ;
132+ }
133+
126134/**
127135 * 지렁이 머리의 위치를 업데이트합니다.
128136 */
129- function updateWormHead ( worm : Worm , deltaTime : number ) : void {
137+ function updateWormHead ( worm : Worm , deltaTime : number , positionHistory : { x : number ; y : number } [ ] ) : void {
130138 const dirX = worm . direction . x ;
131139 const dirY = worm . direction . y ;
132140 const magnitude = Math . sqrt ( dirX * dirX + dirY * dirY ) ;
@@ -144,27 +152,67 @@ function updateWormHead(worm: Worm, deltaTime: number): void {
144152 head . x += normalizedDirX * speed * deltaTime ;
145153 head . y += normalizedDirY * speed * deltaTime ;
146154 }
155+
156+ // 머리의 새 위치를 경로 기록에 추가 (배열 끝 = 최신)
157+ const head = worm . segments [ 0 ] ;
158+ positionHistory . push ( { x : head . x , y : head . y } ) ;
147159}
148160
149161/**
150- * 지렁이의 몸통 세그먼트들이 머리를 따라오도록 업데이트합니다.
162+ * 지렁이의 몸통 세그먼트들이 머리의 경로를 따라가도록 업데이트합니다.
163+ * 경로 기록을 역순(최신→과거)으로 걸어가며 각 세그먼트를 i × SEGMENT_SPACING 거리 지점에 배치합니다.
151164 */
152- function updateWormSegments ( worm : Worm ) : void {
153- // 세그먼트들 따라오게 하기
154- for ( let i = 1 ; i < worm . segments . length ; i ++ ) {
155- const prev = worm . segments [ i - 1 ] ;
156- const curr = worm . segments [ i ] ;
157-
158- const dx = prev . x - curr . x ;
159- const dy = prev . y - curr . y ;
160- const distance = Math . hypot ( dx , dy ) ;
161-
162- if ( distance > GAME_CONSTANTS . SEGMENT_SPACING ) {
163- const moveX = ( dx / distance ) * ( distance - GAME_CONSTANTS . SEGMENT_SPACING ) ;
164- const moveY = ( dy / distance ) * ( distance - GAME_CONSTANTS . SEGMENT_SPACING ) ;
165-
166- curr . x += moveX ;
167- curr . y += moveY ;
165+ function updateWormSegments ( worm : Worm , positionHistory : { x : number ; y : number } [ ] ) : void {
166+ if ( positionHistory . length < 2 ) return ;
167+
168+ let segmentIndex = 1 ;
169+ let targetDistance = GAME_CONSTANTS . SEGMENT_SPACING ;
170+ let accumulatedDistance = 0 ;
171+
172+ // 경로 기록을 역순(최신→과거)으로 순회
173+ for ( let j = positionHistory . length - 1 ; j > 0 && segmentIndex < worm . segments . length ; j -- ) {
174+ const p1 = positionHistory [ j ] ; // 최신 쪽
175+ const p2 = positionHistory [ j - 1 ] ; // 과거 쪽
176+ const dx = p2 . x - p1 . x ;
177+ const dy = p2 . y - p1 . y ;
178+ const pathSegmentLength = Math . hypot ( dx , dy ) ;
179+
180+ // 이 경로 구간 내에 배치할 수 있는 세그먼트가 있는지 확인
181+ while ( segmentIndex < worm . segments . length && accumulatedDistance + pathSegmentLength >= targetDistance ) {
182+ const remaining = targetDistance - accumulatedDistance ;
183+ const t = pathSegmentLength > 0 ? remaining / pathSegmentLength : 0 ;
184+
185+ worm . segments [ segmentIndex ] . x = p1 . x + dx * t ;
186+ worm . segments [ segmentIndex ] . y = p1 . y + dy * t ;
187+
188+ segmentIndex ++ ;
189+ targetDistance = segmentIndex * GAME_CONSTANTS . SEGMENT_SPACING ;
190+ }
191+
192+ accumulatedDistance += pathSegmentLength ;
193+ }
194+
195+ // 경로가 부족한 경우 남은 세그먼트는 경로의 가장 오래된 위치에 배치
196+ if ( segmentIndex < worm . segments . length ) {
197+ const lastPoint = positionHistory [ 0 ] ;
198+ for ( let i = segmentIndex ; i < worm . segments . length ; i ++ ) {
199+ worm . segments [ i ] . x = lastPoint . x ;
200+ worm . segments [ i ] . y = lastPoint . y ;
201+ }
202+ }
203+
204+ // 경로 기록 트리밍 - 필요한 길이 + 여유분만 유지
205+ const maxNeededDistance = worm . segments . length * GAME_CONSTANTS . SEGMENT_SPACING ;
206+ let totalDist = 0 ;
207+ for ( let j = positionHistory . length - 1 ; j > 0 ; j -- ) {
208+ totalDist += Math . hypot (
209+ positionHistory [ j ] . x - positionHistory [ j - 1 ] . x ,
210+ positionHistory [ j ] . y - positionHistory [ j - 1 ] . y ,
211+ ) ;
212+ if ( totalDist > maxNeededDistance + GAME_CONSTANTS . SEGMENT_SPACING * 2 ) {
213+ // j-1 이전의 기록은 불필요하므로 제거
214+ positionHistory . splice ( 0 , j - 1 ) ;
215+ break ;
168216 }
169217 }
170218}
@@ -176,18 +224,22 @@ function updateSingleWorm(
176224 worm : Worm ,
177225 deltaTime : number ,
178226 targetDirections : Map < string , { x : number ; y : number } > ,
227+ positionHistories : Map < string , { x : number ; y : number } [ ] > ,
179228) : void {
180229 // 부드러운 회전 로직 적용
181230 const targetDirection = targetDirections . get ( worm . id ) ;
182231 if ( targetDirection ) {
183232 updateWormRotation ( worm , targetDirection , deltaTime ) ;
184233 }
185234
186- // 머리 위치 업데이트
187- updateWormHead ( worm , deltaTime ) ;
235+ const positionHistory = positionHistories . get ( worm . id ) ;
236+ if ( ! positionHistory ) return ;
237+
238+ // 머리 위치 업데이트 + 경로 기록 추가
239+ updateWormHead ( worm , deltaTime , positionHistory ) ;
188240
189- // 몸통 세그먼트들 업데이트
190- updateWormSegments ( worm ) ;
241+ // 몸통 세그먼트들 업데이트 (경로 기반)
242+ updateWormSegments ( worm , positionHistory ) ;
191243}
192244
193245/**
@@ -204,6 +256,7 @@ export function updateWorld(
204256 foods : Map < string , Food > ,
205257 targetDirections : Map < string , { x : number ; y : number } > ,
206258 botMovementStrategies : Map < string , MovementStrategy > ,
259+ positionHistories : Map < string , { x : number ; y : number } [ ] > ,
207260) : void {
208261 const allWorms = Array . from ( worms . values ( ) ) ;
209262
@@ -214,7 +267,7 @@ export function updateWorld(
214267 }
215268
216269 // 개별 지렁이 상태 업데이트
217- updateSingleWorm ( worm , deltaTime , targetDirections ) ;
270+ updateSingleWorm ( worm , deltaTime , targetDirections , positionHistories ) ;
218271 }
219272}
220273
@@ -294,6 +347,7 @@ function removeDeadPlayer(
294347 playerId : string ,
295348 worms : Map < string , Worm > ,
296349 targetDirections : Map < string , { x : number ; y : number } > ,
350+ positionHistories : Map < string , { x : number ; y : number } [ ] > ,
297351) : void {
298352 const worm = worms . get ( playerId ) ;
299353 if ( worm && worm . isDead && worm . type === WormType . Player ) {
@@ -302,6 +356,7 @@ function removeDeadPlayer(
302356 // 플레이어 상태 제거
303357 worms . delete ( playerId ) ;
304358 targetDirections . delete ( playerId ) ;
359+ positionHistories . delete ( playerId ) ;
305360 }
306361}
307362
@@ -313,15 +368,16 @@ export function handleKilledWorms(
313368 worms : Map < string , Worm > ,
314369 targetDirections : Map < string , { x : number ; y : number } > ,
315370 botMovementStrategies : Map < string , MovementStrategy > ,
371+ positionHistories : Map < string , { x : number ; y : number } [ ] > ,
316372) : string [ ] {
317373 const removedPlayerIds : string [ ] = [ ] ;
318374
319375 for ( const [ wormId , worm ] of worms ) {
320376 if ( worm . isDead ) {
321377 if ( worm . type === WormType . Bot ) {
322- respawnBot ( wormId , worms , targetDirections , botMovementStrategies ) ;
378+ respawnBot ( wormId , worms , targetDirections , botMovementStrategies , positionHistories ) ;
323379 } else if ( worm . type === WormType . Player ) {
324- removeDeadPlayer ( wormId , worms , targetDirections ) ;
380+ removeDeadPlayer ( wormId , worms , targetDirections , positionHistories ) ;
325381 removedPlayerIds . push ( wormId ) ;
326382 }
327383 }
@@ -335,6 +391,7 @@ function respawnBot(
335391 worms : Map < string , Worm > ,
336392 targetDirections : Map < string , { x : number ; y : number } > ,
337393 botMovementStrategies : Map < string , MovementStrategy > ,
394+ positionHistories : Map < string , { x : number ; y : number } [ ] > ,
338395) : void {
339396 // 기존 봇 데이터 제거
340397 const bot = worms . get ( botId ) ;
@@ -344,6 +401,7 @@ function respawnBot(
344401 worms . delete ( botId ) ;
345402 targetDirections . delete ( botId ) ;
346403 botMovementStrategies . delete ( botId ) ;
404+ positionHistories . delete ( botId ) ;
347405
348406 // 새로운 랜덤 타입의 봇 생성
349407 const numericBotTypes = Object . values ( BotType ) . filter ( ( v ) => typeof v === "number" ) as BotType [ ] ;
@@ -356,6 +414,7 @@ function respawnBot(
356414 worms . set ( botId , newBot ) ;
357415 targetDirections . set ( botId , { x : newBot . direction . x , y : newBot . direction . y } ) ;
358416 botMovementStrategies . set ( botId , createMovementStrategy ( botType ) ) ;
417+ positionHistories . set ( botId , initializePositionHistory ( newBot ) ) ;
359418
360419 console . log ( `🤖 Bot ${ botId } respawned as type ${ botType } ` ) ;
361420}
0 commit comments