2021년 7월 13일 화요일

Ubuntu 20.04, DOCKER, CUDA 11.0 기반 NVIDIA-DOCKER, 텐서플로우, 파이토치 설치 및 사용기

이 글은 우분투 20.04, 도커, CUDA 11.0 기반 NVIDIA-DOCKER, Tensorflow, Pytorch 설치 및 사용 방법을 간단히 설명한다. NVIDIA-DOCKER를 이용하면, 딥러닝 프레임웍 설치 시 많은 문제를 일으키는 패키지 의존 에러 없이, 미리 설치된 딥러닝 관련 패키지를 이용해 편리하게 딥러닝 모델을 개발할 수 있다. 또한, 아마존, 구글 같이 비싼 기계학습 딥러닝용 클라우드를 사용하지 않고도, 직접 여러사람이 네트워크로 접속할 수 있는 서버를 개발할 수 있다.

NVIDIA DOCKER 아키텍처 구조

여기서는 엔비디아 CUDA및 도커는 알고 있다는 전제하에 진행한다. 관련 내용은 다음 링크를 참고한다.

NVIDIA-DOCKER 설치

설치는 다음 링크를 참고한다.

다 설치한 후 다음 명령을 입력해 정상 출력되면 성공한 것이다.
sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi

NVIDIA 도커 이용하기
다음과 같이 NVIDIA 이미지 내 명령을 실행한다.
sudo nvidia-docker run --rm hello-world

apt-cache madison nvidia-docker2 nvidia-container-runtime

sudo docker run -it nvidia/cuda:11.0-base

도커 텐서플로우 설치 및 실행
이제 다음 명령으로 도커 텐서플로우 이미지를 설치하고, 실행한다. 
sudo docker run -it --rm --gpus all tensorflow/tensorflow:latest-gpu python -c "import tensorflow as tf; tf.config.list_physical_devices('GPU')"

텐서플로우 이미지 내 우분투를 실행해 본다. 
sudo docker run -it tensorflow/tensorflow:latest-gpu
exit

텐서플로우 소스코드 예제를 다음과 같이 실행해 본다. 화면처럼 출력되면 성공한 것이다.
cd ~
git clone https://github.com/tensorflow/benchmarks
sudo docker run --gpus all -it --rm -v /home/ktw/benchmarks:/benchmarks tensorflow/tensorflow:latest-gpu python benchmarks/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --num_gpus=1 --model resnet50 --batch_size 64

파치토치 도커 이미지 설치 및 실행
다음과 같이 파이토치 도커 이미지를 설치하고, 실행한다. 참고로, NVIDIA DRIVER가 서로 호환되지 않은 도커 이미지라면 드라이버 호환 에러가 발생한다. 이 경우, 여기 링크에 드라이버 버전을 확인하고, 해당 도커를 설치해 실행하기를 바란다.
sudo docker pull nvcr.io/nvidia/pytorch:21.02-py3
sudo docker run --gpus all -it -p 8888:8888 nvcr.io/nvidia/pytorch:21.02-py3

아래와 같이 파이썬 코드를 입력해 실행해 본다.
python
import torch
print(torch.cuda.device_count())
print(torch.cuda.current_device())
print(torch.cuda.get_device_name(torch.cuda.current_device()))
exit()

쥬피터 노트북 실행
다음과 같이 쥬피터 노트북을 도커에서 실행해 본다.
jupyter notebook

크롬을 실행하고, http://localhost:8888/ 주소를 입력한다.

노트북에 파이썬 모듈을 하나 생성하고 다음 코드를 입력해 실행한다.
Input the below code
import torch import torch.nn as nn import torch.nn.functional as F class Net(nn.Module): def __init__(self): super(Net, self).__init__() # 1 input image channel, 6 output channels, 3x3 square convolution # kernel self.conv1 = nn.Conv2d(1, 6, 3) self.conv2 = nn.Conv2d(6, 16, 3) # an affine operation: y = Wx + b self.fc1 = nn.Linear(16 * 6 * 6, 120) # 6*6 from image dimension self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): # Max pooling over a (2, 2) window x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) # If the size is a square you can only specify a single number x = F.max_pool2d(F.relu(self.conv2(x)), 2) x = x.view(-1, self.num_flat_features(x)) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x def num_flat_features(self, x): size = x.size()[1:] # all dimensions except the batch dimension num_features = 1 for s in size: num_features *= s return num_features net = Net() print(net)

아래와 같이 출력되면 딥러닝 모델이 정상 실행된 것이다.

이제, 복잡한 딥러닝 패키지를 설치하고 설정할 필요없어, 도커 기반으로 딥러닝 모델을 손쉽게 개발할 수 있다.

참고

2021년 7월 6일 화요일

3차원 포인트 클라우드와 언리얼 연결

이 글은 3차원 포인트 클라우드와 언리얼 연결 방법을 간단히 정리한다. 관련 활용 사례는 다음을 참고한다.


플러그인 활성화
언리얼을 실행한다. 
메뉴에서 편집 > 플러그인을 선택한다.

"LiDAR 포인트 클라우드 지원"을 검색하고, 활성화됨 박스를 체크한다.

LAS파일 포인트 클라우드 임포트
새 프로젝트를 생성한다. 임포트할 LAS 포인트 클라우드 파일을 선택한 뒤 콘텐츠 브라우저에 드래그한다. 

콘텐츠 브라우저에서 포인트 클라우드를 뷰포트 안으로 드래그한다. 콘텐츠 브라우저에서 포인트 클라우드를 우클릭하고 편집한다. 콜리전을 클릭하고 드롭다운 메뉴에서 콜리전 빌드를 선택한다. 플레이를 클릭한다.

포인트 클라우드 편집
콘텐츠 브라우저에서 포인트 클라우드 를 우클릭하고 편집을 선택한다. 포인트를 선택하고 나면 선택한 포인트를 숨기기, 삭제 또는 추출 할 수 있다.

레퍼런스

IoT 연결을 위한 언리얼기반 인터넷 서버 데이터 실시간 획득 방법

이 글은 IoT 연결을 위한 언리얼기반 인터넷 서버 데이터 실시간 획득 방법을 간략히 설명한다. 이 글에서 사용하는 방법을 이용해 IoT와 언리얼을 연결해, 물리 세계와 가상 세계를 연결하는 간단한 메타버스, 디지털 트윈 앱을 개발할 수 있다.

물리 세계 - 센서 - 아두이노 - 시리얼포트 - 인터넷 서버 - 언리얼 - 가상 디지털 세계

언리얼 네트워크 통신 방법은 다양하다. 제일 편한 방법은 누군가 만들어놓은 플러그인을 사용하는 것이다. 그럼 코딩없이 블루프린트에서 데이터를 교환할 수 있다. 직접 개발하려면 C++을 사용해야 한다. 이 경우, 라이센스 비용 없이 기능을 구현할 수 있고, 실무적으로는 이런 방식이 복잡한 문제인 경우 유연성이 높아 자주 사용한다.

IoT 장치와 연결하기 위해서는 다양한 데이터소스와 연결하는 프로토콜을 구현해야 한다. 데이터소스는 Serial port, Socket, Websocket, HTTPsocket, MQTT, TCP/IP, UDP 외에 MySQL, MongoDB와 같은 데이터베이스가 될 수도 있다. C++로 구현할 경우, 언리얼 개발사에서 제공하는 예제가 그리 많지 않다. 그래서, 소스코드를 확인하고, 개발 문서를 체크해야 한다.

여기서는 대표적인 방법을 설명한다.

SocketIOClient 방법
이는 기본 제공하는 SocketIOClient 컴포넌트를 블루프린트에서 개발하는 방법이다. 개발 방법을 이해하기 쉽게, IoT에서 데이터를 받아 그 값을 인터넷으로 방송하는 new wind서버를 만든다. new wind 토픽의 값은 아두이노 센서에서 얻은 데이터로 한다. 개발 순서는 다음과 같다. 

1. 아두이노를 연결하고 펌웨어 설치(파일>예제>Firmata>StandardFirmataPlus 선택 후 업로드)(참고)
2. Socket이란 이름의 블루프린터를 액터를 파생받아 생성
3. 블루프린트를 다음과 같이 코딩함 
4. 서버 개발을 위한 폴더 생성 후 node 패키지 설치
npm install --save express
npm install johnny-five --save
npm install serialport
npm i socket.io
npm i http
npm i util

5. 서버를 server.js이름으로 코딩
해당 패키지의 메뉴얼(Johnney-five manual)을 참고해, 아두이노에서 센서 데이터를 가져와 변수로 저장한 후, 코드에서 io.emit() 에 해당하는 부분에 해당 변수값을 넣어준다. 

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var util = require('util');
var five = require('johnny-five'); // nw.require('nwjs-j5-fix').fix();
var SerialPort = require("serialport");

var clients = [];

io.on('connection', function(socket){
 clients.push(socket.id);
 var clientConnectedMsg = 'User connected ' + util.inspect(socket.id) + ', total: ' + clients.length;
 console.log(clientConnectedMsg);
socket.on('disconnect', function(){
  clients.pop(socket.id);
  var clientDisconnectedMsg = 'User disconnected ' + util.inspect(socket.id) + ', total: ' + clients.length;
  console.log(clientDisconnectedMsg);
 })
});

var sensorValue = 0;
var board = new five.Board();

board.on('ready', function () {
  var sensor = new five.Sensor("A0");
  // Scale the sensor's data from 0-1023 to 0-10 and log changes
  sensor.on("change", function() {
    sensorValue = this.scaleTo(0, 1000);
    // console.log(sensorValue);
  });
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

function getRandomInRange(min, max) {
  return Math.random() * (max - min) + min;
}
function sendWind() {
 console.log('Wind sent to user: ' + sensorValue);
 io.emit('new wind', sensorValue); // getRandomInRange(0, 360));
}
setInterval(sendWind, 3000);

6. 서버 실행
node server.js

7. 언리얼 레벨에 개발된 블루프린트 socket 액터를 드래그&드롭함
8. 언리얼 플레이

다음은 개발 결과이다. 서버로 부터 데이터를 네트워크로 잘 받아오고 있는 것을 확인할 수 있다.


FSocket 방법
C++을 이용해 다음 소켓 클래스를 직접 사용해 서버와 데이터를 주고 받는다.

FSocket* Socket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(NAME_Stream, TEXT("default"), false);

FIPv4Address ip(127, 0, 0, 1);
TSharedRef<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
addr->SetIp(ip.Value);
addr->SetPort(65500);

bool connected = Socket->Connect(*addr);

레퍼런스

2021년 7월 4일 일요일

간단한 실시간 센서 데이터 가시화를 위한 아두이노와 언리얼 연결 방법

이 글은 아두이노와 언리얼(Unreal) 게임엔진을 연결하는 간단한 방법을 설명한다. 이 글은 실시간으로 시리얼 통신에서 얻은 센서값을 언리얼의 액터 메쉬의 머터리얼(재질) 색상에 직접 설정하는 방법을 포함한다. 센서와 언리얼 연결 방법을 보여주기 위해, 별도 상용 언리얼 플러그인을 사용하지 않고, C++에서 통신 모듈을 간단히 개발해 사용한다. 이 글에서 설명하는 같은 방식으로 텍스처, 광원, 메쉬 위치 등을 센서 값에 따라 실시간으로 변경할 수 있다.

이 글의 개발 결과는 다음과 같다. 화면에서 각 큐브는 물리적 센서에서 얻은 데이터값을 색상으로 표시한다. 

개발 환경

언리얼과 아두이노 IDE를 각각 설치한다.

이 글은 언리얼에 대한 기본 개념은 설명하지 않는다. 이 내용은 여기를 참고한다. 아두이노에 대한 내용은 여기를 참고한다.

개발 순서

간단한 구현을 위해, 센서는 아두이노에서 얻은 값을 사용하고, RS232 시리얼 통신을 이용한다. 

센서 - 아두이노 보드 - RS232 시리얼 통신 - 언리얼 - 데이터 가시화

주요 개발 순서는 다음과 같다. 

1. 아두이노 IDE에서 센서 데이터 획득하는 회로와 프로그램 작성. 아두이노 A0핀에 광센서 등 사용해 시그널을 A0에 입력. LED는 9번핀에 연결.

주요 코드는 다음과 같음.
const int analogInPin = A0;  // Analog input pin that the potentiometer is attached to
const int analogOutPin = 9; // Analog output pin that the LED is attached to

int sensorValue = 0;        // value read from the pot
int outputValue = 0;        // value output to the PWM (analog out)

void setup() {
  Serial.begin(9600);
}

void loop() {
  // read the analog in value:
  sensorValue = analogRead(analogInPin);
  
  // map it to the range of the analog out:
  outputValue = map(sensorValue, 0, 500, 0, 255);
  analogWrite(analogOutPin, outputValue);

  // print the results to the Serial Monitor:
  Serial.write(outputValue);
  delay(50);
}

2. 언리얼 실행. 빈 프로젝트를 C++ 형식으로 생성
3. 블루프린트 작성. 기본 클래스는 Actor에서 파생받음. 이름은 ColoredCube로 설정. 블루프린트에서 Cube 메쉬 객체 설정

4. 액터의 메쉬 객체에 적용할 재질 작성 및 설정. 재질 이름은 Color로 설정하고 다음과 같이 재질 작성
5. ColoredCube에 Color 재질을 설정
6. Visual Studio에서 Serial Port로 부터 센서 데이터를 얻는 모듈을 작성. 헤더 파일은 다음과 같음. 소스 코드는 여기를 참고.
#pragma once

#include "CoreMinimal.h"
#include "CoreTypes.h"

/**
 * 
 */
class IOT6_API SerialPort
{
public:
SerialPort();
~SerialPort();

bool open(const TCHAR* sPort, int nBaud = 9600);
void close();

int write(TArray<uint8>& Buffer);
int read(TArray<uint8>& Buffer);

private:
void* m_PortHandle;
};

7. Visual Studio에서 액터 소스 파일 편집. Serial Port에서 얻은 센서 데이터로 액터 메쉬의 머터리얼을 얻어 생상을 설정함. 주요 코드는 다음과 같음.
#include "ColoredCube.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Components/StaticMeshComponent.h"
#include "Materials/MaterialInterface.h"

#pragma optimize("", off)

class SerialPortInstance
{
public:
SerialPortInstance();
~SerialPortInstance();

SerialPort _port;
};

SerialPortInstance::SerialPortInstance()
{
_port.open(_T("COM3"));  // 이 부분은 각자 시리얼 통신 포트 이름으로 변경해야 함
}

SerialPortInstance::~SerialPortInstance()
{
_port.close();
}


SerialPortInstance _serial;

// Sets default values
AColoredCube::AColoredCube()
{
  // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void AColoredCube::BeginPlay() // 최초 액터 생성 시 실행
{
Super::BeginPlay();

auto Cube = FindComponentByClass<UStaticMeshComponent>();
auto Material = Cube->GetMaterial(0);

_DynamicMaterial = UMaterialInstanceDynamic::Create(Material, NULL);
Cube->SetMaterial(0, _DynamicMaterial);

_randomColor = FMath::Rand() % 64;
}

// Called every frame
void AColoredCube::Tick(float DeltaTime)  // 프레임 렌더링 전에 호출됨
{
Super::Tick(DeltaTime);

TArray<uint8> Buffer;
Buffer.Add(0);
int len = _serial._port.read(Buffer);
if (len)
{
FString string = FString::Printf(TEXT("Data = %d"), Buffer[0]);
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, string);

float blend = ((float)Buffer[0] + _randomColor) / (255.0);
// float blend = 0.5f + FMath::Cos(GetWorld()->TimeSeconds) / 2;
_DynamicMaterial->SetScalarParameterValue(TEXT("Blend"), blend);
}
}

#pragma optimize("", on)

구현 결과
이 결과는 다음과 같다. 센서 값에 따라 큐브 색상에 실시간으로 변화하는 것을 확인할 수 있다.

실제 구현 코드는 아래와 같다.
마무리
이 방법을 응용하면 IoT와 같은 객체에서 얻은 센서값을 언리얼과 연결할 수 있다. 이렇게 연결하여, 물리세계를 게임엔진 기반 3차원 가상세계로 맵핑하여 가시화하거나 그 반대로 액추에이터를 제어하는 앱을 개발할 수 있다. 

레퍼런스

2021년 6월 9일 수요일

심심할때 따라해보는 언리얼엔진 게임 개발하기

이 글은 심심할때 따라해보는 언리얼엔진 게임 개발 방법에 대한 이야기이다. 최근 유행하는 메타버스는 언리얼엔진, 유니티와 같은 게임엔진으로 손쉽게 개발할 수 있다.

머리말
Unreal Engine 5는 2D모바일 게임에서 콘솔 타이틀까지 모든 것을 제작할 수 있다. 

언리얼 엔진 5로 개발하는 것은 초보자에게 매우 간단한다. 블루 프린트 비주얼 스크립팅 시스템을 사용하면 한 줄의 코드를 작성하지 않고도 게임을 만들 수 있다. 

언리얼 엔진는 설치를 위해 Epic Games Launcher를 사용한다. 런처를 다운로드하려면 먼저 계정을 만들어야한다. 계정을 만든 후 운영 체제에 맞는 런처를 다운로드하라.
기본 선택 항목은 시작용 콘텐츠, 템플릿, 기능팩 및 엔진 소스이다. 체크된 상태로 두는 것이 좋다. 

프로젝트 시작
시작 버튼 중 하나를 클릭하여 프로젝트 브라우저를 연다. C++ 유형을 선택하고 빈 템플릿을 선택한다. 프로젝트를 생성하면 편집기가 열린다. 편집기는 여러 패널로 분할된다.

콘텐츠 브라우저: 이 패널은 모든 프로젝트 파일을 표시한다. 이를 사용하여 폴더를 만들고 파일을 구성한다. 검색 창이나 필터를 사용하여 파일을 검색 할 수 있다.
  • 모드: 이 패널을 사용하면 Landscape Tool 및 Foliage Tool 과 같은 도구를 선택할 수 있다. 
  • 배치 도구: 조명 및 카메라와 같은 다양한 유형의 오브젝트를 레벨에 배치 할 수 있다.
  • 월드 아웃라이너(World Outliner): 현재 수준의 모든 개체를 표시한다. 관련 항목을 폴더에 넣어 목록을 구성할 수 있다. 유형별로 검색하고 필터링하는 기능이 있다.
  • 디테일: 선택한 개체 속성이 여기에 표시된다. 이 패널을 사용하여 개체의 설정을 편집한다. 변경 사항은 개체의 해당 인스턴스에만 영향을 준다. 예를 들어, 두 개의 구가 있고 하나의 크기를 변경하면 선택한 오브젝트에만 영향을 준다.
  • 도구 모음: 다양한 기능이 포함되어 있다. 가장 많이 사용하는 것은 Play 이다.
  • 뷰포트: 마우스 오른쪽 버튼을 클릭 하고 마우스를 움직여 주변을 둘러 볼 수 있다. 이동하려면 마우스 오른쪽 버튼을 누른 상태 에서 WASD 키를 사용한다.
컨텐츠 만들기
미리 무료 3차원 모델 파일 제공해 주는 웹사이트에서 파일을 다운로드 받아 놓는다. 이 예제에서는 아래 웹사이트를 사용했다. 
언리얼에서 파일을 사용하려면 먼저 임포트해야한다. 콘텐츠 브라우저로 이동하여 가져 오기를 클릭한다. 파일 브라우저를 사용하여 다운로드한 파일이 있는 폴더를 찾는다. 두 파일을 모두 드래그하여 선택 하고 열기를 클릭 한다. Unreal은 .fbx 파일에 대한 몇 가지 가져 오기 옵션을 제공한다. 
모델 임포트 옵션

참고로, 파일을 가져올 때 명시적으로 수행할 때까지 실제로 프로젝트에 저장되지 않는다. 다음과 같은 방법으로 파일을 저장할 수 있다 마우스 오른쪽 단추로 클릭 해당 파일을 선택하고 저장을 File>Save All을 선택하여 모든 파일을 한 번에 저장할 수 있다.

Unreal에서는 모델을 메시라고 한다. 메시를 만들었으니 레벨에 배치 할 차례이다.

레벨에 메시를 추가하려면 콘텐츠 브라우저에서 모델을 왼쪽클릭하고 뷰포트로 드래그 한다. 왼쪽 클릭을 해제 하면 메시가 배치된다. 레벨의 개체는 이동 , 회전 및 크기 조정이 가능 한다. 이들의 키보드 단축키는 W , E 및 R 이다. 

재질 만들기
모델을 자세히 살펴보면 전혀 노란색이 아님을 알 수 있다. 모델에 약간의 색상과 디테일을 부여하려면 재질을 만들어야한다 .

재질은 표면이 어떻게 보이는지 결정한다. 기본 수준에서 재료는 다음 네 가지를 정의한다.
  • 기본 색상: 표면의 색상 또는 질감이다. 
  • 금속성: 표면이 얼마나 "금속과 같은지"를 설정한다. 순수한 금속은 최대 금속 값을 가지며 직물은 0 값을 갖는다.
  • 반사광: 비금속 표면의 광택을 제어한다. 예를 들어 세라믹은 Specular 값이 높지만 점토는 그렇지 않다.
  • 거칠기: 최대 거칠기를 가진 표면은 광택이 없다. 바위와 나무와 같은 표면에 사용된다.
머티리얼을 생성하려면 콘텐츠 브라우저로 이동하여 녹색 새로 추가 버튼을 클릭한다. 

생성 할 수 있는 자산 목록 메뉴가 나타난다. 재료를 클릭한다. 재질 이름을 Bulb_Material 로 지정한 다음 파일을 두 번 클릭하여 재질 편집기에서 연다.

재료 편집기는 5개 주요 패널로 구성된다.
  • 그래프: 이 패널는 모든 노드와 결과 노드가 포함된다. 
  • 세부 정보: 선택한 모든 노드의 속성이 여기에 표시된다. 
  • 뷰포트: 머티리얼을 표시할 미리보기 메시가 포함되어 있다. 
  • 팔레트: 재료에 사용할 수 있는 모든 노드 목록이다.
노드는 재료 대부분을 구성한다. 다양한 유형의 노드를 사용할 수 있으며 다양한 기능을 제공한다. 노드는 화살표가있는 원으로 표시되는 입력 및 출력을 가질 수 있다. 입력은 왼쪽에 있고 출력은 오른쪽에 있다.

모델에 색상과 디테일을 추가하려면 텍스처가 필요한다. 텍스처는 2D이미지이다. 일반적으로 3D모델에 투영되어 색상과 세부 정보를 제공한다.

텍스처링하려면 앞서 미리 다운로드한 텍스쳐 jpg 파일을 사용한다. TextureSample과의 노드는 물질 내에서 텍스처를 사용할 수 있다.

Palette 패널로 이동하여 TextureSample을 검색한다. 왼쪽 클릭을 누른 상태에서 그래프로 드래그하여 노드를 추가한다. 


디테일 패널로 이동하여 Texture 오른쪽에 있는 드롭 다운을 클릭한다. Texture를 선택 한다. 미리보기 메시에서 텍스처를 보려면 Result 노드에 연결해야 한다. 도구 모음에서 적용을 클릭 하여 재료를 업데이트하고 재료 편집기를 닫는다. 

블루 프린트로 사물 객체 만들기
블루 프린트를 사용하면 개체에 대한 사용자 지정 동작을 만들 수 있다. 물체는 물리적인 것 이거나 시스템과 같은 추상적인 것 일 수 있다. 머티리얼과 마찬가지로 블루 프린트는 노드 기반 시스템을 사용한다. 즉, 노드를 만들고 연결하기만 하면 된다. 코딩이 필요하지 않다. 코드 작성을 선호하는 경우 대신 C ++를 사용할 수 있다.

블루 프린트는 사용하기 쉽지만 C ++ 코드만큼 빠르지는 않다. 따라서 복잡한 알고리즘과 같이 계산량이 많은 것을 사용해야하는 경우 C ++ 사용을 고려해야 한다.

콘텐츠 브라우저로 이동하여 새로 추가를 클릭 한다. 목록에서 Blueprint Class를 선택한다. 부모 클래스를 선택하라는 창이 나타난다. 블루 프린트는 선택한 부모 클래스의 모든 변수, 함수 및 구성 요소를 상속한다. 액터를 선택하고 새 파일 이름을 Bulb_Blueprint로 지정 한다. 

더블 클릭하여 Bulb_Blueprint 연다. 블루 프린트 편집기 열기를 클릭한다. 블루 프린트 에디터에는 4개 메인 패널이 있다.
  • My Blueprint : 이 섹션은 주로 그래프, 함수 및 변수를 관리하는 데 사용된다.
  • 세부 정보: 현재 선택한 항목 속성을 표시한다.
  • 그래프: 모든 노드와 로직이 여기에 입력된다.
  • 뷰포트: 메인 에디터 뷰포트와 동일한 컨트롤을 사용하여 이동하고 둘러 볼 수 있다.
  • 컴포넌트: 블루 프린트가 자동차라면 컴포넌트는 자동차를 구성하는 빌딩 블록이다. 구성 요소는 물리적 개체에 국한되지 않는다. 예를 들어 자동차를 움직이기 위해 이동 구성 요소를 추가 할 수 있다. 비행 부품을 추가하여 자동차를 날릴 수도 있다.

여기서 테이블을 만들것이다. 테이블은 두 가지 구성 요소를 사용한다.
  • 원통: 단순한 흰색 원통이다. 이것은 모델이 앉는 기초가 될 것이다.
  • 스태틱 메시: 이 컴포넌트는 모델 메시를 표시한다.
컴포넌트 패널에서 빈 영역을 마우스 왼쪽 버튼으로 클릭하여 Cylinder 구성 요소를 선택한다. 다음으로 Add Component를 클릭하고 목록에서 Static Mesh를 선택한다.

전구 모델을 표시하려면 스태틱 메시 컴포넌트를 선택한 다음 디테일 탭을 클릭한다. Static Mesh 오른쪽에 있는 드롭 다운을 클릭하고 전구 Model을 선택한다.

블루 프린트 노드에는 실행핀이라는 특수 핀이 있다. 왼쪽의 핀은 입력이고 오른쪽의 핀은 출력이다. 예를 들어, 노드 A 와 노드 B 는 입력 핀이 연결되어 있기 때문에 실행된다. 스크립팅을 시작하려면 이벤트 그래프 탭으로 다시 전환한다.

그래프의 빈 공간을 마우스 오른쪽 버튼으로 클릭 하여 사용 가능한 노드 메뉴를 표시한다. AddLocalRotation을 검색한다 . 베이스와 바나나를 회전해야하므로 루트 구성 요소만 회전하면된다. AddLocalRotation (DefaultSceneRoot)을 선택하라. 회전 값을 설정하려면 Delta Rotation 입력으로 이동 하여 Z 값을 1.0으로 변경한다. 그러면 블루 프린트가 Z 축을 중심으로 회전한다. 값이 클수록 턴테이블이 더 빨리 회전한다.

턴테이블을 계속 회전하려면 매 프레임마다 AddLocalRotation 을 호출해야한다. 매 프레임마다 노드를 실행하려면 Event Tick 노드를 사용하라. Event Tick 노드 의 출력 핀을 AddLocalRotation 노드 의 입력 핀으로 드래그한다.

다음과 같이 해당 모델을 선택해, 디테일에서 Movable 옵션을 설정한다. 

마지막으로 툴바로 이동하여 컴파일(F7)을 클릭 하여 블루 프린트를 업데이트한 다음, 블루 프린트 편집기를 닫는다. 그럼 회전하는 모델을 확인할 수 있다.

키보드 등 입력 이벤트 처리
3차원 객체와 상호반응하기 위해 이벤트를 처리한다. 언리얼은 모든 입력처리를 UInputComponent에서 처리한다. 모든 액터는 입력 컴포넌트를 가진다. 이 컴포넌트가 있으면 입력 이벤트와 바인딩하는 방법은 동일하다. 

언리얼은 다양한 입력과 호환성을 고려해 입력 바인딩 과정을 먼저 거쳐야 입력 이벤트를 액터에 전달할 수 있다.

편집>프로젝트 세팅 메뉴>입력 메뉴를 선택하여 다음과 같이 입력 이벤트 이름과 해당 입력 조합을 설정한다. 아래 경우는 키보드 B를 맵핑하였다.

다음과 같이 앞서 만든 액터 중 하나를 선택해 블루프린트를 편집한다. 우선 액터의 입력을 활성화하고, 입력 이벤트 발생할 때마다 화면에 메시지를 출력한 후 정해진 축으로 이동하도록 하였다.

실행 결과는 다음과 같다.

블루프린트로도 작업할 수 있지만, 내부 동작을 이해하기 위해 C++의 코드를 확인해 보겠다. 예를 들어, Pawn을 기본 클래스로 하는 C++ 클래스인 경우, 다음과 같이 입력이 발생할 경우 특정 함수를 호출해 액터 등을 동작을 시킬 수 있다.
UCLASS()
class AMovePawn : public APawn 
{
GENERATED_BODY()

public:
    // ~Overrides: APawn
virtual void SetupPlayerInputComponent(UInputComponent* InputComponent) override;

    // Declare your callbacks here
    // MoveForward(); 
};
void AUnrealisticPawn::SetupPlayerInputComponent(UInputComponent* InputComponent)
{
    // Always call this.
    Super::SetupPlayerInputComponent(InputComponent);

    // This component belongs to the possessing Player Controller
    InputComponent->BindAxis("Go", this, &AMovePawn::MoveForward);
}


레퍼런스

2021년 6월 8일 화요일

리눅스에서 VSCode, CMake와 GDB 개발환경 만들기

이 글은 리눅스에서 VSCode, CMake와 GDB 개발환경 만들기 방법을 간단히 정리한다.
리눅스에서 개발환경은 윈도우보다 좋지는 않다. 옛날에는 Eclipse 정도가 최신이었지만, 지금은 VSCode, PyCharm 등이 있어 비쥬얼한 개발환경 지원이 좋아졌다. 이 글은 우분투에서 VSCode에 대한 설치 및 사용 방법만 언급한다.

VS Code, CMake 및 GDB 설치 
VS Code 용 C ++ 확장을 설치한다. 확장보기 윈도우( Ctrl + Shift + X )에서 'c ++'를 검색하여 C / C ++ 확장을 설치할 수 있다.

g++ 컴파일러를 사용하여 Linux에서 소스 코드를 컴파일한다. GDB를 사용하여 디버깅한다. 이러한 도구는 Ubuntu에 기본적으로 설치되지 않으므로 설치해야한다. 터미널 창을 열고 다음 명령을 입력하라.
gcc -v
sudo apt-get update

다음 명령으로 GNU 컴파일러 도구와 GDB 디버거를 설치한다.
sudo apt-get install build-essential gdb

Hello World 만들기
다음과 같이 터미널 창에서 프로젝트 저장용 빈 폴더를 만든다. 
mkdir projects
cd projects
mkdir helloworld
cd helloworld
code .

이 code .명령은 "작업 공간"이 되는 현재 작업 폴더에서 VS Code를 실행한다. 작업 공간 의 폴더에 세 개의 파일을 만든다.

tasks.json (컴파일러 빌드 설정)
launch.json (디버거 설정)
c_cpp_properties.json (컴파일러 경로 및 IntelliSense 설정)
Hello World 소스 코드 파일

파일 탐색기 제목 표시 줄에서 새 파일을 선택하고 파일 이름을 helloworld.cpp로 한다. 다음 소스 코드를 붙여 넣는다.

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};

    for (const string& word : msg)
    {
        cout << word << " ";
    }
    cout << endl;
}

이제 Ctrl + S 를 눌러 파일을 저장하라. 파일이 VS Code 사이드 바에 있는 파일 탐색기에 나열된다.

helloworld.cpp 빌드
주 메뉴에서 터미널 > 기본 빌드 작업 구성을 선택한다. g++ 빌드 환경을 설정한다. 그러면 폴더에 tasks.json파일이 생성된다. 새 tasks.json파일은 아래 JSON과 유사하다.
{
  "version": "2.0.0",
  "tasks": [
    {
      "type": "shell",
      "label": "g++ build active file",
      "command": "/usr/bin/g++",
      "args": ["-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}"],
      "options": {
        "cwd": "/usr/bin"
      },
      "problemMatcher": ["$gcc"],
      "group": {
        "kind": "build",
        "isDefault": true
      }
    }
  ]
}

빌드 실행
빌드 작업을 실행하려면, Ctrl + Shift + B를 누르거나 터미널 기본 메뉴에서 빌드 작업을 실행한다.

helloworld.cpp 디버그
F5키를 l눌러 프로그램을 디버깅한다. 주 메뉴에서 실행 > 구성 추가 ... 를 선택한 다음 C ++ (GDB / LLDB) 를 선택한다. 미리 정의된 디버깅 구성에 대한 드롭 다운이 표시된다. g++ 빌드 및 디버그 활성 파일을 선택한다.

VS Code는 launch.json파일을 만들고 편집기에서 열고 'helloworld'를 빌드하고 실행한다.
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "g++ build and debug active file",
      "type": "cppdbg",
      "request": "launch",
      "program": "${fileDirname}/${fileBasenameNoExtension}",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${workspaceFolder}",
      "environment": [],
      "externalConsole": false,
      "MIMode": "gdb",
      "setupCommands": [
        {
          "description": "Enable pretty-printing for gdb",
          "text": "-enable-pretty-printing",
          "ignoreFailures": true
        }
      ],
      "preLaunchTask": "g++ build active file",
      "miDebuggerPath": "/usr/bin/gdb"
    }
  ]
}

디버깅 세션 시작
F5 키를 누르 거나 주 메뉴에서 실행> 디버깅 시작을 선택한다. 디버깅 제어판에서 Step over 아이콘을 클릭하거나 누른다. 통합 터미널의 디버그 콘솔 탭에서 GDB가 출력하는 진단 정보 출력을 볼 수 있다.

레퍼런스