Unreal 개발 사전캠프 11일차

HappyCircle·2025년 11월 17일

Unreal 개발

목록 보기
11/163

사전캠프 퀘스트
캐릭터 Blueprint 생성 및 이동 설정 (Part 1)
왜 중요한가요?
Unreal Engine의 핵심 기능 중 하나는 캐릭터가 공간 내에서 움직이는 것을 구현하는 것입니다. 오늘 과제를 통해 캐릭터를 생성하고 입력 시스템을 연결하는 기본 구조를 익히게 됩니다. 이후 인터랙션이나 게임 로직의 기초가 됩니다.

개념 바로 알기
블루프린트(BluePrint)란?
언리얼 엔진의 비주얼 스크립팅 시스템
코드 없이 로직을 구성할 수 있음 (클래스 설계 가능)
Input Mapping이란?
키보드/마우스/패드 등의 입력을 게임 안의 행동(이동, 점프 등)과 연결하는 시스템

제출하기
직접 구현해보기
1. 오늘 배운 개념 요약
언리얼 엔진 내에서 캐릭터가 살아 움직일 수 있게 구현하는데 사용 되는 것이 블루 프린트
블루 프린트는 코드 없이 로직을 구성할 수 있게하는 언리얼 엔진의 비주얼 스크립팅 시스템
사용자의 입력 장치의 입력을 게임 안의 행동(이동, 점프 등)와 연결하는 것을 InputMapping이라고 지칭

  1. 구현 과정 요약 (스크린샷 + 간단 설명)
  • 에디터 내에서 캐릭터가 이동하는 모습 영상 녹화 (5~10초)
  • 블루프린트 편집 화면 캡처 (Input 처리 부분)
    목표: 나만의 캐릭터 블루프린트를 만들고, 이동 입력 세팅을 완료합니다.
    [Step 1] ThirdPerson Template으로 새 프로젝트 만들기
    새로운 언리얼 프로젝트 생성 시, Third Person Template 선택
    Starter Content 포함

    ThirdPerson Template 새프로젝트 생성 ThirdPerson Template 새프로젝트 생성

    Starter Content 프로젝트에 포함 Starter Content 프로젝트에 포함

[Step 2] 캐릭터 블루프린트 생성
Content > ThirdPerson > Blueprints > BP_ThirdPersonCharacter 열기
복제하여 MyCharacter로 이름 변경

BP_ThirdPersonCharacter 복제 MyCharacter 블루프린트 생성

[Step 3] Input 세팅 확인하기
Edit > Project Settings > Input 열기
MoveForward, MoveRight 등 기본 입력 맵핑 확인
W/S → 전후, A/D → 좌우

전후좌우 기본 입력 맵핑 전후좌우 기본 입력 맵핑

[Step 4] 캐릭터 블루프린트 연결
GameMode → MyCharacter로 변경
Project Settings > Maps & Modes > Default Pawn Class

Project 세팅에서 Default Pawn Class MyCharacter로 변경 Project 세팅에서 Default Pawn Class MyCharacter로 변경

[Step 5] 플레이(Play)로 테스트
캐릭터가 키보드 입력에 따라 이동하는지 확인
이동이 어색하면 위치 조정 및 충돌 범위 재확인

캐릭터 동작 키 맵핑에 따른 동작 여부 체크 확인

  1. 느낀 점 & 개선하고 싶은 점
    입력 매핑 설정을 통해 생성한 캐릭터가 의도한 대로 동작함을 확인하면서, 실제 게임 개발 과정에서 사용되는 다양한 기능(예: 앉기, 달리기 등)을 추가적으로 구현해 보고자 하는 동기가 생겼다. 이를 통해 캐릭터 조작 체계를 보다 현실적이고 자연스럽게 확장할 수 있을 것으로 기대된다.

캐릭터 이동 구현 (Part 2)
왜 중요한가요?
이동, 점프, 회전은 어떤 게임에서든 가장 기본적인 조작 요소입니다. 이 기능들이 자연스럽게 구현되어야 사용자 경험이 매끄럽습니다. 오늘 과제에서는 이동 기능을 완성도 있게 다듬고, 점프와 카메라 회전을 추가해 조작 가능한 주인공을 만드는 데 집중합니다.

개념 바로 알기
Character Movement Component란?
언리얼의 기본 캐릭터 클래스에는 움직임을 제어하는 컴포넌트가 기본 탑재됨
이동 속도, 점프 높이, 중력 설정 등 다양한 조절이 가능함
Spring Arm + Camera 세팅
Spring Arm은 카메라와 캐릭터 간 거리를 부드럽게 유지시켜줌
카메라가 캐릭터를 따라다니며 회전하게 만들 수 있음

  1. 오늘 배운 개념 요약
    게임 내에서 캐릭터의 이동,점프,회전은 가장 기본이 되는 조작 요소이므로 해당 부분이 자연스럽게 구현되어 있어야 사용자 경험이 쾌적해진다.
    기본적인 조작요소 구현을 위해서
    Character Movement Component를 사용해서 사용자의 움직임을 제어 가능
    속도,점프 높이, 중력 등 다양한 값들 조절을 통해서 움직임 세밀하게 조절 가능
    Spring Arm은 카메라와 캐릭터 간 거리를 부드럽게 유지시켜주고 카메라가 캐릭터를 따라다니며 회전이 가능

  2. 구현 과정 요약 (스크린샷 + 간단 설명)

  • 캐릭터가 회전하며 이동 & 점프하는 플레이 영상 (10초 내외)필수 첨부
  • 블루프린트에서 Jump와 Movement 관련 노드 캡처
    직접 구현해보기
    목표: 캐릭터에 점프, 카메라 회전 기능을 추가하고, 자연스러운 조작을 구현합니다.
    [Step 1] 점프 기능 활성화
    Project Settings > Input에서 Jump 액션 확인 (기본: Spacebar)

Project Settings Jump 액션 확인 Project Settings Jump 액션 확인

MyCharacter 블루프린트에서 Jump 입력 이벤트 연결
MyCharacter 블루프린트 → Event Graph
우클릭 → InputAction Jump 검색 → Event → Jump() 노드 연결
InputAction StopJumping → StopJumping() 연결

언리얼 엔진 5.6 버전 업데이트에 따른 EnhancedInputAction 사용 언리얼 엔진 5.6 버전 업데이트에 따른 EnhancedInputAction 사용

[Step 2] 이동 속도와 점프 높이 조절
MyCharacter > Character Movement 컴포넌트 선택
아래 항목 조정해보기:
Max Walk Speed: 기본 600 → 800 추천
Jump Z Velocity: 기본 420 → 600 추천

Max Walk Speed 조절 Jump Z Velocity 조절 Max Walk Speed, Jump Z Velocity 조절

[Step 3] 카메라 회전 설정
MyCharacter > SpringArmComponent 선택
Use Pawn Control Rotation: 체크

CameraComponent 선택
Use Pawn Control Rotation: 체크 해제

MyCharacter 블루프린트의 Class Defaults에서:
Use Controller Rotation Yaw: 체크

[Step 4] 테스트 플레이
WASD + 마우스 회전 + 점프 조합으로 캐릭터 조작
자연스럽게 회전하며 걷고 점프하는지 확인

  1. 느낀 점 & 개선하고 싶은 점
    현재 구현된 캐릭터는 카메라의 좌우 회전은 가능하지만 상하 회전이 제한되어 있어 실제 게임 환경에서 요구되는 자유로운 시야 이동이 충분히 반영되지 못하고 있다. 따라서 향후에는 상하좌우로 자연스럽게 움직일 수 있는 카메라 제어 기능을 보완하여, 사용자 경험을 개선하고 더 완성도 높은 3D 캐릭터 조종 시스템을 구축하고자 한다.

오늘 학습 진행 내용
C++ 강의
3-2. 배열
배열의 개념과 필요성을 이해
C++에서 배열을 선언하고 사용하는 방법을 학습

배열은 동일한 데이터 타입의 여러 원소들을 연속된 메모리 공간에 저장하여 관리하는 선형 자료 구조
주요 특징
동질성
배열의 원소는 모두 동일한 타입
예를 들어 정수형 배열의 모든 원소는 정수형, 문자형 배열의 모든 원소는 문자형

연속된 메모리 할당
단편화 문제가 발생할 확률이 적기 때문에 접근 속도도 빠름

인덱스 기반 접근
배열의 첫 번째 원소는 인덱스 0, 이후 원소 순차적 1씩 증가하는 인덱스

배열 선언
배열 선언만 하고 초기화하지 않은경우
값은 쓰레기 값이 할당됨

int arr[5]; // 초기화하지 않음

모든 원소를 명시적으로 초기화

int arr[5] = { 10, 20, 30, 40, 50 }; // 모두 초기화

일부만 초기화하고 나머지는 0으로 자동 초기화
배열 초기화 시 값이 부족하면 나머지는 0으로 자동 초기화

 int arr[5] = {10, 20, 30}; // 3개만 초기화 → 나머지 2개는 0 으로 자동 채워짐

2차원 배열 선언만 하고 초기화 하지 않는 경우
2차원 배열을 선언만 한 경우 쓰레기 값이 할당

int arr[2][3]; // 2행 3열의 배열,  초기화하지 않음

2차원 배열을 행-열 구조로 명확히 초기화

int arr[2][3] = {
{1, 2, 3},
{4, 5, 6}
};

일부만 초기화 하는 경우
2차원 배열을 부분적으로 초기화 하면, 나머지 값은 0으로 자동 초기화

int arr[2][3] = {
{10}, //첫 번째 행의 첫번째 원소만 초기화
{20,30} //두 번째 행의 앞 2개 원소 초기화
};

배열이란?
기존 변수를 선언할 때는 int a와 같이 변수의 타입과 이름을 명시
배열은 여기에 더해 배열의 크기를 추가로 명시하여 선언
C++에서 배열을 선언하고 초기화하는 방법
1차원 배열의 선언
데이터를 선형 형태로 저장하는 가장 기본적인 배열
기차 칸 처럼 데이터들이 순서대로 나열

data_type array_name[size]
예시: int arr[5]

data_type array_name[size] = {value1,value2,...}
예시 : int arr[5] = {1,2,3,4,5}
앞에서부터 순서대로 값이 초기화
만약 초기화 값의 개수가 size보다 적으면 나머지 원소들은 0으로 자동 초기화

2차원 배열의 선언
데이터를 행과 열로 구성된 표 또는 격자 형태로 저장하는 배열
1차원 배열을 여러 개 묶어 놓은 형태

data_type array_name[row_size][col_size]
예시 : int arr[2][3]

data_type array_name[2][3] = {{value1,value2,value3},{value4,value5,value6}}
예시 : int arr[2][3] = {{1,2,3},{4,5,6}}

배열의 메모리 구조
1차원 배열의 경우 배열의 각원소가 연속된 메모리 주소에 저장
2차원 배열의 경우 배열의 원소들은 메모리상에 행 우선 순서로 연속 저장

배열의 효율성
배열의 맨 뒤에 값 삽입
다른 원소를 건드리지 않으므로 O(1) 연산

int arr[10] = {1,2,3,4,5}; //현재 5개의 원소
int size = 5; //현재 배열에 저장된 원소 개수
int capacity = 10; //배열 전체 크기

int newValue = 99;

if(size<capacity){
	arr[size] = newValue; // 맨 뒤에 삽입
    size++;
 }else{
 	cout << "배열이 가득 찼습니다. 삽입할 수 없습니다."<<endl;

배열의 맨 앞에 값 삽입
기존 원소들을 모두 한칸씩 뒤로 밀기 때문에 O(N) 시간 복잡도

int arr[10] = {1,2,3,4,5}; //현재 5개의 원소
int size = 5; //현재 배열에 저장된 원소 개수
int capacity = 10; //배열 전체 크기

int newValue = 5;

if(size<capacity){
	//원소들을 뒤에서부터 한 칸씩 밀기
    for(int i=size-1; i>=0; i--){
    	arr[i+1] = arr[i];
    }
    //첫 번째 자리에 새 값 삽입
    arr[0] = newValue;
    size++;
 }else{
    cout<<"삽입 실패: 배열이 가득 찼습니다."<<endl;
}

문제 풀어보기
배열 제어하기
문제 설명
정수 배열 lst가 주어집니다. 배열의 중복값을 제거하고 배열 데이터를 내림차순으로 정렬해서 반환하는 solution 함수를 구현

제약 조건
lst의 길이는 2 이상 1000이하입니다.
lst의 원소 값은 -100,000 이상 100,000 이하

풀이

#include <vector>
#include <algorithm> // sort, unique를 위해 선언
using namespace std;

bool compare(int a, int b){ //정렬 기준 정의
	return a>b;
}

vector<int> solution(vector<int> lst) {
	sort(lst.begin(),lst.end(),compare); //내림차순 정렬
    lst.erase(uniuqe(lst.begin(), lst.end(), lst.end()); //중복값 제거
    
    return lst;
}

합격자가 되는 모의 테스트
두 수를 뽑아서 더하기
문제 설명
정수배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 2개의 수를 뽑아 더해 만들 수 있는 수를 배열에 오름차순으로 담아 반환하는 solution()함수를 완성하세요

제약 조건
numbers의 길이는 2이상 100이하입니다.
numbers의 모든 수는 0이상 100이하입니다.

풀이

#include <vector>
#include <set>

using namespace std;

vector<int> solution(vector<int> numbers){
	set<int> sum; //두 수의 합을 저장할 저장 공간 선언
    for(int i=0; i<numbers.size();++i) // 반복문을 수행하면서 두 수의 합을 저장
    	for(int j=i+1; j<numbers.size(); ++j)
        	sum.insert(numbers[i] + numbers[j]);
    
   	vector<int> answer(sum.begin(),sum.end()); //반환타입을 맞추기 위해 벡터로 변환
    return answer;
}
    

모의고사
문제 설명
수포자는 수학을 포기한 사람, 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다.
1번 수포자가 찍는 방식 : 1,2,3,4,5,1,2,3,4,5,...
2번 수포자가 찍는 방식 : 2,1,2,3,2,4,2,5,2,1,2,3,2,4,2,5,....
3번 수포자가 찍는 방식 : 3,3,1,1,2,2,4,4,5,5,3,3,1,1,2,2,4,4,5,5,....
1번 문제부터 마지막 문제까지의 정답이 순서대로 저장된 배열 answers가 주어졌을 때 가장 많은 문제를 맞힌 사람이 누구인지 배열에 담아 반환하도록 solution() 함수를 작성

제약 조건
시험은 최대 10,000문제로 구성
문제의 정답은 1,2,3,4,5 중 하나
가장 높은 점수를 받은 사람이 여럿이라면 반환하는 값을 오름차순으로 정렬

풀이

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> firstPattern = {1,2,3,4,5};
vector<int> secondPattern = {2,1,2,3,2,4,2,5};
vector<int> thirdPattern = {3,3,1,1,2,2,4,4,5,5};

vector<int> solution(vector<int> answer) {
  //최종적으로 가장 많이 문제를 맞힌 사람이 저장될 벡터
  vector<int> answer;

  //각 수포자들의 패턴대로 답안을 작성할 때 문제를 맞힌 갯수가 저장될 벡터
  vector<int> matchCnt(3);

  //실제 정답과 각 수포자들의 패턴을 비교해서 맞춘 갯수
  for(int i=0; i<answers.size(); i++){
      if(answers[i] == firstPattern[i % firstPattern.size()]) matchCnt[0]++;
      if(answers[i] == secondPattern[i % secondPattern.size*(]) matchCnt[1]++;
      if(answers[i] == thirdPattern[i % thirdPattern.size*(]) matchCnt[2]++;
  }

  //가장 많이 맞춘 수포자가 얻은 점수
  int max_score = *max_element(matchCnt.begin().matchCnt.end()); //*는 해당 값의 위치의 실제 값 가져오는데 사용 iterator의 실제 값

  //가장 많이 맞춘 수포자의 번호를 저장
  for(int i=0; i<3; i++){
      if(matchCnt[i] == max_score) answer.push_back(i+1);
  }

  return answer;
}


    
    
profile
개발합시다!

0개의 댓글