두 정수 사이의 합 구하기
#include <string>
#include <vector>
#include <algorithm> // 여기에 std::min, std::max 있음
using namespace std;
long long solution(int a, int b)
{ // static_cast<long long>을 사용하는 이유는 int 범위를 넘는 경우를 방지
long long MinValue = static_cast<long long>(min(a, b)); // min(A, B) : 두 값 중 작은 값을 반환하는 함수 - 소문자 사용
long long MaxValue = static_cast<long long>(max(a, b));
long long Count = MaxValue - MinValue + 1; // 항의 개수 구하기
long long answer = (MinValue + MaxValue) * Count / 2; // 등차수열의 합 공식
return answer;
}
// 다른 방법
long long solution(int a, int b)
{
long long answer = 0;
if (a > b)
{
int temp = a;
a = b;
b = temp;
}
for (int i = a; i <= b; ++i)
{
answer += i;
}
return answer;
}
long long을 사용하는 이유a = 5,000,000, b = 10,000,000 같은 경우엔 합이 30조 이상 될 수 있음int는 범위가 약 21억까지라서 overflow 발생long long(64비트 정수) 써야 안전함std::min(a, b) / std::max(a, b) 사용법#include <algorithm> 필요Min(a, b) 또는 Max(a, b) 쓰면 에러 남Min, Max로 지으면 충돌하니 MinValue, MaxValue 등으로 구분long long MinValue = static_cast<long long>(std::min(a, b));
long long MaxValue = static_cast<long long>(std::max(a, b));
Count = Max - Min + 1 → 항의 개수(Min + Max) * Count / 2 → 첫 수 + 끝 수 × 항 개수 ÷ 2| 실수한 코드 | 이유 |
|---|---|
Min(a, b) | ❌ 함수 아님. 대문자 함수는 정의되어 있지 않음 |
long long Min = ... + Min(a, b) | ❌ Min은 이미 변수라 함수처럼 쓸 수 없음 |
#include <algorithm> 빠짐 | ❌ std::min과 std::max 못 찾음 |
📍 2주차 3강 ~ 4강
🛠️ 언리얼 입력 시스템 흐름
구성 요소 역할
1️⃣ GameMode
: 게임 전체 흐름과 Character 생성 및 관리
2️⃣ Character
: 실제 게임 속 플레이어 캐릭터 (GameMode에서 스폰됨)
3️⃣ PlayerController
: 입력 담당. Character와 플레이어를 연결하는 뇌/영혼 역할
4️⃣ Input Mapping Context (IMC)
: 어떤 키에 어떤 동작이 연결됐는지 정의
5️⃣ Local Player Subsystem
: IMC를 관리하고, 입력의 의미를 해석해 PlayerController에 전달
Local Player - Local Player Subsystem - IMC 활성화
🧩 입력 흐름 예시
1️⃣ 사용자가 A키 누름
2️⃣ PlayerController가 "A" 입력 감지
3️⃣ "A가 뭔데?" → Local Player Subsystem에 물어봄
4️⃣ Subsystem: "A = 공격 동작!"
5️⃣ Controller: "오케이, 공격 실행!" → 캐릭터에게 명령
6️⃣ Character: 실제 공격 함수 실행
셋은 유기적으로 상호작용하면서 입력 → 해석 → 실행 과정을 반복
PlayerController = 중간 관리자
Local Player Subsystem = 통역사
Character = 실행자
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "SpartaCharacter.generated.h"
// 헤더에서 include를 사용하지 않고 미리 선언을 사용
class USpringArmComponent;
class UCameraComponent;
struct FInputActionValue; // 구조체
UCLASS()
class SPARTAPROJECT_API ASpartaCharacter : public ACharacter
{
GENERATED_BODY()
public:
ASpartaCharacter();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera") // 객체 변경은 불가능, 내부 속성은 에디터에서 조정 가능
USpringArmComponent* SpringArmComp; // 컴포넌트 선언
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
UCameraComponent* CameraComp;
protected:
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; // IA에다 동작 함수들 연결하는 장소
// 함수 선언
// IA 만들 때 설정한 valuetype
// 구조체 같은 건 크기 때문에 참조 안 하고 갖고 오면 객체의 모든 데이터를 복사해서 갖고 옴 -> 성능, 복사 비용 크다
// 수정 못하게 const로 선언
// 리플렉션 시스템에 등록만
UFUNCTION()
void Move(const FInputActionValue& value);
UFUNCTION()
// boolean 타입은 on/off 상태를 나눠주는게 좋음
void StartJump(const FInputActionValue& value);
UFUNCTION()
void StopJump(const FInputActionValue& value);
UFUNCTION()
void Look(const FInputActionValue& value);
UFUNCTION()
void StartSprint(const FInputActionValue& value);
UFUNCTION()
void StopSprint(const FInputActionValue& value);
private:
float NormalSpeed; // 기본 속도
float SprintSpeedMultiplier; // 기본 속도에 몇 개를 곱해줄건데
float SprintSpeed; // 위에 두 개를 곱해서 나오는 속도 = 얼마나 빨라졌는지
};
#include "SpartaCharacter.h"
#include "EnhancedInputComponent.h" // Enhanced Input 시스템을 사용하기 위한 include
#include "SpartaPlayerController.h" // 플레이어 컨트롤러를 사용하기 위한 include
// 헤더 파일에서 include를 사용하지 않고 미리 선언을 사용
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
ASpartaCharacter::ASpartaCharacter()
{
PrimaryActorTick.bCanEverTick = false; // 이 캐릭터는 Tick을 사용하지 않음
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp")); // 컴포넌트 생성
SpringArmComp->SetupAttachment(RootComponent); // 루트 컴포넌트에 연결
SpringArmComp->TargetArmLength = 300.0f; // 스프링 암(삼각대)의 길이 설정
SpringArmComp->bUsePawnControlRotation = true; // 캐릭터의 회전을 스프링 암이 따르도록 설정
CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComp"));
CameraComp->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName); // 스프링 암 끝 부분에 연결
CameraComp->bUsePawnControlRotation = false; // 카메라가 캐릭터의 회전을 따르지 않도록 설정
NormalSpeed = 600.0f; // 기본 속도 초기화
SprintSpeedMultiplier = 1.7f; // 스프린트 속도 배율 초기화
SprintSpeed = NormalSpeed * SprintSpeedMultiplier;
// CharacterMovement 컴포넌트가 여러 이동 함수들을 가지고 있음
GetCharacterMovement()->MaxWalkSpeed = NormalSpeed; // 캐릭터의 최대 걷기 속도를 기본 속도로 설정
}
void ASpartaCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(PlayerInputComponent)) // PlayerInputComponent 여러 갠데 우리가 사용하는 EnhancedInput 기능으로 정해주기
{
ASpartaPlayerController* PlayerController = Cast<ASpartaPlayerController>(GetController()); // 현재 캐릭터가 조작하는 Controller get -> SpartaPlayerController로 한 번 캐스팅
if (PlayerController == nullptr) // null 체크
{
return;
}
if (PlayerController->MoveAction) // IMC가 할당되어 있다면
{
EnhancedInput->BindAction( // BindAction : 이벤트랑 함수를 연결하는 핵심 코드
PlayerController->MoveAction, // IA 가져오기
ETriggerEvent::Triggered, // IMC에서 설정한 트리거 이벤트
this, // 이 캐릭터에 바인딩
&ASpartaCharacter::Move // Move 함수에 바인딩, 포인터 - 호출된 함수 주소 가져옴
);
}
if (PlayerController->JumpAction)
{
EnhancedInput->BindAction(
PlayerController->JumpAction,
ETriggerEvent::Triggered,
this,
&ASpartaCharacter::StartJump
);
EnhancedInput->BindAction(
PlayerController->JumpAction,
ETriggerEvent::Triggered,
this,
&ASpartaCharacter::StopJump
);
}
if (PlayerController->LookAction)
{
EnhancedInput->BindAction(
PlayerController->LookAction,
ETriggerEvent::Triggered,
this,
&ASpartaCharacter::Look
);
}
if (PlayerController->SprintAction)
{
EnhancedInput->BindAction(
PlayerController->SprintAction,
ETriggerEvent::Started,
this,
&ASpartaCharacter::StartSprint
);
EnhancedInput->BindAction(
PlayerController->SprintAction,
ETriggerEvent::Completed,
this,
&ASpartaCharacter::StopSprint
);
}
}
}
void ASpartaCharacter::Move(const FInputActionValue& value)
{
// 컨트롤러가 있어야 방향 계산이 가능
if (!Controller) return;
// Value는 Axis2D로 설정된 IA_Move의 입력값 (WASD)을 담고 있음
// 예) (X=1, Y=0) → 전진 / (X=-1, Y=0) → 후진 / (X=0, Y=1) → 오른쪽 / (X=0, Y=-1) → 왼쪽
const FVector2D MoveInput = value.Get<FVector2D>();
if (!FMath::IsNearlyZero(MoveInput.X))
{
// 캐릭터가 바라보는 방향(정면)으로 X축 이동
AddMovementInput(GetActorForwardVector(), MoveInput.X);
}
if (!FMath::IsNearlyZero(MoveInput.Y))
{
// 캐릭터의 오른쪽 방향으로 Y축 이동
AddMovementInput(GetActorRightVector(), MoveInput.Y);
}
}
void ASpartaCharacter::StartJump(const FInputActionValue & value)
{
if (value.Get<bool>()) // bool 값이 true라면
{
Jump(); // 점프 함수 호출
}
}
void ASpartaCharacter::StopJump(const FInputActionValue & value)
{
if (!value.Get<bool>()) // bool 값이 false라면
{
StopJumping(); // 점프 중지 함수 호출
}
}
void ASpartaCharacter::Look(const FInputActionValue & value)
{
FVector2D LookInput = value.Get<FVector2D>(); // 입력값을 2D 벡터로 가져옴
AddControllerYawInput(LookInput.X); // X축 입력값을 yaw 회전에 적용
AddControllerPitchInput(LookInput.Y); // Y축 입력값을 pitch 회전에 적용
}
void ASpartaCharacter::StartSprint(const FInputActionValue & value)
{
if (GetCharacterMovement())
{
GetCharacterMovement()->MaxWalkSpeed = SprintSpeed; // 캐릭터의 최대 걷기 속도를 스프린트 속도로 설정
}
}
void ASpartaCharacter::StopSprint(const FInputActionValue & value)
{
if (GetCharacterMovement())
{
GetCharacterMovement()->MaxWalkSpeed = NormalSpeed; // 캐릭터의 최대 걷기 속도를 기본 속도로 설정
}
}
캐릭터의 상태에 따라 애니메이션 적용해야 함 → 애니메이션 블루프린트를 만들어야 한다

→ 애니메이션 계속 반복됨


Character Movement Component
Event BlueprintInitializeAnimation = BeginPlay
Event BlueprintUpdateAnimation = Tick상태에 따라 적절한 애니메이션으로 전환

Locomotion
▶ 각 스테이트들 Loop Animation ☑
▶ Walk/Run은 블랜드 스페이스로 애니메이션 자연스럽게 섞어주기
▶ 트랜지션 룰 : Idle <-> Walk/Run 조건 설정하기
▶ 최종 형태 애니메이션 재생 중
→ 발이 공중에 뜸 (not IsFalling)
→ Control Rig 사용
→ 발 위치를 땅 높이에 맞게 보정
→ 자연스러운 움직임 완성
▶ Set Initial Transforms from Mesh ☑
▶ Control Rig Class 설정 후 ShouldDoIKUse Pin ☑ + 조건 연결
| 항목 | 🦿 IK (Inverse Kinematics) | 🎮 Control Rig |
|---|---|---|
| 뭐냐? | 관절 뼈 계산 (관절 자동 제어) | IK 계산법을 따라 실제로 캐릭터를 움직임 (툴/시스템) |
| 목적 | 손·발 같은 끝 위치 고정하고 나머지 자동 계산 | 본을 직접 스크립트처럼 조정하거나 제어 |
| 작동 방식 | 목표 위치 주면 → 나머지 관절 알아서 계산 | IK 포함해서 회전, 위치, 조건 등 전부 조작 가능 |
| 대표 예시 | Two Bone IK, Full Body IK | Control Rig 블루프린트에서 IK 노드, 조건, 계산 등 |
| 쓰는 곳 | 애님 블루프린트, Skeletal Control | Control Rig 에디터, 애님 BP, 시퀀서 등 |
| 단독 사용? | O (IK 노드만 써도 작동함) | X (Control Rig은 IK를 포함해서 씀) |



Jump는 Loop Animation 체크 해제해도 됨 그룹 형태로 만듦
To Land

To Falling

Automatic Rule Based on Sequence Player in State ☑ 해서 자연스럽게 연결하기
Locomotion ← Land
조건 2개 필요
To Land → Land
Land로 전환To Falling → Jump
To Falling → Fall Loop 
Idle / Run / Jump
↓ (is falling?)
[To Falling]
↓ (looping fall anim)
[Fall Loop]
↓ (is on ground?)
[Land]
영훈 튜터님께서 어제 이해 못했던 IMC swizzle을 알려주셨다.
swizzle을 들어오는 값 자체로 생각하지 말고 바뀐 축 순서로 생각하기
swizzle은 Swizzle은 벡터의 축 순서를 바꾸는 것
<X, Y, Z Swizzle>
Swizzle 순서: (X, Y, Z)
Vector: (1, 0, 0) → 그대로 유지됨
→ X값은 그대로 첫 번째 자리에 있음 → 정상 전진 ✅
<X, Z, Y Swizzle>
Swizzle 순서: (X, Z, Y)
Vector: (1, 0, 0) → X값이 그대로 첫 번째 자리에 있음
→ X가 여전히 첫 번째 자리에 있음 → 정상 전진 ✅
| Swizzle | 첫 번째 축 | 벡터 해석 | 전진/후진 작동? |
|---|---|---|---|
| X, Y, Z | X | (1, 0, 0) → 전진 | ✅ |
| X, Z, Y | X | (1, 0, 0) → 전진 | ✅ |
| Y, X, Z | Y | (0, 1, 0) → 좌우 | ❌ 전진 실패 |
| Y, Z, X | Y | (0, 0, 1) → 엉뚱한 방향 | ❌ 전진 실패 |
Y값이 Swizzle에서 첫 번째 자리에 있어야 좌우 이동이 제대로 동작
Original Vector: (-1, 0, 0) // A키 입력 → 왼쪽으로 가고 싶음
<Y, X, Z Swizzle>
Swizzle 순서: (Y, X, Z)
Vector: (0, -1, 0)
→ 첫 번째 자리에 Y가 들어옴
→ “왼쪽으로 이동한다”는 의미가 정상 반영됨 ✅
<Y, Z, X Swizzle>
Swizzle 순서: (Y, Z, X)
Vector: (0, 0, -1)
최종 출력 벡터는 (0, 0, 1) → Z축(위 방향)으로 이동함 ❌