00 / ARRIVAL
2교시의 도착점
코드를 복사하는 것이 아니라, 각 줄이 어느 시간에 어떤 책임을 맡는지 설명합니다.
- 01
Update와FixedUpdate가 서로 다른 시계인 이유를 설명합니다. - 02
Rigidbody2D는 움직이는 몸체, Collider2D는 충돌 경계라고 구분합니다.
- 03
moveInput × moveSpeed가linearVelocity가 되는 과정을 읽습니다. - 04
Transform 직접 이동, 2D·3D 혼용, 대각선 과속이 왜 문제인지 진단합니다.
01 / RUN OF CLASS
60분의 설명 흐름
입력값이 물리 엔진에 안전하게 도착하는 순서를 먼저 설명하고, 마지막에 전체 코드를 하나로 연결합니다.
의도에서 충돌까지
Lecture + live demonstration
- 1교시 회수
Move가 만드는 값과 입력이 없을 때의 값을 말합니다. - 두 개의 시계
가변 프레임과 고정 물리 간격을 타임라인으로 비교합니다.
- 2D 물리 몸체
Rigidbody2D, Collider2D와 Body Type의 책임을 구분합니다.
- Controller 해설
초기화, 입력 읽기, 속도 적용을 코드 한 줄씩 연결합니다.
- 충돌 시연
정면·대각선 충돌과 잘못된 설정의 결과를 비교합니다.
- 오류 예측
세 가지 잘못된 이동 방식의 원인을 말로 진단합니다.
02 / TWO CLOCKS
화면의 시계와 물리의 시계는 다릅니다
한 프레임을 그리는 시간은 기기와 장면 복잡도에 따라 달라집니다. 물리 계산은 가능한 한 일정한 간격으로 실행됩니다.
Update()
렌더링 프레임마다 한 번 호출됩니다. 키가 눌렸는지 읽고, UI나 일반 상태를 갱신하기에 알맞습니다.
입력 읽기 → moveInput 저장
FixedUpdate()
고정된 물리 시간 간격에 맞춰 호출됩니다. Rigidbody2D의 속도와 힘을 적용하는 자리에 알맞습니다.
moveInput 사용 → 속도 적용
- 01Frame
Update가 현재 Move 값을 읽습니다. - 02Store
값을
moveInputfield에 잠시 보관합니다. - 03Physics step
FixedUpdate가 가장 최근 값을 가져옵니다. - 04Velocity
방향에 초당 속도를 곱합니다.
- 05Collision
Physics 2D가 벽과의 실제 이동 결과를 계산합니다.
03 / PHYSICS BODY
몸체와 경계가 함께 있어야 충돌합니다
Sprite는 보이는 모양일 뿐입니다. 물리 엔진은 Rigidbody2D와 Collider2D를 통해 움직임과 경계를 이해합니다.
움직임의 주체
위치, 회전, 속도와 물리 반응을 관리합니다. Player처럼 움직이는 대상에는 Dynamic 몸체를 사용합니다.
접촉의 경계
원, 상자, 캡슐 등의 모양으로 충돌 범위를 만듭니다. Sprite의 투명 영역까지 자동으로 정확히 읽는 것은 아닙니다.
- Body Type
- Dynamic
- Gravity Scale
- 0
- Linear Damping
- 0
- Interpolate
- Interpolate
- Collision Detection
- Continuous
- Constraints
- Freeze Rotation Z
- Component
- Box Collider 2D
- Is Trigger
- Off
- Rigidbody2D
- 없음 또는 Static
- Count
- 네 개 이상
- Purpose
- 충돌 경계 검증
- Dimension
- 2D만 사용
04 / VELOCITY
속도는 ‘한 프레임의 거리’가 아닙니다
linearVelocity는 Rigidbody2D가 1초 동안 각 축으로 얼마나 이동하려는지를 나타내는 초당 속도입니다.
moveInput×moveSpeed=linearVelocity
| 입력 | moveSpeed | 계산 결과 | 뜻 |
|---|---|---|---|
(0, 0) | 5 | (0, 0) | 멈춤 |
(1, 0) | 5 | (5, 0) | 오른쪽으로 초당 5 unit |
(0, -1) | 5 | (0, -5) | 아래로 초당 5 unit |
| 제한한 대각선 | 5 | 길이 5 | 직선과 같은 최대 속도 |
Transform.position += ...위치를 직접 바꾸면 Rigidbody2D의 물리 계산을 우회할 수 있습니다. 충돌 누락이나 떨림처럼 예측하기 어려운 결과가 생길 수 있습니다.
body.linearVelocity = ...몸체의 속도를 물리 엔진에 전달합니다. 충돌 해결과 다음 위치 계산은 Physics 2D가 맡습니다.
05 / CONTROLLER
전체 코드를 네 책임으로 나눠 읽습니다
교수자가 위에서 아래로 입력합니다. 학생은 각 블록이 “참조 준비, Action 활성화, 입력 읽기, 물리 속도 적용” 중 무엇을 맡는지 추적합니다.
Input Action과 Rigidbody2D를 기억합니다.
활성화될 때 Action을 켜고 꺼질 때 끕니다.
현재 입력을 읽고 길이를 제한합니다.
저장한 입력을 초당 속도로 바꿉니다.
[RequireComponent(typeof(Rigidbody2D))]이 Script가 Rigidbody2D를 필요로 한다는 계약입니다. 누락된 상태로 붙이는 실수를 줄입니다.
InputActionReference moveActionInput Actions 에셋 속 Move를 Inspector에서 연결할 자리입니다. 장치별 키를 코드에 직접 적지 않습니다.
Awake()같은 GameObject의 Rigidbody2D를 찾아 body에 보관합니다. 매번 다시 찾지 않습니다.
OnEnable() / OnDisable()Component가 활성화된 동안에만 Move Action이 값을 만들도록 생명주기를 맞춥니다.
Update()가장 최근의 입력을 읽어 moveInput에 보관하고, 대각선 벡터의 길이를 1 이하로 제한합니다.
FixedUpdate()저장된 의도에 초당 속도를 곱해 Rigidbody2D의 linearVelocity에 적용합니다.
OnGUI()수업 중 관찰을 위한 간단한 계기판입니다. 최종 게임 UI가 아니라 입력과 실제 속도를 비교하는 진단 도구입니다.
REFERENCE / COMPLETE SCRIPT전체 PlayerController 코드 펼쳐보기
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(Rigidbody2D))]
public class PlayerController : MonoBehaviour
{
[Header("Input")]
[SerializeField] private InputActionReference moveAction;
[Header("Movement")]
[SerializeField, Min(0f)] private float moveSpeed = 5f;
[SerializeField] private bool showInputMonitor = true;
private Rigidbody2D body;
private Vector2 moveInput;
private void Awake()
{
body = GetComponent<Rigidbody2D>();
}
private void OnEnable()
{
if (moveAction == null)
{
Debug.LogError("PlayerController: Move Action을 연결하세요.", this);
enabled = false;
return;
}
moveAction.action.Enable();
}
private void OnDisable()
{
if (moveAction != null)
{
moveAction.action.Disable();
}
}
private void Update()
{
moveInput = moveAction.action.ReadValue<Vector2>();
moveInput = Vector2.ClampMagnitude(moveInput, 1f);
}
private void FixedUpdate()
{
body.linearVelocity = moveInput * moveSpeed;
}
private void OnGUI()
{
if (!showInputMonitor || body == null) return;
GUI.Box(new Rect(16, 16, 250, 82), "INPUT MONITOR");
GUI.Label(new Rect(32, 46, 220, 20),
$"Move: ({moveInput.x:0.00}, {moveInput.y:0.00})");
GUI.Label(new Rect(32, 68, 220, 20),
$"Speed: {body.linearVelocity.magnitude:0.00}");
}
}
06 / INSPECTOR WIRING
코드의 빈칸은 Inspector에서 연결합니다
Script가 정확해도 참조가 비어 있으면 Move 값을 읽을 수 없습니다. 교수자 시연에서는 아래 연결을 눈으로 확인합니다.
- 01
Player에 Rigidbody2D와 Collider2D가 있는지 확인
- 02
PlayerController를 Player에 부착
- 03
Move Action슬롯에서 Player / Move 선택 - 04
Move Speed를 5로 설정 - 05
Play 후 계기판의 Move와 Speed 관찰
07 / COLLISION DEMO
벽 앞에서 멈추는 것은 코드 한 줄의 결과가 아닙니다
Controller는 속도를 제안합니다. Rigidbody2D와 두 Collider2D, Physics 2D가 함께 실제 접촉 결과를 만듭니다.
① 입력 계기판의 값이 바뀐다 → ② Player의 속도가 바뀐다 → ③ Collider 경계에 닿는다 → ④ 벽을 통과하지 않고 접촉면을 따라 움직이거나 멈춘다.
정면 충돌
벽을 향해 계속 눌러도 Player 중심이 벽 안으로 통과하지 않는지 봅니다.
대각선 충돌
두 키를 함께 눌러 모서리에서 튀거나 빠져나가는지 관찰합니다.
Trigger 비교
Wall의 Is Trigger를 잠시 켰을 때 통과가 허용되는 차이를 봅니다.
Transform 비교
물리 몸체를 Transform으로 직접 옮길 때 왜 결과가 불안정할 수 있는지 설명합니다.
08 / PREDICT ERRORS
결과를 보고 책임이 어긋난 곳을 찾습니다
오류 문구가 없어도 구조가 잘못되면 움직임이 이상할 수 있습니다. 현상에서 원인으로 거슬러 올라갑니다.
WASD를 눌러도 계기판이 (0, 0)입니다
먼저 Move Action 참조가 연결되었는지, Action이 활성화되었는지, Input Actions 에셋을 저장했는지 확인합니다. 물리 설정보다 입력 단계의 문제입니다.
계기판 값은 바뀌는데 Player가 움직이지 않습니다
입력은 정상입니다. PlayerController가 활성화되어 있는지, Rigidbody2D Body Type이 Dynamic인지, moveSpeed가 0보다 큰지 확인합니다.
대각선으로 갈 때 Speed가 약 7.07입니다
moveSpeed = 5일 때 (1, 1)의 길이 때문에 나타나는 값입니다. ClampMagnitude가 빠졌거나 결과를 다시 moveInput에 저장하지 않았는지 봅니다.
벽을 그대로 통과합니다
Player와 Wall에 2D Collider가 모두 있는지, Wall의 Is Trigger가 꺼져 있는지, Rigidbody2D를 Transform으로 직접 움직이는 다른 Script가 없는지 확인합니다.
Player가 멈춘 뒤 천천히 미끄러집니다
Update에서 입력 없음이 (0, 0)으로 갱신되는지 확인합니다. 관성 기반 설계가 아니라면 매 FixedUpdate에 속도 0을 명시적으로 적용해야 합니다.
모서리에 닿을 때 Player가 회전합니다
Rigidbody2D의 Constraints에서 Freeze Rotation Z가 켜져 있는지 확인합니다. 회전이 필요한 게임이라면 잠그지 않지만, 이번 4방향 이동 기준에서는 고정합니다.
09 / RETRIEVAL
세 문장으로 2교시를 닫습니다
아래 문장을 자신의 말로 완성하면 3교시에서 무엇을 만들어야 하는지 흔들리지 않습니다.
Update에서는 ______을 읽어 저장하고, FixedUpdate에서는 ______을 적용합니다.
linearVelocity는 초당 속도이므로 계산할 때 ______을 따로 곱하지 않습니다.
Player와 Wall이 충돌하려면 같은 ______ 물리 체계의 Rigidbody와 Collider를 사용해야 합니다.
10 / OFFICIAL SOURCES
Unity 공식 자료
시간 흐름, Rigidbody2D와 Collider2D의 설명은 Unity 6.6 문서를 기준으로 정리했습니다.
- Per-frame updates ↗가변 프레임과 Update 계열의 시간 개념
- Fixed updates ↗고정 timestep과 Physics update loop
- Rigidbody2D.linearVelocity ↗2D 몸체의 초당 X·Y 속도
- Introduction to Rigidbody 2D ↗Transform, Rigidbody2D와 Collider2D의 관계
- Collider 2D ↗2D 충돌 영역과 Collider 종류