← Back to Portal Hub

로블록스 수중 물리 & 선박 부력: 게르스트너 파도, 유체 항력 및 보트 시뮬레이션 아키텍처

By 도파브레인 스튜디오 엔지니어링 팀 • 2026-09-30 • Technical Guide

로블록스의 기본 지형(Terrain) 수면은 아바타의 기초적인 수영과 플로팅을 지원하지만, 사실적인 해양 시뮬레이션, 고속 모터보트, 범선, 해상 전투 게임을 구현하기에는 물리적 한계가 큽니다. 기본 부력은 선박의 제어 불가능한 요동, 전복 버그, 움직이는 파도와의 비동기 문제를 야기합니다.

선체 각 모서리에 다지점 아르키메데스 부력 시스템을 구축하면 선체의 각 부분에 가해지는 양력을 독립적으로 계산할 수 있습니다. Luau 기반의 해석적 게르스트너(Gerstner) 파도 공식, 2차 유체 항력 텐서, AngularVelocity 감쇠 제어를 결합하면 파도의 고저차에 맞춰 자연스럽게 피치(Pitch)와 롤(Roll)을 형성하며 흔들리지 않는 조작감을 제공합니다.

1. 기본 지형 수면의 한계와 커스텀 선박 물리 엔진의 필요성

로블록스 기본 수면이 복잡한 해양 이동체에서 실패하는 구조적 이유:

2. 다지점 아르키메데스 부력 & 게르스트너 파도 샘플링

선체 분할 샘플링 포인트를 활용한 정밀 양력 연산:

Luau 다지점 게르스트너 파도 부력 및 유체 감쇠 물리 컨트롤러
--!strict
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")

local BoatPhysics = {}

-- 게르스트너 파도 구성 요소: {방향벡터, 첨두도, 파장, 전파속도}
local WAVES = {
    {Dir = Vector2.new(1, 0).Unit, Steepness = 0.4, Length = 60, Speed = 12},
    {Dir = Vector2.new(0.7, 0.7).Unit, Steepness = 0.25, Length = 35, Speed = 9},
    {Dir = Vector2.new(-0.3, 0.9).Unit, Steepness = 0.15, Length = 18, Speed = 7}
}

local GRAVITY = Workspace.Gravity
local WATER_DENSITY = 1.025
local POINT_SUBMERGED_VOLUME = 850 -- 각 포인트별 가상 배수 체적
local DAMPING = 280

-- 월드 X, Z 좌표에서 현재 시간 기준 수면 높이 Y 계산
function BoatPhysics.GetWaveHeight(worldX: number, worldZ: number, timeVal: number): number
    local elevation = 0
    for _, wave in ipairs(WAVES) do
        local k = (2 * math.pi) / wave.Length
        local c = wave.Speed
        local dot = wave.Dir.X * worldX + wave.Dir.Y * worldZ
        local phase = k * (dot - c * timeVal)
        local amplitude = wave.Steepness / k
        elevation += math.sin(phase) * amplitude
    end
    return elevation
end

-- 선체 Attachment들의 부력 및 유체 저항 실시간 업데이트
function BoatPhysics.UpdateBuoyancy(hullRoot: BasePart, sampleAttachments: {Attachment}, vectorForces: {VectorForce})
    local t = Workspace:GetServerTimeNow()
    
    for i, attach in ipairs(sampleAttachments) do
        local worldPos = attach.WorldPosition
        local waterY = BoatPhysics.GetWaveHeight(worldPos.X, worldPos.Z, t)
        local depth = waterY - worldPos.Y
        
        local vf = vectorForces[i]
        if depth > 0 then
            -- 수면 아래 침수: 부력 및 수직 감쇠력 적용
            local pointVelocity = hullRoot:GetVelocityAtPosition(worldPos)
            local upwardForce = (depth * POINT_SUBMERGED_VOLUME * GRAVITY * WATER_DENSITY) - (pointVelocity.Y * DAMPING)
            upwardForce = math.clamp(upwardForce, 0, 80000)
            
            -- 수평 방향 물 저항(횡미끄러짐 억제)
            local horizontalDrag = -Vector3.new(pointVelocity.X, 0, pointVelocity.Z) * 45
            vf.Force = Vector3.new(horizontalDrag.X, upwardForce, horizontalDrag.Z)
        else
            -- 수면 위: 부력 제로
            vf.Force = Vector3.zero
        end
    end
end

return BoatPhysics

3. 비등방성 유체 항력 텐서와 선체 유체역학

선체 형태에 따른 물의 저항력을 수학적으로 모델링하는 기법:

4. 네트워크 소유권 및 래그 없는 멀티플레이어 동기화

운전자와 관전자 모두에게 완벽한 물리 동기화를 보장하는 네트워크 설계:

5. 상용 프로덕션 최적화 체크리스트

로블록스 스튜디오에서 버그 없는 선박 시스템을 출시하기 위한 핵심 원칙:

Frequently Asked Questions

지형 수면 레이캐스트 대신 수학적 게르스트너 파도를 사용하는 이유는 무엇인가요?

로블록스 기본 지형 수면은 정적이며 3D 파도로 변형되지 않습니다. 해석적 게르스트너 파도를 사용하면 지오메트리 쿼리 없이 서버와 클라이언트 모두에서 무한한 대양 파도를 가볍고 정확하게 동기화할 수 있습니다.

보트 하나에 몇 개의 부력 샘플링 포인트가 가장 적절한가요?

모터보트나 일반 크기 선박은 선수 좌/우, 선미 좌/우 4개 포인트로도 충분히 안정적인 피치 및 롤 복원력을 냅니다. 거대 군함이나 화물선은 6~8개 포인트를 사용하여 여러 파도 봉우리에 걸치는 물리 현상을 구현합니다.

파도에 고속으로 부딪힐 때 배가 하늘로 튀어 오르는 버그는 어떻게 막나요?

프레임당 인가되는 최대 상향 부력을 클램핑(Clamp)하고, 선체가 수면을 완전히 벗어났을 때는 즉시 부력을 0으로 설정하며 공기 저항을 인가해야 합니다.

Explore More Interactive Tests & Guides

Discover personalized cognitive assessments, stress evaluations, and game psychology tools on DopaBrain.

Go to Portal Hub