Week 03 / Period 02 / Runtime motion

실행 순서와 시간 기반 움직임

Unity의 메서드 호출 순서에 따라 Inspector 설정값과 프레임 간격을 사용하여 이동량과 회전량을 계산하는 과정을 확인합니다.

60분 / 실행 원리 / AI와 이동 작성 / Unity 검증

산호색 오브젝트가 초기화된 뒤 네 개의 시간 프레임을 따라 일정하게 이동하고 회전하는 장면
기기의 프레임률이 달라도 이동 속도를 유지하려면 초당 속도에 프레임 간격을 곱해 이동량을 계산합니다.
이 페이지에서
핵심 질문
프레임률이 달라도 이동 속도를 유지하는 방법은?
학습 방식
코드 추적, 값 변경, 결과 관찰
연결 개념
필드, 메서드, if
새 개념
생명주기 · 직렬화 · deltaTime
관찰 기준
각 필드를 사용하는 메서드와 실행 결과
다음 교시
같은 구조로 세 움직임을 구현
학습 목표 Inspector 설정값이 움직임에 반영되는 과정

1교시에는 MotionController.cs에서 속도와 회전 허용 여부를 저장하고, Start가 호출한 PrintStartMessage로 설정을 출력했습니다. 1교시에서 완성한 파일과 씬을 이어서 사용합니다. 현재 Update에는 실행할 코드가 없으므로 게임오브젝트가 움직이지 않습니다. 이번 교시에는 Inspector 값 → Update 호출 → 시간 계산 → Transform 변화를 따라 이동, 회전, 회전 조건, 경계 재시작을 하나씩 추가합니다. 각 기능의 예상 결과와 실행 결과를 비교한 뒤 다음 기능을 구현합니다.

  1. 01생명주기와 Console

    Start와 Update의 호출 횟수를 Debug.Log로 확인합니다.

  2. 02Inspector 직렬화

    코드 기본값과 인스턴스별 저장값의 차이를 확인합니다.

  3. 03시간 기반 변화

    프레임 이동량과 초당 속도의 차이를 계산합니다.

  4. 04기능을 하나씩 추가

    AI와 이동을 작성한 뒤 회전, 조건, 경계를 차례로 구현하고 검증합니다.

  5. 05값 예측과 점검

    필드 값을 변경하기 전에 실행 결과를 예측하고 관련 코드를 확인합니다.

60분 학습 흐름과 단계별 완료 기준
시간활동확인할 결과
0~5분1교시 파일과 씬 확인기존 설정 출력, 빈 Update, 컴포넌트 하나
5~12분Start·Update 로그 비교출력 줄 수와 호출 횟수를 구분
12~20분Inspector 저장값 실험코드 기본값과 실제 사용값을 구분
20~32분시간·2D 좌표·이동 구현계산한 이동량을 Position 변화와 연결
32~42분회전과 조건 추가이동은 유지하고 회전만 제어
42~52분경계 처리와 한 프레임 추적경계를 넘으면 X만 재배치
52~60분값 예측 실험과 정리관찰 결과를 필드와 메서드로 설명

Unity는 생명주기에 따라 메서드를 호출합니다

1교시의 실행 경로는 Unity → Start() → PrintStartMessage()였습니다. Start와 Update는 Unity가 정해진 시점에 호출하는 메서드입니다. PrintStartMessage는 Start 내부의 코드에서 호출합니다. 두 호출 방식의 차이를 바탕으로 실행 시점과 횟수를 비교합니다.

Unity가 자동으로 호출하는 메서드는 이름과 대소문자를 정확히 작성해야 합니다. start나 START는 Start로 인식되지 않습니다. 문법 오류가 없지만 코드가 실행되지 않는 경우 메서드 이름과 대소문자, 매개변수 형식을 확인합니다.

Unity 신호 장치가 Start에는 시작 신호 한 번을, Update에는 F1부터 F5까지 프레임마다 신호를 보내는 두 줄의 시간선
Unity는 첫 Update() 전에 Start()를 한 번 호출합니다. 활성화된 컴포넌트의 Update()는 프레임마다 호출됩니다. 초기화 작업과 매 프레임 갱신할 작업을 구분합니다.
핵심 개념

Unity는 Start와 Update를 정해진 시점에 호출합니다. 각 메서드 안에 이동·회전 등 필요한 기능의 호출 코드를 작성합니다.

  1. Awake()초기화 시 한 번

    컴포넌트 인스턴스가 초기화될 때 한 번 호출됩니다. 다른 컴포넌트의 참조나 내부 상태를 초기화하는 데 사용합니다. 이번 완성 코드에서는 사용하지 않습니다.

  2. OnEnable()활성화될 때마다

    게임오브젝트와 컴포넌트가 활성 상태가 될 때 호출됩니다. 비활성화 후 다시 활성화할 때도 호출됩니다. 이번 교시에서는 호출 시점만 확인합니다.

  3. Start()첫 Update 전에 한 번

    활성화된 컴포넌트의 첫 Update 전에 한 번 호출됩니다. 시작 로그 출력이나 초기 위치 저장 등 한 번만 필요한 준비 작업을 작성합니다.

  4. Update()Play 중 매 프레임

    활성화된 컴포넌트에서 매 프레임 호출됩니다. 이동, 회전, 경계 검사 등 반복해서 갱신할 작업을 작성합니다.

이번 실습에서는 Start와 Update를 사용합니다. Start에 이동 코드를 작성하면 실행 시작 시 한 번만 이동합니다. Update에 로그 출력을 작성하면 매 프레임 같은 메시지가 반복 출력됩니다. 한 번 실행할 작업과 반복할 작업을 적절한 메서드에 배치하는 것이 생명주기에 맞게 코드를 구성하는 기준입니다.

Start · once

한 번 실행할 준비 작업

초기 위치 저장, 초기 상태 설정, 시작 로그 출력 등 한 번만 필요한 작업입니다. 현재 파일은 PrintStartMessage를 한 번 호출하여 여러 줄을 출력합니다. 출력 줄 수와 메서드 호출 횟수는 다릅니다.

Update · repeated

매 프레임 갱신할 작업

위치 이동, 회전, 조건 검사 등 매 프레임 결과를 갱신해야 하는 작업입니다. 이동 구현 후에는 Inspector의 Transform Position X가 계속 증가하는지 확인하여 반복 실행 결과를 관찰합니다.

실행하기 전에 호출을 예측합니다

Start에서 로그 두 줄을 출력할 때의 호출 횟수와, 같은 출력문을 Update에 작성할 때의 차이를 예측합니다. 예상 결과를 기록하고 다음 Console 실습에서 확인합니다. update()처럼 이름을 바꾸면 Unity가 자동으로 호출하지 않는다는 점도 구분합니다.

생명주기 실행 시작 시 초기 위치를 저장하는 코드는 Start에, 이동을 반복하는 코드는 Update에 작성합니다. 실행 중 사용할 값을 변수에 저장하는 것과 씬 파일을 저장하는 것은 구분합니다.

Debug.Log로 호출 횟수와 값을 확인합니다

1교시 완료 코드에는 moveSpeed와 shouldRotate 필드, 그리고 출력 메서드가 이미 있습니다. 기존 스크립트에서 시작 메시지의 출력이 종료되는 시점과 Update 출력이 반복되는 상태를 비교합니다. 파일이 다르면 1교시 완료 코드와 일치시킨 뒤 진행합니다.

  1. 기존 시작 메시지 확인

    대상 하나의 Inspector에서 Move Speed를 2, Should Rotate를 켠 상태로 둡니다. Console의 Clear를 누르고 Collapse를 끈 뒤 Play를 눌러 실행합니다. 이름, moveSpeed=2, 회전 허용 메시지까지 세 줄이 한 번씩 출력되는지 확인합니다. 세 줄은 PrintStartMessage를 세 번 호출했다는 뜻이 아닙니다. 한 번 호출된 메서드 안에서 세 번 출력한 결과입니다.

    관찰점Start → PrintStartMessage는 한 번 실행되고, 현재 대상은 아직 움직이지 않습니다.
  2. 출력되는 필드 값 비교

    Play를 종료하고 Inspector의 Move Speed를 5로 바꿉니다. 다시 Clear로 로그를 지운 뒤 Play를 누르면 기존 출력문의 결과가 moveSpeed=5로 바뀝니다. 코드의 기본값은 2로 유지했지만 이 인스턴스의 Inspector 값은 5입니다. 확인 후 Play를 종료하고 Inspector 값을 2로 되돌립니다. 기존 출력문을 그대로 사용합니다.

    관찰점Console에 출력되는 값은 실행 중인 인스턴스의 필드 값입니다.
  3. Update에 관찰용 로그 추가

    기존 Update 안에 Debug.Log("Update"); 한 줄을 추가하고 저장합니다. 아래 코드는 Start와 Update의 위치를 비교하기 위한 것입니다. 필드와 PrintStartMessage 정의는 유지합니다. Clear로 로그를 지운 뒤 Play를 누르면 처음의 설정 메시지에 이어 Update라는 메시지가 반복 출력됩니다.

    비교시작 시 한 번 실행하는 작업과 매 프레임 반복하는 작업을 구분합니다.
  4. 관찰용 Update 로그 삭제

    Play를 종료하고 Update에 추가한 로그 한 줄을 삭제하고 저장합니다. Start의 PrintStartMessage(); 호출과 출력 메서드는 유지합니다. 씬도 저장합니다. 이동 실습 전에는 Update가 다시 비어 있어야 합니다.

    정리1교시 완료 상태를 복원한 뒤 Inspector 저장값과 시간 계산을 확인합니다.
관찰 중의 Start / Update · 필드와 출력 메서드는 유지
private void Start()
{
    PrintStartMessage();
}

private void Update()
{
    Debug.Log("Update");
}

출력한 줄의 위치를 확인합니다

Console 메시지를 더블클릭하면 코드 편집기에서 해당 출력 줄을 찾을 수 있습니다. 이름과 속도는 PrintStartMessage 안에서, 반복 메시지는 Update 안에서 출력되었는지 확인합니다. 코드 수정은 Play를 종료한 뒤 저장하고 Unity의 컴파일이 끝나면 확인합니다.

Inspector에서 인스턴스별 설정값을 관리합니다

1교시에는 같은 스크립트를 여러 게임오브젝트에 추가하면 각각의 컴포넌트 인스턴스가 생성됨을 확인했습니다. 속도와 같은 설정값은 인스턴스별로 저장됩니다. 코드의 = 2f는 새로 추가한 컴포넌트의 초기값입니다. 기존 인스턴스의 현재 값은 Inspector에 표시되며, 씬이나 Prefab 파일에 함께 저장됩니다.

직렬화(serialization)는 필드 값을 저장하거나 복원할 수 있는 데이터로 변환하는 과정입니다. Inspector에서는 직렬화 대상 필드를 확인하고 수정할 수 있습니다. 따라서 같은 코드를 사용하는 인스턴스마다 다른 설정값을 유지할 수 있습니다. 3교시에서는 각 인스턴스의 저장값을 조절하여 서로 다른 움직임을 구현합니다.

수업 기본 선언
[SerializeField] private float moveSpeed = 2f;
[SerializeField] private bool shouldRotate = true;
01초기값 지정

필드 선언에서 새 컴포넌트의 초기값을 지정합니다.

02값 저장

Unity가 지원하는 필드 값을 씬 또는 Prefab에 저장합니다.

03값 조절

코드 수정 없이 Inspector에서 값을 조절합니다.

04인스턴스별 적용

각 컴포넌트가 자신의 저장값으로 같은 코드를 실행합니다.

수업 기본 형태는 [SerializeField] private입니다. Inspector에서 값을 조절하면서 다른 클래스의 직접 접근을 제한합니다. public float moveSpeed;도 Inspector에 표시되지만, 다른 클래스에서도 직접 접근할 수 있습니다.

public 필드

public float moveSpeed;

Inspector에 표시되며 다른 클래스에서도 필드 값을 직접 읽거나 변경할 수 있습니다. 이번 주에는 사용하지 않습니다.

수업 기본 선언

[SerializeField] private float moveSpeed;

Inspector에서 값을 조절할 수 있으며, 다른 클래스의 직접 접근은 제한합니다.

코드 기본값보다 저장된 Inspector 값이 우선할 수 있습니다

컴포넌트를 추가한 뒤 Inspector에서 moveSpeed를 5로 저장했다면 그 인스턴스는 5를 사용합니다. 이후 코드의 기본값을 3으로 바꾸어도 이미 직렬화된 값이 자동으로 3으로 바뀌지 않을 수 있습니다. 코드의 초기값을 수정해도 실행 결과가 변하지 않는 경우 Inspector의 현재 값을 확인하고 필요한 값을 입력합니다.

코드 기본값이 2에서 3으로 바뀌는 전후 비교. 기존 인스턴스의 Inspector에 꽂힌 저장 카드는 두 장면 모두 5를 유지함
설계서의 기본값을 2에서 3으로 변경해도, 이미 저장된 설정 카드가 5라면 그 인스턴스는 5를 사용합니다. 실행에 사용되는 값은 해당 컴포넌트의 Inspector에서 확인합니다.

Play Mode의 값 변경은 실행 중에만 적용됩니다

Play 중 컴포넌트의 필드를 바꾸면 실행 중인 인스턴스의 값이 바뀌고, Play를 종료하면 그 변경은 되돌아갑니다. 현재는 이동 코드가 없어 게임오브젝트가 움직이지 않으며, Start의 로그도 다시 출력되지 않습니다. 이후 Update에서 속도를 읽도록 구현하면 변경이 움직임에 즉시 반영됩니다. 최종 값은 Edit Mode에서 맞춘 뒤 Ctrl/Cmd + S로 씬을 저장합니다.

코드 기본값과 저장값을 직접 비교합니다

  1. Inspector에는 5를 저장

    Play를 종료한 상태에서 Move Speed를 5로 바꾸고 씬을 저장합니다. 코드의 기본값은 아직 2입니다. 다음 실행에서 출력될 값을 미리 기록합니다.

    예측기본값 2와 저장값 5 중 어느 값이 사용됩니까?
  2. 코드 기본값만 3으로 변경

    moveSpeed = 2f;의 초기값만 3f로 변경하고 저장합니다. 컴파일 후 Inspector의 값을 확인하고, Clear로 로그를 지운 뒤 Play를 눌러 moveSpeed=5가 출력되는지 확인합니다. 새 컴포넌트를 추가하지 않습니다.

    비교코드 3 / Inspector 5 / Console 5를 나란히 기록합니다.
  3. 실행 중 변경과 저장을 구분

    Play 중 Inspector의 Move Speed를 4로 변경합니다. Start에서 이미 출력한 로그의 값은 5로 유지됩니다. Play를 종료한 뒤 Move Speed가 저장값 5로 복원되는지 확인합니다. 실험 후 코드 기본값과 Edit Mode의 Move Speed를 모두 2로 되돌리고 파일과 씬을 저장합니다.

    완료로그는 출력 당시의 값이며, 다음 실습은 기본값과 저장값 모두 2에서 시작합니다.

속도는 1초 동안의 이동량으로 정의합니다

기기마다 1초 동안 처리하는 프레임 수가 다릅니다. 30 FPS 환경에서는 Update가 초당 약 30번, 60 FPS 환경에서는 약 60번 호출됩니다. 매 프레임 같은 거리를 이동하면 프레임률이 높은 환경에서 1초 동안 더 멀리 이동합니다.

프레임률이 달라도 이동 속도를 유지하려면 초당 속도에 해당 프레임의 시간 간격을 곱합니다. 각 프레임의 이동량을 경과 시간에 맞춰 계산하므로 같은 시간 동안 이동한 거리를 일정하게 유지할 수 있습니다.

고정된 프레임당 이동량

distance = speed

매 프레임 2 unit씩 이동하면 30 FPS에서는 1초에 60 unit, 60 FPS에서는 120 unit을 이동합니다. 같은 이동량을 적용해도 초당 이동 거리는 두 배 차이가 발생합니다.

시간에 비례하는 이동량

distance = speed × deltaTime

각 프레임의 이동량을 합하면 총 이동 거리는 초당 속도와 전체 경과 시간의 곱에 해당합니다. 이번 실습의 이동량과 회전량은 이 방식으로 계산합니다.

같은 1초 동안의 이동을 위쪽은 1 unit 네 조각, 아래쪽은 0.5 unit 여덟 조각으로 나누어 두 줄 모두 총 4 unit에 도달하는 모형
프레임별 이동량을 비교하기 위해 4 FPS와 8 FPS 환경으로 단순화한 그림입니다. 속도가 4 unit/s이면 4 × 0.25 = 1, 4 × 0.125 = 0.5만큼씩 이동하고, 두 경우 모두 1초 동안의 총 이동 거리는 4 unit입니다. 실제 프레임 간격은 달라질 수 있습니다.
프레임률별 시간 기반 이동 비교
환경한 프레임 시간프레임 이동량1초 총거리
30 FPS약 0.033초2 × 0.033 ≈ 0.066약 2 unit
60 FPS약 0.0167초2 × 0.0167 ≈ 0.033약 2 unit

같은 식으로 프레임당 이동량을 계산합니다. 속도가 4 unit/s이고 이번 프레임의 deltaTime이 0.25초라면, 이번 프레임 이동량은 4 × 0.25 = 1 unit입니다. 0.25초 간격의 프레임 네 개를 합하면 1초 동안 총 4 unit을 이동합니다.

이번 프레임의 변화량 = 초당 변화량 × Time.deltaTime

프레임 간격이 달라도 총거리를 계산할 수 있습니다

시작 X는 0, 속도는 2 unit/s입니다. 세 프레임의 시간 간격이 각각 0.1초, 0.3초, 0.6초일 때 각 프레임의 이동량을 계산합니다. 이번 위치 = 이전 위치 + 이번 이동량으로 세 번째 프레임의 X까지 계산합니다. 계산 결과를 확인하기 전에 프레임별 이동량 세 개와 최종 X 좌표를 기록합니다.

계산 확인: 세 프레임 뒤의 X

이동량은 0.2, 0.6, 1.2 unit이고, X는 0.2 → 0.8 → 2.0입니다. 간격이 고르지 않아도 합이 1초이므로 총 2 unit 이동합니다. 계산을 단순화하기 위해 긴 프레임 간격을 사용한 예제입니다. 2 × deltaTime은 현재 위치가 아니라 해당 프레임에서 더할 이동량입니다.

Time.deltaTime은 프레임 사이의 시간 간격입니다

이전 프레임에서 현재 프레임까지 경과한 시간을 초 단위로 나타냅니다. 프레임마다 조금씩 달라질 수 있습니다. Time.time은 게임 시작 후 경과한 시간을 나타내므로 역할이 다릅니다. 프레임별 이동량 계산에는 deltaTime을 사용합니다.

시간 초당 속도에 프레임 간격을 곱해 이동량을 계산하면 프레임률이 달라도 같은 시간 동안 일정한 거리를 이동합니다.

Translate와 Rotate는 Transform의 상태를 바꿉니다

2주차에는 Transform이 위치, 회전, 크기를 저장하는 컴포넌트임을 확인했습니다. 이번 실습에서는 스크립트로 Transform의 값을 직접 변경합니다. 힘이나 충돌에 따른 물리 계산은 사용하지 않습니다. 다음 주에는 Rigidbody2D를 사용한 물리 기반 이동과 차이를 비교합니다.

방향, 초당 속도, 프레임 간격을 하나의 식으로 연결하여 이동량을 계산합니다. 이동은 월드의 +X 방향으로 적용하고, 회전은 스프라이트의 로컬 Z축을 기준으로 적용합니다.

2D 이동에서 Vector3를 사용하는 이유

Unity의 Transform은 2D에서도 (x, y, z)를 사용하고, transform.position의 자료형은 Vector3입니다. 이번 장면은 X·Y 평면에서 움직이므로 이동량의 Z를 0으로 두어 기존 Z 위치를 유지합니다. 평면상의 입력이나 Rigidbody2D 이동에는 Vector2를 사용하지만, 이번에는 Transform API에 맞춰 Vector3를 사용합니다.

현재 위치와 이번 프레임 이동량 구분
역할값해석
현재 위치(2, 1, 0)현재 좌표는 X=2, Y=1입니다.
이동 방향Vector3.right = (1, 0, 0)오른쪽을 향합니다. 현재 위치가 아닙니다.
이번 이동량(1, 0, 0) × 2 × 0.25 = (0.5, 0, 0)X 방향으로 0.5만큼 더합니다.
이동 뒤 위치(2.5, 1, 0)월드 좌표계를 기준으로 X 좌표만 증가합니다.

transform.Translate(이동량, Space.World)는 현재 위치에 이동량을 더합니다. transform.position = 새위치;는 지정한 위치로 변경합니다. 연속 이동에는 Translate를 사용하고, 경계를 넘어 왼쪽으로 재배치할 때는 position에 새 좌표를 대입합니다.

Vector3.right

방향

(1, 0, 0) 방향 벡터입니다. 이번 Translate에서는 Space.World와 함께 사용하여 월드의 +X 방향을 나타냅니다. 속도와 시간을 곱하면 이번 프레임의 이동 벡터가 됩니다.

Space.World

기준 좌표계

오브젝트의 회전 상태와 관계없이 월드의 +X 방향으로 이동합니다. 이번 장면에서는 화면의 오른쪽에 해당합니다. 오브젝트의 로컬 X축을 기준으로 이동하면 회전 각도에 따라 이동 방향도 달라집니다.

Vector3.forward

2D 회전축

X·Y 평면에 수직인 Z축 방향 (0, 0, 1)입니다. Rotate에서는 이동 방향이 아니라 회전축을 뜻합니다. 종이 가운데 핀을 꽂고 돌릴 때 핀이 이 축에 해당합니다. x나 y를 축으로 돌리면 Sprite가 종이처럼 뒤집혀 보일 수 있습니다.

rotationSpeed

부호와 방향

양수와 음수는 반대 회전 방향을 만듭니다. 값의 절댓값은 초당 회전량이며, 부호는 회전 방향을 결정합니다. 3교시에서는 부호를 다르게 설정하여 두 인스턴스가 반대 방향으로 회전하도록 구성합니다.

0도와 45도 방향의 같은 사각 오브젝트 비교. 오브젝트에 붙은 LOCAL +X 화살표는 함께 회전하고, 바닥의 WORLD +X 화살표는 두 장면 모두 오른쪽을 가리킴
로컬 좌표축은 오브젝트와 함께 회전하지만 월드 좌표축은 고정되어 있습니다. 이번 이동은 Space.World를 사용하므로 Sprite가 회전해도 월드의 오른쪽으로 이동합니다.
Transform API
transform.Translate(
    Vector3.right * moveSpeed * Time.deltaTime,
    Space.World
);

transform.Rotate(
    Vector3.forward,
    rotationSpeed * Time.deltaTime,
    Space.Self
);
  • 방향 × 속도 × 시간방향 벡터에 초당 속도와 이번 프레임 시간을 곱해 이동 벡터를 만듭니다. Time.deltaTime을 빼면 같은 이동량을 매 프레임 더하게 되어 프레임률에 따라 초당 거리가 달라집니다.
  • Space.World이동 기준을 월드로 고정합니다. 회전 중인 Sprite도 월드의 +X 방향으로 계속 이동합니다.
  • Space.Self회전을 현재 오브젝트의 축을 기준으로 적용합니다. 이번 2D 실습에서는 로컬 Z축을 중심으로 스프라이트가 회전합니다.

transform으로 현재 게임오브젝트의 Transform에 접근합니다

스크립트가 게임오브젝트에 추가되어 있으면 transform으로 그 오브젝트의 Transform 컴포넌트에 접근합니다. 다른 오브젝트의 Transform을 바꾸려면 해당 Transform의 참조가 필요합니다. 이번 실습에서는 스크립트가 추가된 게임오브젝트만 이동합니다.

AI와 이동 메서드 하나를 작성하고 검증합니다

앞에서 확인한 실행 시점과 초당 속도를 이동 코드에 적용합니다. 1교시에 연결한 AI 도구로 현재 파일의 이동 기능 하나를 작성합니다. 파일 지정 → 동작과 범위 요청 → 변경점 확인 → Unity 실행의 순서로 진행합니다.

시작 전 확인: Play를 종료하고 파일과 씬을 저장합니다. 1교시 완료 코드에서 Start가 PrintStartMessage를 호출하고 Update는 비어 있어야 합니다. 앞의 관찰용 Update 로그는 삭제합니다. 수정 전 내용을 편집기의 변경 이력 또는 프로젝트 외부의 백업 파일로 보관합니다. Assets 안에 .cs 복사본을 생성하면 동일한 클래스가 중복 정의될 수 있습니다.

작은 기능 요청 · MotionController.cs만 수정
Unity 6.6의 GameEngine1 프로젝트입니다.
Assets/Scripts/MotionController.cs를 분석한 뒤 오른쪽 이동 기능을 구현해 주십시오.
현재 파일은 1교시 완료 코드이며 moveSpeed, shouldRotate, Start,
빈 Update, PrintStartMessage가 정의되어 있습니다.

다음 조건을 준수합니다.
- 기존 moveSpeed를 초당 속도로 사용합니다.
- private void Move()를 정의하고 Update에서 호출합니다.
- Vector3.right * moveSpeed * Time.deltaTime으로 이동량을 계산합니다.
- transform.Translate는 Space.World 기준으로 적용합니다.
- 기존 필드와 Start, PrintStartMessage를 유지합니다.
- 이 파일만 수정합니다. 새 파일, 패키지, 입력, 물리, 회전, 경계 기능은 추가하지 않습니다.

변경할 위치와 이유를 설명한 뒤 코드를 제안해 주십시오.
속도 0에서 정지하고 속도 2에서 오른쪽으로 이동하는지
Unity에서 검증할 절차를 제시해 주십시오.
  1. 분석 대상 파일 확인

    AI에 현재 Assets/Scripts/MotionController.cs를 전달하고 위 요청을 보냅니다. 웹 채팅을 사용하는 경우 현재 파일의 전체 코드를 함께 입력합니다. 응답이 기존 필드와 PrintStartMessage를 사용하는지 확인합니다.

    기준현재 파일을 기준으로 Update의 수정과 Move의 추가를 제안해야 합니다.
  2. 변경 사항 확인 및 적용

    편집기의 변경 비교(diff)에서 추가되거나 삭제된 줄을 확인합니다. 빈 Update에 Move(); 호출이 추가되고, Move는 Update 밖의 같은 클래스 안에 정의되어야 합니다. 기존 필드와 시작 출력은 유지합니다. 요청 범위를 넘는 변경은 제외하고 다시 요청합니다.

    작성 위치기존 Update를 교체합니다. Update를 중복 정의하거나 Update 내부에 Move 메서드를 정의하지 않습니다.
  3. 예측과 실제 움직임 비교

    파일을 저장한 뒤 Unity의 컴파일을 기다립니다. Edit Mode에서 Move Speed를 0으로 두고 Play를 눌러 이동하지 않는지 확인합니다. Play를 종료한 뒤 값을 2로 변경하고 다시 실행하여 오른쪽 이동을 확인합니다. 매번 대상의 시작 위치와 Console을 확인합니다.

    검증Console에 오류가 없고 예상한 위치 변화가 나타나는지 직접 확인합니다.
비교용 코드: Update 교체와 Move 추가

아래는 결과를 점검할 수 있는 수업 예제입니다. 변수 이름과 코드 배치가 달라도 동일한 동작 조건을 충족해야 합니다. 적용할 경우 기존 Update를 아래 Update로 교체하고, 같은 클래스 안에 Move를 하나만 정의합니다. 나머지 1교시 코드는 유지합니다.

비교 예제 · Update와 Move 메서드
private void Update()
{
    Move();
}

private void Move()
{
    Vector3 movement = Vector3.right * moveSpeed * Time.deltaTime;
    transform.Translate(movement, Space.World);
}

현재는 이동만 추가했으므로 Should Rotate를 켜도 대상이 실제로 회전하지 않습니다. 경계 처리도 없어 화면 밖으로 이동한 뒤 자동으로 재배치되지 않습니다. 이동 결과를 확인한 뒤 Play를 종료합니다. 다음 단계에서 회전과 조건을 추가하고, 그다음 경계 재시작을 연결합니다.

첫 AI 코딩의 완료 기준 수정한 파일과 메서드, 변경 내용을 설명하고, 속도 0과 2의 결과를 직접 확인하면 완료입니다.

회전을 추가하고 조건에 따라 실행합니다

이동 기능을 검증한 파일에 회전을 추가합니다. Move(), Start, PrintStartMessage와 기존 필드는 유지합니다. 코드를 바꿀 때마다 Play를 종료하고, 저장·컴파일·실행 순서로 확인합니다.

1. 각속도 필드와 회전 메서드를 추가합니다

기존 필드 두 개 아래에 다음 필드를 추가합니다. 90은 초당 90도라는 뜻입니다. 프레임 간격이 0.1초일 때의 회전량을 먼저 계산합니다.

클래스 안, 기존 필드 아래에 추가
[SerializeField] private float rotationSpeed = 90f;

기존 Update를 아래 Update로 교체하고, RotateWhenEnabled()는 다른 메서드 밖의 같은 클래스 안에 추가합니다. 메서드 이름은 다음 단계에서 추가할 조건을 반영한 것입니다. 현재는 조건 없이 회전합니다.

Update 교체 + 회전 메서드 추가
private void Update()
{
    Move();
    RotateWhenEnabled();
}

private void RotateWhenEnabled()
{
    float angle = rotationSpeed * Time.deltaTime;
    transform.Rotate(Vector3.forward, angle, Space.Self);
}

실행 결과 확인: Inspector에서 Move Speed=0, Rotation Speed=90으로 설정하고 Play를 눌러 실행합니다. Position은 그대로이고 Rotation Z만 변해야 합니다. 90 × 0.1 = 9도이며, 일정한 속도라면 1초 동안 약 90도 회전합니다. Play 중 Rotation Speed를 -90으로 바꾸면 방향이 반대가 되는지 확인합니다. 아직 Should Rotate를 꺼도 회전은 계속됩니다. bool을 읽는 코드가 없기 때문입니다.

2. 기존 bool을 회전 조건에 연결합니다

1교시의 if (shouldRotate)는 값이 true일 때 메시지를 출력했습니다. 이번에는 논리 부정 연산자 !를 사용하여, 회전이 허용되지 않으면 계산 전에 메서드를 종료합니다. 아래 메서드로 기존 RotateWhenEnabled만 교체합니다. Update는 유지합니다.

RotateWhenEnabled만 교체
private void RotateWhenEnabled()
{
    if (!shouldRotate)
    {
        return;
    }

    float angle = rotationSpeed * Time.deltaTime;
    transform.Rotate(Vector3.forward, angle, Space.Self);
}
bool 값, 조건식, 실행 결과를 나누어 읽기
shouldRotate!shouldRotate실행 결과
truefalseif 내부를 건너뛰고 회전량을 계산합니다.
falsetrueif 내부의 return으로 회전 메서드를 종료합니다.

실행 결과 확인: Edit Mode에서 Move Speed=2, Rotation Speed=90, Should Rotate 켬으로 맞춥니다. Play 중 Should Rotate만 끄면 위치는 계속 변하고 각도만 멈춰야 합니다. 다시 켜면 멈췄던 각도에서 회전을 이어갑니다. return은 현재 메서드만 종료합니다. 실행은 Update의 호출문 다음 줄에서 계속됩니다.

오류 점검: 회전을 해제했을 때 이동도 정지하는 경우

if (!shouldRotate) return;을 Update의 Move 호출 앞에 작성하면 Update가 종료되어 이동도 실행되지 않습니다. 조건과 return이 RotateWhenEnabled 안에 있는지 확인합니다. Move 호출을 회전 허용 if 내부에 작성해도 이동이 같은 조건의 영향을 받습니다.

3. 회전해도 오른쪽으로 이동하는 이유를 확인합니다

Play를 종료하고 Transform Rotation Z를 45, Should Rotate를 끈 뒤 Play를 눌러 실행합니다. 기울어진 Sprite가 월드의 오른쪽으로 가는지 확인합니다. 종료 후 Move의 Space.World만 Space.Self로 잠시 바꿔 다시 실행하면 로컬 X축을 따라 대각선으로 이동합니다. 비교를 마치면 Play를 종료하고 코드는 Space.World, Rotation Z는 0, Should Rotate는 켬으로 되돌려 저장합니다.

CHECK

이동과 회전이 함께 실행되고, bool은 회전만 제어하며, 각속도의 부호는 회전 방향을 바꿉니다. 다음 단계 전 Move Speed=2, Rotation Speed=90으로 맞춥니다.

경계를 넘으면 X만 왼쪽으로 옮깁니다

현재 대상은 오른쪽으로 이동하다 화면 밖으로 나갑니다. 계속 관찰할 수 있도록 오른쪽 경계를 넘으면 왼쪽 시작 위치로 재배치합니다. 경계는 화면 가장자리를 자동으로 찾는 값이 아니라 직접 지정하는 월드 X 좌표입니다.

1. 두 경계값을 필드로 추가합니다

기존 필드 아래에 추가합니다. resetAtX는 검사할 오른쪽 경계이고, restartAtX는 재배치할 왼쪽 위치입니다. 오른쪽으로 움직이는 이번 실습에서는 restartAtX < resetAtX로 둡니다.

클래스 안, 기존 필드 아래에 추가
[SerializeField] private float resetAtX = 8f;
[SerializeField] private float restartAtX = -8f;

2. 위치를 읽고, X를 바꾸고, 다시 적용합니다

transform.position을 지역 변수 nextPosition으로 읽습니다. 이 변수의 X 좌표만 변경한 뒤 Transform에 대입하면 Y·Z 좌표가 유지됩니다. 지역 변수는 메서드 내부의 계산에 사용하며, Inspector에 저장하는 필드와 역할이 다릅니다.

Update 교체 + 경계 메서드 추가
private void Update()
{
    Move();
    RotateWhenEnabled();
    WrapAtBoundary();
}

private void WrapAtBoundary()
{
    if (transform.position.x <= resetAtX)
    {
        return;
    }

    Vector3 nextPosition = transform.position;
    nextPosition.x = restartAtX;
    transform.position = nextPosition;
}

기존 Update는 교체하고 WrapAtBoundary는 같은 클래스 안에 추가합니다. x <= resetAtX에는 경계와 같은 값도 포함됩니다. X가 정확히 8이면 그대로 두고, 8을 넘은 뒤에만 -8로 옮깁니다. Move 다음에 검사하므로 이번 프레임 이동으로 경계를 넘었는지 바로 판단합니다.

코드 점검: 마지막 대입문을 생략하면 결과는 어떻게 달라집니까?

nextPosition의 X만 바뀌고 실제 Transform에는 적용되지 않습니다. transform.position = nextPosition;까지 실행해야 실제 위치가 변경됩니다. new Vector3(restartAtX, 0, 0)을 대입하면 Y도 0이 되어 레인 높이가 달라질 수 있습니다.

3. 화면 안쪽의 작은 경계로 검증합니다

  1. 관찰 가능한 경계값 설정

    Edit Mode에서 Position=(-1, 2, 0), Move Speed=2, Should Rotate 끔, Reset At X=1, Restart At X=-1로 둡니다. Play하면 오른쪽으로 이동하다 X만 -1로 되돌아가는지 확인합니다.

    관찰순간적인 가로 재배치는 정상이며, Y=2와 Z=0은 유지되어야 합니다.
  2. 경계값과 경계를 초과한 값 비교

    Play 중 Move Speed를 0으로 바꿉니다. Position X에 1을 입력하면 그대로 있고, 1.1을 입력하면 다음 Update에서 -1로 바뀌는지 확인합니다. 이동 속도를 0으로 설정하면 이동의 영향 없이 경계 조건만 확인할 수 있습니다.

    조건X=1은 유지, X=1.1은 재배치입니다.
  3. 실습 기본값 복원

    Play를 종료하고 Move Speed=2, Should Rotate 켬, Rotation Speed=90, Reset At X=8, Restart At X=-8로 맞춥니다. 씬을 저장합니다. 카메라에 ±8 위치가 표시되지 않으면 앞에서 사용한 ±1 경계값으로 다시 확인할 수 있습니다.

    완료이동·회전·경계 재시작이 한 파일에서 이어집니다.

한 프레임의 실행 결과를 계산합니다

각각 검증한 기능을 한 번의 Update 호출로 연결합니다. 실행 순서는 Move → RotateWhenEnabled → WrapAtBoundary입니다. 아래 조건에 따라 각 단계의 결과를 직접 계산합니다.

시작 Position=(7.9, 2, 0), Rotation Z=30도, moveSpeed=2, deltaTime=0.1초, shouldRotate=false, resetAtX=8, restartAtX=-8입니다. 각 메서드 실행 후의 X 좌표와 회전각, 프레임 종료 시점의 Y·Z 좌표를 기록합니다.

계산과 호출 순서 확인
한 프레임 안에서 바뀌는 값
순서판단 또는 계산결과
Move2 × 0.1 = 0.2X: 7.9 → 8.1
RotateWhenEnabled!false는 true이므로 returnRotation Z: 30도 유지, Update로 복귀
WrapAtBoundary8.1 <= 8은 false이므로 재배치 실행Position: (-8, 2, 0)

회전 메서드의 return 뒤에도 경계 검사는 실행됩니다. 이동량과 회전량은 시간 간격을 곱해 계산합니다. 경계 검사에서는 시간 간격을 곱하지 않고 현재 위치와 기준값을 비교합니다.

추가 예측: 이동 속도가 0일 때 X=8에서 시작하는 경우와 X=8.1에서 시작하는 경우의 위치 변화를 비교합니다. 경계와 같은 값까지 포함하는 <=를 근거로 설명합니다.

완성 코드를 실행 흐름으로 읽습니다

아래는 지금까지 같은 파일에 추가한 내용을 모은 비교용 코드입니다. 작성한 파일에서 필드 다섯 개와 Update의 호출 세 개를 먼저 찾습니다. Start와 PrintStartMessage는 1교시부터 유지했습니다. 누락된 코드나 중복된 메서드가 있으면 해당 부분을 수정하고 이동·회전·경계 동작을 다시 검증합니다.

먼저 필드 다섯 개의 역할을 확인하고, 이후 메서드 호출 순서를 따라 코드를 읽습니다. 필드 → Start → Update → Move, RotateWhenEnabled, WrapAtBoundary 순서로 확인합니다.

Update의 신호가 Move, RotateWhenEnabled, WrapAtBoundary 작업실을 차례로 지나는 모형. 회전 작업실의 false 경로는 회전을 건너뛴 뒤 경계 검사로 계속 이어짐
한 프레임마다 Move() → RotateWhenEnabled() → WrapAtBoundary() 순서로 호출합니다. 회전 메서드의 return은 현재 메서드만 종료하므로, 회전을 꺼도 다음 경계 검사는 실행됩니다.
MotionController.cs
using UnityEngine;

public class MotionController : MonoBehaviour
{
    [SerializeField]
    private float moveSpeed = 2f;

    [SerializeField]
    private bool shouldRotate = true;

    [SerializeField]
    private float rotationSpeed = 90f;

    [SerializeField]
    private float resetAtX = 8f;

    [SerializeField]
    private float restartAtX = -8f;

    private void Start()
    {
        PrintStartMessage();
    }

    private void Update()
    {
        Move();
        RotateWhenEnabled();
        WrapAtBoundary();
    }

    private void Move()
    {
        Vector3 movement =
            Vector3.right * moveSpeed * Time.deltaTime;

        transform.Translate(movement, Space.World);
    }

    private void RotateWhenEnabled()
    {
        if (!shouldRotate)
        {
            return;
        }

        float angle =
            rotationSpeed * Time.deltaTime;

        transform.Rotate(
            Vector3.forward,
            angle,
            Space.Self
        );
    }

    private void WrapAtBoundary()
    {
        if (transform.position.x <= resetAtX)
        {
            return;
        }

        Vector3 nextPosition = transform.position;
        nextPosition.x = restartAtX;
        transform.position = nextPosition;
    }

    private void PrintStartMessage()
    {
        Debug.Log(name + "의 설정을 확인합니다.");
        Debug.Log("moveSpeed=" + moveSpeed);

        if (shouldRotate)
        {
            Debug.Log("회전이 허용되었습니다.");
        }
    }
}
필드별 사용 메서드와 실행 결과
필드사용되는 곳화면에 나타나는 결과
moveSpeedMove()오른쪽 이동 속도. 이번 실습에서는 0 이상의 값을 사용합니다.
shouldRotateRotateWhenEnabled()회전 허용 여부. false일 때도 이동은 유지됩니다.
rotationSpeedRotateWhenEnabled()초당 회전량과 부호에 따른 회전 방향.
resetAtXWrapAtBoundary()재배치 여부를 판단하는 오른쪽 X 경계값.
restartAtXWrapAtBoundary()경계를 초과했을 때 재배치할 왼쪽 X 좌표.
  1. Start()PrintStartMessage를 한 번 호출합니다. Should Rotate가 true이면 이름·속도·회전 허용 메시지 세 줄, false이면 이름·속도 두 줄을 출력하고 멈춥니다.
  2. Update()매 프레임 이동, 회전, 경계 처리 메서드를 같은 순서로 호출하여 전체 실행 흐름을 구성합니다.
  3. Move()오른쪽 방향, 초당 속도, 프레임 시간을 곱해 월드 좌표로 이동합니다. 회전 여부와 무관합니다.
  4. RotateWhenEnabled()shouldRotate가 false이면 즉시 종료하고, true일 때만 시간에 비례하는 회전량을 적용합니다. 조기 return은 현재 메서드만 종료하며 Update의 다음 호출은 계속 실행됩니다.
  5. WrapAtBoundary()x가 오른쪽 경계를 넘었을 때 y와 z는 유지하고 x만 왼쪽 시작값으로 바꿉니다. Y·Z 좌표를 변경하지 않으므로 기존 높이가 유지됩니다.

shouldRotate가 false이면 !shouldRotate는 true가 되어 return을 실행합니다. WrapAtBoundary에서는 X가 경계보다 작거나 같을 때 return합니다. 두 경우 모두 현재 메서드만 종료됩니다. 조건식이 true일 때 if 내부의 코드가 실행된다는 규칙은 동일합니다.

오른쪽 경계 x=8을 넘어 x=8.1에 있던 사각 오브젝트가 왼쪽 x=-8로 재배치됨. 이전과 이후 모두 y=2와 z=0을 유지하는 포털 모형
오른쪽 경계를 넘으면 x를 왼쪽 시작값으로 즉시 바꿉니다. 그림에서는 x = 8.1에서 x = -8로 바뀌고, y = 2와 z = 0은 유지됩니다. 포털은 좌표 재배치의 비유입니다.
  1. Inspector의 설정값 다섯 개 확인

    Play 전에 moveSpeed, shouldRotate, rotationSpeed, resetAtX, restartAtX를 확인하고 각 값을 사용하는 메서드를 찾습니다. 값이 코드 기본값과 다르면 해당 인스턴스에 저장된 값이 적용된 상태입니다.

    관찰점코드는 하나여도 값은 인스턴스마다 다를 수 있습니다.
  2. Play 직후 Console과 Transform 비교

    Console에는 시작 설정이 한 번 출력되고, Transform Position은 계속 바뀌는지 비교합니다. 설정 로그가 매 프레임 반복되면 출력 메서드의 호출이 Update 내부에 작성되어 있는지 확인합니다.

    관찰점Start는 한 번, Update는 매 프레임입니다.
  3. 값 하나를 바꾸기 전 예측

    moveSpeed, shouldRotate, rotationSpeed 중 하나를 바꾸기 전에 예상되는 화면 변화를 한 문장으로 기록합니다. 여러 값을 동시에 바꾸면 원인을 구분할 수 없습니다.

    예측 질문이번 변경은 이동, 회전, 경계 처리 중 어디에 영향을 줍니까?
  4. 경계 조건 관찰

    resetAtX를 화면 안쪽으로 옮겨 재배치 조건이 충족되는 시점을 확인합니다. X 좌표만 지정된 시작 위치로 즉시 변경되어야 합니다. 높이까지 바뀌면 y와 z를 덮어쓰고 있는지 확인합니다.

    관찰점경계 처리는 위치 전체를 덮어쓰지 않고 x만 바꿉니다.

값 변경에 따른 실행 결과를 예측합니다

아래 시뮬레이터는 Unity Inspector를 단순화한 모형입니다. 실제 씬의 Transform, Console, Prefab 연결을 대신하지는 않습니다. 코드 수정 없이 필드 값만 변경해도 실행 결과가 달라지는지 확인합니다.

아래 네 실험을 순서대로 수행합니다. 매번 예측 기록 → 값 하나 변경 → 관찰 기록 → 관련 메서드 확인의 순서로 진행합니다. 모형은 속도·회전의 관계를 보여줍니다. FPS 차이는 앞의 시간 계산으로, 실제 좌표와 경계 조건은 Unity의 Transform으로 확인합니다.

값 변경 모형

MotionController 필드 관찰

네 가지 조건을 예측하고 관찰합니다

재생 상태에서 moveSpeed=2, rotationSpeed=90, shouldRotate 켬으로 시작합니다. 아래 기록표를 작성한 뒤 결과 설명과 비교합니다.

  1. A · 이동 속도만 0으로

    moveSpeed만 0으로 바꿉니다. 위치와 회전 중 무엇이 정지할지 예측하여 기록하고 실행 결과를 확인합니다.

    관찰 대상화면의 위치와 회전각을 각각 확인합니다.
  2. B · 회전 허용만 끄기

    A의 속도 0을 유지하고 shouldRotate만 끕니다. 정지 원인이 일시 정지 버튼인지 두 필드의 설정값인지 구분합니다.

    관찰 대상재생 버튼 상태와 필드 값을 함께 기록합니다.
  3. C · 이동 속도만 2로

    회전은 꺼 둔 채 moveSpeed만 2로 바꿉니다. 화면에 반영되는 계산을 수행한 메서드를 확인합니다.

    관찰 대상회전 여부와 이동의 관계를 설명합니다.
  4. D · 같은 회전량으로 방향 반전

    먼저 shouldRotate를 켜 90도/s의 회전을 관찰합니다. 이 상태를 기준으로 rotationSpeed만 -90으로 바꿉니다. 이동 방향, 회전 방향, 초당 회전량 중 변화한 항목을 기록합니다.

    관찰 대상부호와 절댓값의 역할을 구분합니다.
예측·관찰·코드 근거 기록표
실험변경 전 예측실제 관찰관련 필드·메서드·이유
A · 속도 0위치: ___ / 회전: _________
B · 회전 끔위치: ___ / 회전: _________
C · 속도 2위치: ___ / 회전: _________
D · 회전 -90방향: ___ / 회전량: _________
네 실험의 결과와 코드 근거 확인

A: 이동량은 0이지만 회전은 계속됩니다. B: 회전 메서드도 return하므로 위치와 회전각이 모두 유지됩니다. C: Move는 실행되고 회전은 꺼져 있어 이동만 실행됩니다. D: 각속도의 절댓값은 같으므로 같은 회전량으로 반대 방향으로 회전하고, Space.World 이동 방향은 유지됩니다.

Unity에서도 같은 조건을 적용하고 Transform의 Position X와 Rotation Z로 비교합니다. shouldRotate를 Play 중 바꾸어도 Start의 설정 로그는 새로 출력되지 않습니다. 관찰한 변화의 근거는 Update가 호출하는 메서드에서 찾습니다.

핵심 개념

Update가 호출한 이동·회전 메서드에서 속도와 deltaTime을 곱해 프레임별 변화량을 계산하고, Transform에 적용합니다.

학습 내용 점검

항목별 설명을 확인하기 전에 움직임의 실행 과정을 네 단계로 설명합니다. Inspector 값 → Update 호출 → deltaTime 계산 → Transform 변화 순서로 문장을 작성하고 제시된 설명과 비교합니다.

초기 위치 저장과 반복 이동 코드는 각각 어디에 작성합니까?

실행 시작 시 초기 위치를 저장하는 코드는 Start에, 이동을 반복하는 코드는 Update에 작성합니다. 초기 위치 저장은 실행 중 사용할 값을 변수에 보관하는 것이며, 씬 파일 저장과는 다릅니다.

같은 스크립트를 사용하는 두 오브젝트의 이동 속도가 다를 수 있는 이유는 무엇입니까?

스크립트 코드는 하나여도 직렬화된 필드 값은 컴포넌트 인스턴스마다 따로 저장할 수 있습니다. 하나의 클래스로 생성한 인스턴스들이 각자의 필드 값을 사용합니다.

속도가 4 unit/s이고 deltaTime = 0.25일 때 프레임 이동량은 얼마입니까?

4 unit/s × 0.25 s = 1 unit입니다. 초당 속도에 이번 프레임이 차지한 시간을 곱합니다.

이동을 유지하면서 회전을 정지하려면 어느 값을 변경해야 합니까?

shouldRotate를 false로 두면 RotateWhenEnabled()가 조기 return합니다. Move()와 WrapAtBoundary()는 계속 호출됩니다.

Vector3를 사용한 2D 이동에서 Z 좌표를 유지하려면 어떻게 설정해야 합니까?

Transform의 위치가 Vector3이므로 이동량도 Vector3로 표현합니다. 월드 기준 이동량의 Z를 0으로 두면 기존 Z 위치는 유지됩니다. Rotate에 전달하는 Vector3.forward는 이동량이 아니라 Z 회전축입니다.

X가 경계값과 같을 때와 경계를 초과했을 때 실행되는 코드는 어떻게 다릅니까?

X가 경계값과 같으면 x <= resetAtX가 true이므로 메서드를 종료합니다. 경계를 초과하면 조건식이 false가 되어 위치 재배치를 실행합니다. nextPosition의 X만 바꾼 뒤 Transform에 다시 적용하므로 Y·Z가 유지됩니다.

3교시를 위한 저장 및 결과 확인

Play를 종료하고 파일과 씬을 저장합니다. 이동·회전·경계가 작동하는 파일과 네 실험의 기록이 2교시 결과물입니다. 3교시는 구현한 기능을 TODO 코드에서 직접 재구성한 뒤, 하나의 Prefab을 세 인스턴스로 확장하는 연습입니다. 완성 코드는 비교 자료로 보관하고, 각 메서드의 역할을 설명한 다음 필요한 부분부터 복원합니다.

핵심 정리 Inspector에서 인스턴스의 설정값을 지정합니다. Unity는 매 프레임 Update를 호출하고, 이동·회전 메서드는 속도에 deltaTime을 곱한 변화량을 Transform에 적용합니다.

Unity 공식 문서

현재 수업 기준인 Unity 6.6 문서에서 실행 순서, 직렬화, 시간과 Transform API의 정확한 의미를 확인합니다.

질문이 남았다면

변경한 값, 관찰한 결과, 추가 설명이 필요한 코드 범위를 함께 제시합니다. 예: “shouldRotate를 false로 변경하면 이동은 유지되고 회전만 정지합니다. 이때 return이 종료하는 메서드의 범위를 설명해 주십시오.”

Next period3교시: 하나의 스크립트로 세 가지 움직임 만들기
같은 구조로 3교시 실습 →