JK강재신 / Unity · XR
← 프로젝트 목록

쓰리디아이 / 2023

특전교 낙하산 시뮬레이터

회전 장비에서도 자연스럽게 이어지는 트래킹

Kinect회전 기반 센서 선택팀 리딩
프로젝트 연도 / 기록 당시 상태2023 · 개발 중 이직
개발팀 · 리소스 인력 제외3인 · 팀장
사용 기술Unity3D · C# · VR · Winform

PROJECT IN ACTION

실행 화면과 시연

특전교 낙하산 시뮬레이터 프로젝트 화면
특전교 낙하산 시뮬레이터 · 시연 1 프로젝트 이미지
YouTube / PROJECT VIDEO

특전교 낙하산 시뮬레이터 · 시연 1

직접 열기 ↗

OVERVIEW

프로젝트와 나의 역할

장비 회전 방향에 따른 Kinect 데이터 선택과 자세 Merge/Fade, 개발 일정 관리를 담당했습니다.

  • 회전하는 장비에 대한 키넥트 자세 Merge 기능 개발
  • 프로젝트 고도화 작업 업무 분장 및 일정 관리

ENGINEERING NOTES

기능별 구현과 문제 해결

담당 기능의 배경부터 구현 방식과 선택한 범위까지 정리했습니다.

01

장비 회전에 따라 Kinect 입력 선택

배경과 과제

회전 장비를 세 대의 Kinect로 관측하면서 모든 센서의 자세를 일괄 결합하기 어려웠습니다.

내가 구현한 방식

하드웨어 회전 각도를 이용해 훈련자를 잘 관측하는 센서의 각도 범위를 정하고, 해당 범위의 센서 데이터를 선택했습니다.

선택의 이유와 범위

아래 예제는 이 선택 원리를 새로 작성한 코드로 설명합니다. 전환 임계값과 보간은 2026년 예제에 추가한 설계이며 당시 구현과 구분됩니다.

RECONSTRUCTED EXAMPLE

2026년 포트폴리오용 재구성 코드

장비 회전 각도에 따른 센서 선택을 재현합니다. 전환 임계값과 보간은 이번 예제에 추가한 설계입니다.

당시 제품의 원본 소스가 아닌 새로 작성한 예제입니다. Unity 6000.0.23f1 컴파일 및 Editor 검증 56개 통과. 실제 장비 연동과 제품 통합은 검증 범위에 포함하지 않습니다.

코드 펼쳐보기
// Portfolio reconstruction, 2026. Not original production source.
// Models rotation-dependent sensor selection; no proprietary sensor SDK or data.
using System;
using UnityEngine;

public sealed class RotationSensorSelector : MonoBehaviour
{
    [Serializable] public class Source
    {
        public Transform calibratedPose; // All poses must already share world coordinates.
        public float bestFacingYaw;
        public bool tracked = true; // Set by the sensor adapter, including stale-data handling.
    }
    [SerializeField] Transform rig;
    [SerializeField] Transform output;
    [SerializeField] Source[] sources;
    [SerializeField, Min(0)] float switchMarginDegrees = 12;
    [SerializeField, Min(0.001f)] float blendSeconds = 0.15f;
    int selected = -1;

    bool Valid(int i) => sources != null && i >= 0 && i < sources.Length && sources[i] != null
        && sources[i].tracked && sources[i].calibratedPose != null;

    void LateUpdate()
    {
        if (rig == null || output == null || sources == null) return;
        float yaw = rig.eulerAngles.y;
        int best = -1; float bestError = float.PositiveInfinity;
        for (int i = 0; i < sources.Length; i++)
        {
            if (!Valid(i)) continue;
            float error = Mathf.Abs(Mathf.DeltaAngle(yaw, sources[i].bestFacingYaw));
            if (error < bestError) { best = i; bestError = error; }
        }
        if (best < 0) { selected = -1; return; } // Hold the last pose on complete tracking loss.
        float currentError = Valid(selected)
            ? Mathf.Abs(Mathf.DeltaAngle(yaw, sources[selected].bestFacingYaw)) : float.PositiveInfinity;
        if (!Valid(selected) || bestError + switchMarginDegrees < currentError) selected = best;
        Transform pose = sources[selected].calibratedPose;
        float alpha = 1 - Mathf.Exp(-Time.deltaTime / Mathf.Max(0.001f, blendSeconds));
        output.SetPositionAndRotation(Vector3.Lerp(output.position, pose.position, alpha),
            Quaternion.Slerp(output.rotation, pose.rotation, alpha));
    }
}
02

기존 팀과 고도화 작업 진행

배경과 과제

이전 비상탈출 낙하산 프로젝트에 함께 참여했던 팀원들과 고도화 작업을 진행했습니다.

내가 구현한 방식

업무를 나누고 개발 일정을 관리했습니다.

선택의 이유와 범위

기존 협업 경험을 바탕으로 업무와 일정을 관리했습니다. 참여 종료 시점은 개발 중 이직이며, 이후 완성·운영 상태는 이 포트폴리오의 작업 범위와 구분합니다.

MORE SCREENS

추가 실행 화면

CODE & VALIDATION

코드와 검증 범위

기존 코드 발췌와 2026년 포트폴리오용 재구성 코드를 구분해 제공합니다. 재구성 코드는 실제 제품 소스가 아니며, 일반 원리를 설명하기 위해 새로 작성했습니다.

Unity 6000.0.23f1 컴파일 및 Editor 검증 56개 통과. 실제 장비 연동과 제품 통합은 검증 범위에 포함하지 않습니다.

예제별 사용법과 검증 내용 ↗
다른 프로젝트 살펴보기 ↗