언리얼 엔진 프로젝트에서 C++ 클래스 삭제하기


1. Visual Studio를 닫는다

2. UE4 Editor를 닫는다


탐색기에서 프로젝트 폴더를 찾아서


3. 프로젝트이름/Source/프로젝트이름/Public, Private 폴더 안에서 *.h, *.cpp 파일 삭제

4. 프로젝트 이름/Binaries/ 폴더 삭제


프로젝트 이름/프로젝트이름.uproject 파일을 찾아서


5. uproject 파일에서 마우스 우측 > Generate Visual Studio Project Files 선택

6. UE4 Editor 를 사용하여 정상적으로 프로젝트를 다룬다



Posted by cwisky
Unreal C++/C++ Actor Example2018. 11. 22. 00:45

C++ 액터 클래스를 이용하여 복잡한 로직을 작성하고 BP를 파생하여 사용하는 예


레벨 위에 TargetPoint 4개를 큰 사각형이 되도록 배치하고 그 위치를 향하여 계속 이동하는 큐브를 구현하고자 한다. 이러한 로직이 그다지 복잡한 것은 아니지만 블루프린트를 이용하기에는 적합하지 않을 정도로 복잡해지므로 C++ 액터 클래스를 생성하고 그 안에 사각형 운동을 하는 로직을 작성한 다음 완성된 C++액터 클래스를 기반으로 블루프린트 액터를 생성하여 사용하면 C++의 장점과 블루프린트의 장점을 동시에 활용할 수 있다


CppActor.h

#pragma once


#include "Runtime/Engine/Classes/Engine/TargetPoint.h"

#include "CoreMinimal.h"

#include "GameFramework/Actor.h"

#include "CppActor.generated.h"


UCLASS(Blueprintable, BlueprintType)

class CPP_TEST_API ACppActor : public AActor

{

GENERATED_BODY()

public:

// Sets default values for this actor's properties

ACppActor();


protected:

// Called when the game starts or when spawned

virtual void BeginPlay() override;


public:

// Called every frame

virtual void Tick(float DeltaTime) override;


UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cpp")

TArray<ATargetPoint *> Arr;


UFUNCTION(BlueprintCallable, Category = "Cpp")

void MoveRect(float DeltaTime);

};




위의 헤더파일에 Arr 배열의 원소가 포인터로 선언된 것은 블루프린트에서 취급되는 모든 UObject와 그 하위 클래스의 변수들은 모두 포인터이다. 언리얼 에디터에서 레퍼런스라고 부르고 있지만 C++의 레퍼런스와는 달리 언리얼의 레퍼런스는 포인터를 의미하는 경우가 많다

https://answers.unrealengine.com/questions/351656/pointer-to-uobject-and-inheritance-in-bpc.html

All UObject (and its descendants) variables are pointers.

Calling it a reference is misleading. It is not a reference (&), it is a pointer.



CppActor.cpp

#include "CppActor.h"

#include "../Public/CppActor.h"



// Sets default values

ACppActor::ACppActor()

{

  // 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 ACppActor::BeginPlay()

{

Super::BeginPlay();

}


// Called every frame

void ACppActor::Tick(float DeltaTime)

{

Super::Tick(DeltaTime);


}


void ACppActor::MoveRect(float DeltaTime)

{

static int idx = 0;

static FVector * SpdVec = new FVector(0.0F, 200.0F*DeltaTime, 0.0F);

FVector ALoc, NextLoc;

ALoc = GetActorLocation();

int next = idx + 1 == 4 ? 0 : idx + 1;

NextLoc = Arr[next]->GetActorLocation();


switch (idx) {

case 0:

if (ALoc.Y >= NextLoc.Y) {

ALoc.Y = NextLoc.Y;

idx++;

SpdVec->X = -200.0F * DeltaTime; SpdVec->Y = 0.0F; SpdVec->Z = 0.0F;

}

break;

case 1:

if (ALoc.X <= NextLoc.X) {

ALoc.X = NextLoc.X;

idx++;

SpdVec->X = 0.0F; SpdVec->Y =-200.0F * DeltaTime; SpdVec->Z = 0.0F;

}

break;

case 2:

if (ALoc.Y <= NextLoc.Y) {

ALoc.Y = NextLoc.Y;

idx++;

SpdVec->X = 200.0F * DeltaTime; SpdVec->Y =0.0F; SpdVec->Z = 0.0F;

}

break;

case 3:

if (ALoc.X >= NextLoc.X) {

ALoc.X = NextLoc.X;

idx=0;

SpdVec->X = 0.0F;  SpdVec->Y = 200.0F*DeltaTime; SpdVec->Z = 0.0F;

}

break;

}

ALoc += (*SpdVec);

SetActorLocation(ALoc);

}



위에 있는 헤더파일에는 아래와 같은 내용이 있다


UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cpp")

TArray<ATargetPoint *> Arr;


ATargetPoint 클래스 객체의 포인터를 원소로 하는 배열이 선언되어 있는데, 이 배열의 원소는 Blueprint에서 초기화하여 Arr 변수에 전달하기 위해 EditAnywhere 를 선언한 것이다. 위의 클래스를 드래그하여 레벨에 배치하거나 위의 클래스를 기반으로 블루프린트 클래스를 생성하여 블루프린트 에디터를 열고 [클래스 디폴트] 를 선택하고 우측 [디테일] 뷰를 보면 Arr변수의 원소를 초기화할 수 있는 항목이 나타난다


블루프린트에서 TargetPoint 배열을 선언할 때 원소의 자료형은 [TargetPoint 오브젝트 레퍼런스]형으로 지정해야 하고 [인스턴스 편집가능]을 선택하면 언리얼 에디터의 레벨 위에서 위의 액터를 선택했을 때 우측 Detail 뷰에서 배열의 원소를 할당할 수가 있으므로 레벨에 배치한 ATargetPoint을 선택하여 할당해 줄 수 있다


TargetPoint 레퍼런스 배열의 선언




위의 블루프린트에서 선언한 TargetPoint 참조배열에 원소를 초기화하는 예



블루프린트에서 생성하고 원소가 초기화된 배열을 C++의 TArray<AtargetPoint *> Arr 배열에 전달하는 블루프린트의 예

Posted by cwisky
Unreal C++/C++ Milliseconds2018. 9. 27. 14:49

C++에서 밀리초(Milliseconds) 사용하는 예


#include <chrono> // c++ 11부터 지원


// ...


using namespace std::chrono;


milliseconds start = duration_cast< milliseconds >(

    system_clock::now().time_since_epoch()

);


// 측정 대상 로직


milliseconds end = duration_cast< milliseconds >(

    system_clock::now().time_since_epoch()

);


cout <<"처리 경과시간:" << end.count() - start.count() << endl;


Posted by cwisky
Unreal C++/C++ Object IO2018. 9. 14. 18:15

바이너리 모드로 객체를 파일에 입/출력하는 예


다수개의 클래스를 이용할 때 클래스 간의 연결문제를 경험하고 해결하기 위한 연습이므로 클래스 구성에 대한 다른 견해가 있을 수 있습니다


Main.cpp

#include <iostream>

#include <fstream>

#include <string>

#include <cstring>


#include "Student.h"

#include "StudentDAO.h"

#include "StudentView.h"

#include "StudentMgr.h"


using namespace std;


int StudentDAO::StudentCnt = 0;


int main()

{

StudentMgr mgr;

mgr.Process();


cout << "프로그램 종료...." << endl;

return 0;

}


Student.h

#pragma once

#include <cstring>


class Student {

int id;

char name[50];

public:

Student() {}

Student(int id, char* name);

void SetId(int id);

void SetName(char* name);

int GetId();

char* GetName();

};


Student.cpp

#include "Student.h"


Student::Student(int id, char * name){

this->id = id;

strcpy(this->name, name);

}


void Student::SetId(int id){

this->id = id;

}


void Student::SetName(char * name){

strcpy(this->name, name);

}


int Student::GetId(){

return id;

}


char * Student::GetName(){

return name;

}


StudentMgr.h

#pragma once


#include "Student.h"

#include "StudentView.h"

#include "StudentDAO.h"


class StudentMgr

{

public:

StudentMgr(){}

StudentView view;

StudentDAO dao;


void Process();

void list();

void insert();

void search();

void update();

void remove();

};


StudentMgr.cpp

#include <iostream>

#include "StudentMgr.h"

#include "Student.h"


using namespace std;


void StudentMgr::Process()

{

bool go = true;


while (go)

{

char m = view.Menu();

switch (m)

{

case 's': list(); break;

case 'i': insert(); break;

case 'f': search();  break;

case 'u': update();  break;

case 'd': remove();  break;

case 'x': go = false; break;

default: view.PrintError("입력오류");

}

}

}


void StudentMgr::list()

{

Student** arr = dao.GetList();

view.PrintLine();

view.PrintList(arr);

view.PrintLine();

}


void StudentMgr::insert()

{

Student* pSt = view.InputSt();

bool inserted = dao.Insert(pSt);

if (inserted) view.PrintMsg("저장 성공");

else view.PrintMsg("저장 실패");

}


void StudentMgr::search()

{

int id = view.InputSearchKey();

Student* pSt = dao.Search(id);

view.PrintSt(pSt);

}


void StudentMgr::update()

{

Student* pSt = view.InputUpdateSt();

bool updated = dao.Update(pSt);

view.PrintUpdateResult(updated);

}


void StudentMgr::remove()

{

int del = view.InputDeleteKey();

bool deleted = dao.Delete(del);

view.PrintDeleteResult(deleted);

}


StudentDAO.h

#pragma once

#include "Student.h"


class StudentDAO {

public:

static int StudentCnt;


StudentDAO() {};

Student** GetList();

int GetCount();

bool Insert(Student* pSt);

Student* Search(int id);

bool Update(Student* pSt);

bool Delete(int id);

};


StudentDAO.cpp

#include "StudentDAO.h"

#include <iostream>

#include <fstream>


using namespace std;


Student ** StudentDAO::GetList() {

ifstream in;

in.open("D:\\test\\student.dat", ios::binary);

if (!in.is_open()) {

cout << "파일열기 실패"<<endl;

return NULL;

}

int StCnt = GetCount();


Student** arr = new Student*[StCnt];

for (int i = 0; i < StCnt; i++) {

Student* pSt = new Student;

if (in.read((char*)pSt, sizeof(*pSt))) {

arr[i] = pSt;

}

}

StudentDAO::StudentCnt = StCnt;

in.close();

return arr;

}


int StudentDAO::GetCount()

{

ifstream in("D:\\test\\student.dat");

if (!in.is_open()) {

cout <<"파일열기 실패"<<endl;

return 0;

}

int cnt = 0;


Student obj; 

while (in.read((char*)&obj, sizeof(obj))) cnt++;

in.close();

return cnt;

}


bool StudentDAO::Insert(Student * pSt)

{

bool inserted = false;

ofstream outFile;

outFile.open("D:\\test\\student.dat", ios::binary | ios::app);


outFile.write((char*)pSt, sizeof(*pSt));

if (outFile.good()) inserted = true;

else inserted = false;

outFile.close();

delete pSt;

return inserted;

}


Student * StudentDAO::Search(int id)

{

ifstream inFile; 

inFile.open("D:\\test\\student.dat", ios::binary); 

Student* pObj = new Student; 

bool found = false;

while (inFile.read((char*)pObj, sizeof(*pObj))) {

if(pObj->GetId() == id){

found = true;

break;

}

inFile.close();

if (!found) pObj = NULL;

return pObj;

}


bool StudentDAO::Update(Student * pSt)

{

fstream file; 

file.open("D:\\test\\student.dat", ios::binary | ios::in | ios::out); 

Student obj;

bool updated = false;

while (file.read((char*)&obj, sizeof(obj))) { 

if (obj.GetId() == pSt->GetId()) { 


obj.SetId(pSt->GetId());

obj.SetName(pSt->GetName());

int pos = -1 * sizeof(obj); 

file.seekp(pos, ios::cur); 

file.write((char*)&obj, sizeof(obj));

cout << "학생정보 변경 성공" << endl;

if (file.good()) {

updated = true;

break;

}

file.close();

return updated;

}


bool StudentDAO::Delete(int id)

{

Student obj; 

ifstream inFile; 

inFile.open("D:\\test\\student.dat", ios::binary); 


ofstream outFile;

outFile.open("D:\\test\\temp.dat", ios::out | ios::binary); 

bool found = false;

while (inFile.read((char*)&obj, sizeof(obj))) { 

if (obj.GetId() != id) { 

outFile.write((char*)&obj, sizeof(obj)); 

}

else {

found = true;

}

}


inFile.close(); 

outFile.close(); 

remove("D:\\test\\student.dat");

rename("D:\\test\\temp.dat", "D:\\test\\student.dat");

return found ? true : false;

}


StudentView.h

#pragma once

#include "Student.h"

#include <string>


using namespace std;


class StudentView {

public:

char Menu();

void PrintList(Student** arr);

Student* InputSt();

int InputSearchKey();

int InputDeleteKey();

Student* InputUpdateSt();

void PrintSt(Student* pSt);

void PrintUpdateResult(bool updated);

void PrintDeleteResult(bool delted);

void PrintMsg(string msg);

void PrintError(string msg);

void PrintLine();

};


StudentView.cpp

#include "StudentView.h"

#include "StudentDAO.h"

#include "Student.h"


#include <iostream>


char StudentView::Menu()

{

cout << "메뉴: 리스트(s), 추가(i), 검색(f), 수정(u), 삭제(d), 종료(x):";

char m;

cin >> m;

return m;

}


void StudentView::PrintList(Student ** arr)

{

cout << "  ***** 학생정보 리스트 *****" << endl;

for (int i = 0; i < StudentDAO::StudentCnt; i++) {

cout << arr[i]->GetId() << "\t";

cout << arr[i]->GetName() << endl;

}

for (int i = 0; i < StudentDAO::StudentCnt; i++) {

delete arr[i];

}

delete[] arr;

}


Student * StudentView::InputSt()

{

int id;

string name;


cout << "번호 이름 (공백으로 구분):";

cin >> id >> name;

Student* pSt = new Student(id, (char*)name.c_str());

return pSt;

}


int StudentView::InputSearchKey()

{

cout << "검색할 학생 번호:";

int id;

cin >> id;

return id;

}


int StudentView::InputDeleteKey()

{

cout << "삭제할 학생 번호:";

int id;

cin >> id;

return id;

}


Student * StudentView::InputUpdateSt()

{

cout << "수정할 학생번호 이름:";

int id;

string name;

cin >> id >> name;

Student* pSt = new Student;

pSt->SetId(id);

pSt->SetName((char*)name.c_str());

return pSt;

}


void StudentView::PrintSt(Student * pSt)

{

if (pSt) {

cout << "  ***** 검색된 학생정보 *****" << endl;

cout << pSt->GetId() << "\t" << pSt->GetName() << endl;

delete pSt;

}

else {

cout << "  검색된 정보가 없습니다" << endl;

}

}


void StudentView::PrintUpdateResult(bool updated)

{

string msg;

if (updated) msg = "정상적으로 회원정보를 수정했습니댜";

else msg = "회원정보를 수정하지 못했습니다";

cout << "  --> 회원정보 수정결과:" << msg << endl;

}


void StudentView::PrintDeleteResult(bool delted)

{

string msg;

if (delted) msg = "정상적으로 회원정보를 삭제했습니댜";

else msg = "회원정보를 삭제하지 못했습니다";

cout << "  --> 회원정보 삭제결과:" << msg << endl;

}


void StudentView::PrintMsg(string msg)

{

cout << "  --> " << msg << endl;

}


void StudentView::PrintError(string msg)

{

cerr << "  --> " << msg << endl;

}


void StudentView::PrintLine()

{

cout << "------------------------------------------------------------" << endl;

}



Posted by cwisky

C++ 을 이용하여 이미지 파일을 한번에 읽어서 복사하는 예


#include <iostream>

#include <fstream>

#include <string>


using namespace std;


int main()

{

cout << "바이너리 파일 다루기" << endl;

//파일의 크기를 미리 확인하여 한번에 읽어서 다른 파일에 복사하는 예


ifstream in;

in.open("D:\\test\\mountains.jpg", ios::binary);


// 파일의 전체 크기를 확인한다

in.seekg(0, ios::end);

int length = in.tellg();

cout << "읽어올 파일의 전체 크기:" << length <<" 바이트"<< endl;


// 파일의 전체 크기만큼 메모리에 로드한다

in.seekg(0, ios::beg);

char* buf = new char[length];

in.read(buf, length);

in.close();


// 메모리에 저장된 파일 데이터를 다른 파일에 저장한다

ofstream out;

out.open("D:\\test\\copy.jpg", ios::binary);

out.write(buf, length);

out.close();


cout << "파일 복사 성공(D:\\test\\copy.jpg)" << endl;

return 0;

}


Posted by cwisky
Unreal C++/Text File CRUD2018. 9. 13. 17:42

앞서 작성했던 기능을 클래스 사용 버전으로 변경한 예


헤더파일과 소스파일을 분리하지 않고 한개의 파일에 모든 클래스를 선언함


#include <iostream>

#include <string>

#include <fstream>

#include <sstream>

#include <cstring>


using namespace std;


class MemVO {

private:

int id; string name; string phone;

public:

MemVO() {}

MemVO(int id, string name, string phone) :id(id), name(name), phone(phone) {

}

void SetId(int id) { this->id = id; }

void SetName(string name) { this->name = name; }

void SetPhone(string phone) { this->phone = phone; }

int GetId() { return id; }

string GetName() { return name; }

string GetPhone() { return phone; }

};



class MemDAO {

public:

static int MemCnt;


MemDAO() {};

MemVO** GetList();

int GetCount();

bool Insert(MemVO* pMem);

MemVO* Search(int id);

bool Update(MemVO* pMem);

bool Rewrite(MemVO** arr, int sz);

bool Delete(int id);

};



class MemView {

public:

char Menu() {

cout << "메뉴: 리스트(s), 추가(i), 검색(f), 수정(u), 삭제(d), 종료(x):";

char m;

cin >> m;

return m;

}


void PrintList(MemVO** arr) {

cout << "  ***** 회원정보 리스트 *****" << endl;

for (int i = 0; i < MemDAO::MemCnt; i++) {

cout << arr[i]->GetId() << "\t";

cout << arr[i]->GetName() << "\t";

cout << arr[i]->GetPhone() << endl;

}

delete[] arr;

}


MemVO* InputMem() {

int id;

string name;

string phone;


cout << "번호 이름 전화(공백으로 구분):";

cin >> id >> name >> phone;

MemVO* pMem = new MemVO(id, name, phone);

return pMem;

}


int InputSearchKey() {

cout << "검색할 회원 번호:";

int id;

cin >> id;

return id;

}


int InputDeleteKey() {

cout << "삭제할 회원 번호:";

int id;

cin >> id;

return id;

}


MemVO* InputUpdateMem() {

cout << "수정할 회원번호 전화:";

int id;

string phone;

cin >> id >> phone;

MemVO* pMem = new MemVO;

pMem->SetId(id);

pMem->SetPhone(phone);

return pMem;

}


void PrintMem(MemVO* pMem) {

if (pMem) {

cout << "  ***** 검색된 회원정보 *****" << endl;

cout << pMem->GetId() << "\t" << pMem->GetName() << "\t" << pMem->GetPhone() << endl;

delete pMem;

}

else {

cout << "  검색된 정보가 없습니다" << endl;

}

}


void PrintUpdateResult(bool updated) {

string msg;

if (updated) msg = "정상적으로 회원정보를 수정했습니댜";

else msg = "회원정보를 수정하지 못했습니다";

cout << "  --> 회원정보 수정결과:" << msg << endl;

}


void PrintDeleteResult(bool delted) {

string msg;

if (delted) msg = "정상적으로 회원정보를 삭제했습니댜";

else msg = "회원정보를 삭제하지 못했습니다";

cout << "  --> 회원정보 삭제결과:" << msg << endl;

}


void PrintMsg(string msg) {

cout << "  --> " << msg << endl;

}


void PrintError(string msg) {

cerr << "  --> " << msg << endl;

}


void PrintLine() {

cout << "------------------------------------------------------------" << endl;

}

};



MemVO** MemDAO::GetList() {

MemView view;

ifstream in("D:\\test\\members.txt");

if (!in.is_open()) {

view.PrintError("파일열기 실패");

return NULL;

}

char input[50];

int lineCnt = GetCount();

int id;

string name;

string phone;

MemVO** arr = new MemVO*[lineCnt];

for (int i = 0; i < lineCnt;i++) {

in.getline(input, 50);

stringstream ss(input);

ss >> id >> name >> phone;

MemVO* pMem = new MemVO(id, name, phone);

arr[i] = pMem;

}

MemDAO::MemCnt = lineCnt;

in.close();

return arr;

}


int MemDAO::GetCount() {

MemView view;

ifstream in("D:\\test\\members.txt");

if (!in.is_open()) {

view.PrintError("파일열기 실패");

return 0;

}


char line[50];

int cnt = 0;


while (!in.eof()) {

in.getline(line, 50);

if (strlen(line)) cnt++;

}


in.close();

return cnt;

}


bool MemDAO::Insert(MemVO* pMem) {

bool Inserted = false;

MemView view;

ofstream out;

out.open("D:\\test\\members.txt", ios::app);


if (!out.is_open()) {

view.PrintError("파일열기 실패");

return false;

}


out << pMem->GetId() << " " << pMem->GetName() << " " << pMem->GetPhone() << endl;

if (out.good()) {

Inserted = true;

}

else {

Inserted = false;

}

out.close();

delete pMem;

return Inserted;

}


MemVO* MemDAO::Search(int id) {

MemView view;

ifstream in("D:\\test\\members.txt");

if (!in.is_open()) {

view.PrintError("파일열기 실패");

return NULL;

}


char line[50];

int usrId;


MemVO* pMem = NULL;

while (!in.eof()) {

in.getline(line, 50);

stringstream ss(line);

ss >> usrId;


if (id == usrId) {

string name;

string phone;

ss >> name;

ss >> phone;

pMem =  new MemVO(usrId, name, phone);

break;

}

}


in.close();

return pMem;

}


bool MemDAO::Update(MemVO* pMem) {

MemView view;

ifstream in("D:\\test\\members.txt");

if (!in.is_open()) {

view.PrintError("파일열기 실패");

return false;

}


int lineCount = GetCount();

MemVO** arr = new MemVO*[lineCount];


int usrId;

string name;

bool found = false;

int i = 0;

char* line;


for (int j = 0; j < lineCount; j++) {

line = new char[50];

in.getline(line, 50);

stringstream ss(line);

ss >> usrId;


if (pMem->GetId() == usrId) {

found = true;

ss >> name;

string newLine = usrId + " " + name + " " + pMem->GetPhone();

MemVO* pM = new MemVO(usrId, name, pMem->GetPhone());

arr[i++] = pM;

}

else {

string name,phone;

ss >> name>>phone;

MemVO* pM = new MemVO(usrId, name, phone);

arr[i++] = pM;

}

}

in.close();

if (found) {

Rewrite(arr, lineCount);

}

else {

view.PrintMsg("검색결과가 없습니다");

}

}


bool MemDAO::Delete(int id) {

MemView view;

ifstream in("D:\\test\\members.txt");

if (!in.is_open()) {

view.PrintError("파일열기 실패");

return false;

}


int lineCount = GetCount();

MemVO** arr = new MemVO*[lineCount-1];


int usrId;

string name;

bool found = false;

int i = 0;

char* line;


for (int j = 0; j < lineCount; j++) {

line = new char[50];

in.getline(line, 50);

stringstream ss(line);

ss >> usrId;


if (id == usrId) {

found = true;

}

else {

string name, phone;

ss >> name >> phone;

MemVO* pM = new MemVO(usrId, name, phone);

arr[i++] = pM;

}

}

in.close();

if (found) {

bool rewrited = Rewrite(arr, lineCount-1);

return rewrited;

}

return false;

}


bool MemDAO::Rewrite(MemVO** arr, int sz) {

MemView view;

ofstream out("D:\\test\\members.txt");

if (!out.is_open()) {

view.PrintError("파일열기 실패");

return false;

}


for (int i = 0; i < sz; i++) {

out << arr[i]->GetId()<<" "<< arr[i]->GetName()<<" "<< arr[i]->GetPhone() << endl;

}


bool updated = false;

if (out.good()) updated = true;

out.close();


for (int i = 0; i < sz; i++) {

delete arr[i];

}


delete[] arr;

return updated;

}



class MemMgr 

{

public:

MemView view;

MemDAO dao;

void Process() 

{

bool go = true;


while (go) 

{

char m = view.Menu();

switch (m) 

{

case 's': memList(); break; // 파일에서 리스트를 읽어와서 MemVO객체에 저장, View에 전달

case 'i': insert(); break;

case 'f': search();  break;

case 'u': update();  break;

case 'd': remove();  break;

case 'x': go = false; break;

default: view.PrintError("입력오류");

}

}

}


void memList() {

MemVO** arr = dao.GetList();

view.PrintLine();

view.PrintList(arr);

view.PrintLine();

}


void insert() {

MemVO* pMem = view.InputMem();

string msg;

if (dao.Insert(pMem)) msg = "회원정보 추가 성공";

else msg = "회원정보 추가 실패";

view.PrintMsg(msg);

view.PrintLine();

view.PrintList(dao.GetList());

view.PrintLine();

}


void search() {

MemVO* pMem = dao.Search(view.InputSearchKey());

view.PrintLine();

view.PrintMem(pMem);

view.PrintLine();

}


void update() {

MemVO* pMem = view.InputUpdateMem();


if (!pMem) {

view.PrintMsg("검색 결과가 없습니다");

return;

}


bool updated = dao.Update(pMem);

view.PrintUpdateResult(updated); 

view.PrintLine();

view.PrintList(dao.GetList());

view.PrintLine();

}


void remove() {

int del = view.InputDeleteKey();

bool deleted = dao.Delete(del);


view.PrintDeleteResult(deleted);

view.PrintLine();

view.PrintList(dao.GetList());

view.PrintLine();

}

};



int MemDAO::MemCnt = 0;


int main()

{

cout << "MVC 패턴으로 텍스트 파일 다루기" << endl;


MemMgr mgr;

mgr.Process();


cout << "프로그램 종료" << endl;

return 0;

}

Posted by cwisky
Unreal C++/Text File IO2018. 9. 12. 19:21

C++ 텍스트파일 CRUD 예제


구성 파일

Member 클래스 (Member.h, Member.cpp)

번호, 이름, 이메일, 전화번호를 저장할 수 있는 회원정보 데이터 모델


MemberIO 클래스 (MemberIO.h, MemberIO.cpp)

파일에 저장된 회원정보 4가지를 입출력 하는 모든 기능

추가, 수정, 삭제, 검색, 리스트, 회원수 확인 등


main.cpp ( main함수와 그 외 서비스 함수들)

프로그램의 흐름을 관리하며 이용자로부터 키보드 입력을 받아 기능을 호출하고 화면에 표시하는 모든 기능



Member.h

#pragma once

#include <string>


using namespace std;


class Member

{

int num;

string name;

string email;

string phone;

public:

Member();

Member(int num, string name, string email, string phone);

void set_num(int num);

void set_name(string name);

void set_email(string email);

void set_phone(string phone);

int get_num();

string get_name();

string get_email();

string get_phone();

};


Member.cpp

#include "Member.h"


Member::Member(){}


Member::Member(int num, string name, string email, string phone)

:num(num), name(name), email(email), phone(phone)

{}


void Member::set_num(int num) { this->num = num; }


void Member::set_name(string name){ this->name = name;}


void Member::set_email(string email){ this->email = email;}


void Member::set_phone(string phone){ this->phone = phone;}


int Member::get_num() { return num; }


string Member::get_name() { return name; }


string Member::get_email(){ return email; }


string Member::get_phone(){ return phone; }



MemberIO.h

#pragma once

#include <string>

#include "Member.h"


using namespace std;


class MemberIO

{

string filepath;

public:

int curr_cnt;

MemberIO();

MemberIO(string filepath);

Member* list();

bool add(Member& m);

Member search(string name);

bool update(Member& m);

bool del(string name);

int get_count();

private:

bool overwrite(string* arr, int cnt);

};


MemberIO.cpp

#include "MemberIO.h"

#include <fstream>

#include <iostream>

#include <sstream>


using namespace std;


MemberIO::MemberIO(){

this->filepath = "D:\\test\\mem.txt";

}


MemberIO::MemberIO(string filepath){

this->filepath = filepath;

}


Member * MemberIO::list(){

int cnt = get_count();

if (cnt == 0) { 

cout << "회원정보가 아직 없음" << endl; 

return nullptr;

}

Member* pm = new Member[cnt];

ifstream fin(filepath);

if (!fin.is_open()) {

cout << "파일열기 오류" << endl;

return nullptr;

}

string line, name, email,phone;

int num, idx = 0;

stringstream ss;

while (!fin.eof()) {

getline(fin, line);

if (line.length() == 0) break;


ss.str(line);

ss >> num >> name >> email >> phone;

pm[idx++] = Member(num, name, email, phone);

ss.clear();

line.clear();

}

fin.close();

return pm;

}


bool MemberIO::add(Member& m){

ofstream fout(filepath, ios::app);

if (!fout.is_open()) {

cout << "파일 열기 오류" << endl;

return false;

}

fout << m.get_num() << " " << m.get_name() << " " << m.get_email() << " " << m.get_phone() << endl;

fout.close();

return true;

}


Member MemberIO::search(string name){

ifstream fin(filepath);

if (!fin.is_open()) {

cout << "파일 열기 오류" << endl;

throw exception("파일열기 오류") ;

}

string line;

int cnt = 0;

stringstream ss;

int num;

string fname, email,phone;

Member m;

while (!fin.eof()) {

getline(fin, line);

ss.str(line);

ss >> num >> fname;

if (fname == name) {

ss >> email >> phone;

fin.close();

return Member(num, fname, email, phone);

}

}

fin.close();

throw exception("검색실패") ;

}


bool MemberIO::update(Member & m){

int cnt = get_count();

ifstream fin(filepath);

string* all_lines = new string[cnt];

string line,num,name,email,phone;

stringstream ss;

int idx = 0;

while (!fin.eof()) {

getline(fin, line);

if (line.length() == 0) break;

ss.str(line);

ss >> num >> name;

if (name == m.get_name()) {

ss >> email >> phone;

stringstream ss2;

ss2 << num <<" "<< name <<" "<<

m.get_email() <<" "<< phone;

all_lines[idx++] = ss2.str();

ss2.clear();

}

else {

all_lines[idx++] = line;

}

ss.clear();

}

bool ok = overwrite(all_lines, idx);

cout << "업데이트 결과:" << ok << endl;

return true;

}


bool MemberIO::del(string name){

int cnt = get_count();

ifstream fin(filepath);

string* all_lines = new string[cnt-1];

string line, num, fname;

stringstream ss;

int idx = 0;

while (!fin.eof()) {

getline(fin, line);

if (line.length() == 0) break;

ss.str(line);

ss >> num >> fname;

if (fname != name) {

all_lines[idx++] = line;

}

ss.clear();

}

bool ok = overwrite(all_lines, idx);

cout << "삭제 결과:" << ok << endl;

return true;

}


int MemberIO::get_count(){

ifstream fin(filepath);

if (!fin.is_open()) { 

cout << "파일 열기 오류" << endl;

return -1;

}

string line;

int cnt = 0;

while (!fin.eof()) {

getline(fin, line);

if(line.length()!=0) cnt++;

line.clear();

}

fin.close();

curr_cnt = cnt;

return cnt;

}


bool MemberIO::overwrite(string * arr, int cnt)

{

ofstream fout(filepath);

if (!fout.is_open()) {

cout << "파일 열기 오류" << endl;

return false;

}

for (int i = 0; i < cnt; i++) {

fout << arr[i] << endl;

}

fout.close();

delete[]arr;

return true;

}


main.cpp

#include <iostream>

#include <fstream>

#include <string>

#include <sstream>

#include "MemberIO.h"


using namespace std;


enum Menu { LIST, SEARCH, ADD, UPDATE, DELETE, EXIT };


void print_menu() {

cout << "\t----------------------------------------------------------------" << endl;

cout << "\n\t회원관리: 목록(p), 추가(a), 검색(s), 수정(u), 삭제(d), 종료(x) :";

}

Menu get_user_menu() {

string input;

getline(cin, input);

stringstream ss(input);

char m;

ss >> m;


Menu menu = LIST;

switch (m) {

case 'p': menu = LIST; break;

case 'a': menu = ADD; break;

case 's': menu = SEARCH; break;

case 'u': menu = UPDATE; break;

case 'd': menu = DELETE; break;

case 'x': menu = EXIT; break;

}

return menu;

}

void print_list(Member* pm, int cnt) {

cout << "\n\t----------------------------------------------------------------" << endl;

for (int i = 0; i < cnt; i++) {

if(pm[i].get_name().empty()) break;

cout << pm[i].get_num() << "\t\t" << pm[i].get_name() << "\t\t"

<< pm[i].get_email() << "\t\t" << pm[i].get_phone() << endl;

}

delete[]pm;

}

Member add() {

cout << "\n번호 이름 이메일 전화번호를 공백으로 구분하여 입력:";

int num;

string name;

string email;

string phone;

cin >> num >> name >> email >> phone;

Member m(num, name, email, phone);

string line;

getline(cin, line);

return m;

}

string search() {

cout << "\n검색할 회원의 이름:";

string name;

getline(cin, name);

return name;

}

void print_mem(Member& m) {

cout << "\n검색결과----------------------------------------------------------------" << endl;

cout << m.get_num() << "\t" << m.get_name() << "\t"

<< m.get_email() << "\t" << m.get_phone() << endl;

}

Member update_info() {

cout << "\n수정할 회원의 이름, 이메일 입력:";

string input, name, email;

getline(cin, input);

stringstream ss(input);

ss >> name >> email;

ss.clear();


Member m;

m.set_name(name);

m.set_email(email);

return m;

}

string get_del_name() {

cout << "\n삭제할 회원의 이름:";

string name;

getline(cin, name);

return name;

}


int main()

{

MemberIO mio("D:\\test\\mem.txt");


cout << "\t\t\t\t** 회원관리 프로그램 **" << endl;

cout << "\t\t\t\t=======================" << endl;

bool go = true, saved=false;

Member* pm = 0;

Member mem;


while (go) {

print_menu();

Menu m = get_user_menu();


switch (m) {

case LIST:

pm = mio.list();

print_list(pm, mio.curr_cnt);

break;

case ADD:

mem = add();

saved = mio.add(mem);

cout << "파일에 추가:" << saved ? "성공" : "실패";

cout << endl;

break;

case SEARCH:

try { 

Member m = mio.search(search()); 

print_mem(m);

}catch (exception& e) { cerr << e.what() << endl; }

break;

case UPDATE:

mem = update_info();

mio.update(mem);

break;

case DELETE:

mio.del(get_del_name());

break;

case EXIT: go = false; break;

}

}

cout << "\n프로그램 종료...." << endl;


return  0;

}


Posted by cwisky

VS 2015에서는 문자열 변수 선언시 아래의 문장은 아무런 문제가 없이 실행된다.


char * str = "Hello";


그러나 VS 2017에서는 에디터에서 상수 문자열을 일반 문자열로 형변환할 수 없다는 컴파일러 오류가 발생한다

VisualStudio 2017 15.5 버전부터는 모든 솔루션에 [준수모드]가 디폴트로 Permissive 상태로 설정되어 있기 때문에 위의 문장에서 오류가 발생한다. 


"const char *" 형식의 값을 사용하여 "char *" 형식의 엔티티를 초기화할 수 없습니다"



 VS 2017에서는 더 엄격한 문법 준수 모드로 설정되어 있기 때문이다

그러므로 아래와 같은 방법으로 표현해야 한다

const char * str = "Hello" 



이를 원래의 설정으로 변경할 필요가 있다면 아래의 절차에 따라서 설정하면 된다


프로젝트 > [프로젝트이름] 속성 > C/C++ > 언어 > 준수모드 > 아니오 선택 > 적용 > 확인



Posted by cwisky
Unreal C++/C++ Pawn2018. 8. 22. 18:49

언리얼 엔진에서 C++를 이용한 Pawn 클래스 기반으로 커스텀 폰 클래스 정의하기


언리얼 엔진에서 Actor와 Pawn이 다른 점은 Pawn은 AddMovementInput()함수를 사용하여 전후좌우상하로 이동할 수 있으므로 키보드의 입력을 Pawn에 반영할 수 있다는 것이다


여기서는 C++를 사용하여 Pawn 기반의 MyPawn 클래스를 생성하고 키보드(W, A, S, D)키를 누를 때마다 해당 폰이 전후좌우로 이동하는 기능을 작성해보고자 한다


C++ 프로젝트 생성

Pawn 기반 C++ 클래스 생성(MyPawn)



.h 헤더파일 ( 적색 코드는 추가한 내용)

// Fill out your copyright notice in the Description page of Project Settings.


#pragma once


#include "CoreMinimal.h"

#include "GameFramework/Pawn.h"

#include "Components/InputComponent.h"

#include "GameFramework/FloatingPawnMovement.h"

#include "Camera/CameraComponent.h"

#include "MyPawn.generated.h"


UCLASS()

class CPP_PAWN_TEST_API AMyPawn : public APawn

{

GENERATED_BODY()


public:

// Sets default values for this pawn's properties

AMyPawn();


UPROPERTY(EditAnywhere)

UStaticMeshComponent* Mesh;


UPROPERTY(EditAnywhere)

UCameraComponent* Camera;


protected:

// Called when the game starts or when spawned

virtual void BeginPlay() override;


public:

// Called every frame

virtual void Tick(float DeltaTime) override;


// Called to bind functionality to input

virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;


void MoveForward(float Value);

void MoveRight(float Value);

};



.cpp 파일 (적색 코드는 추가된 내용)

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyPawn.h"


// Sets default values

AMyPawn::AMyPawn()

{

  // Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.

PrimaryActorTick.bCanEverTick = true;


Mesh = CreateDefaultSubobject<UStaticMeshComponent>("MyMesh");

CreateDefaultSubobject<UFloatingPawnMovement>("PawnMovement");

Camera = CreateDefaultSubobject<UCameraComponent>("MyCamera");

}


// Called when the game starts or when spawned

void AMyPawn::BeginPlay()

{

Super::BeginPlay();

}


// Called every frame

void AMyPawn::Tick(float DeltaTime)

{

Super::Tick(DeltaTime);

}


// Called to bind functionality to input

void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)

{

Super::SetupPlayerInputComponent(PlayerInputComponent);

PlayerInputComponent->BindAxis("MoveForward", this, &AMyPawn::MoveForward);

PlayerInputComponent->BindAxis("MoveRight", this, &AMyPawn::MoveRight);

}


void AMyPawn::MoveForward(float Value)

{

AddMovementInput(GetActorForwardVector(), Value);

}


void AMyPawn::MoveRight(float Value)

{

AddMovementInput(GetActorRightVector(), Value);

}



Axis Mappings

언리얼 에디터에서 Edit > 프로젝트 세팅 > 엔진 > 입력 > Axis Mappings

MoveForward 라는 바인딩 이름으로 W(1.0), S(-1.0) 등록

MoveRight 라는 바인딩 이름으로  D(1.0), A(-1.0) 키를 등록



실행 테스트

완성된 C++ MyPawn 클래스를 Content Browser에서 드래그하여 뷰포트에 놓는다

컴포넌트로 포함된 카메라의 위치와 각도를 언리얼 에디터에서 변경하려면 Camera 변수에 매크로UFUNCTION(EditAnywhere0 를 설정해 주어야 한다

뷰포트에서 MyPawn을 선택하고 [디테일] 뷰에서 Mesh 변수를 선택한 후 Static Mesh 패널에서 Cube 등 임의의 메시를 선택하여 설정한다


게임을 실행하여 W, S, A, D 키를 이용하여 이동할 수 있는지 확인한다

만약 카메라만 이동하고 메시가 이동하지 않으면, Mesh 변수를 선택하고 [디테일]뷰의 Pawn 패널에서 Auto Possess Player 항목에 Disabled 가 아닌 Player0를 설정하면 된다


Posted by cwisky
Unreal C++/C++ Actor2018. 8. 21. 18:57

C++ 클래스를 이용하여 액터를 생성하고 Z 위치를 변경하는 예

https://www.youtube.com/watch?v=K8iSi1oGaBI


C++ 프로젝트 생성

C++ 클래스 생성(Actor 클래스 기반, 클래스이름: CubeActor 등으로 지정)

Content Browser 안에 C++ 클래스가 보이지 않으면 우측 하단의 [뷰옵션] > [C++ 클래스 표시] 선택

VS에 CubeActor.h, CubeActor.cpp 파일이 생성된다


CubeActor.cpp의 Tick 함수에 아래의 적색부분 같이 작성하여 게임 시작시에 큐브가 상승하도록 한다

// Fill out your copyright notice in the Description page of Project Settings.


#include "CubeActor.h"



// Sets default values

ACubeActor::ACubeActor()

{

  // 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 ACubeActor::BeginPlay()

{

Super::BeginPlay();

}


// Called every frame

void ACubeActor::Tick(float DeltaTime)

{

Super::Tick(DeltaTime);

FVector NewLoc = GetActorLocation();

NewLoc.Z += 5;

SetActorLocation(NewLoc);

}


위와 같이 cpp 파일을 편집하여 VS에서 저장하고 UE4의 툴바에서 [컴파일] 버튼을 누른다

컴파일이 완료되면 컨텐츠 브라우저에서 CubeActor를 드래그하여 뷰포트에 올릴 수 있지만 아무런 형제가 없기에 뷰포트에서 선택할 수도 없는 상태가 된다

이와같이 C++ 액터클래스 드래그하여 뷰포트에서 보여지고 또 마우스로 선택이 가능하도록 하려면 가시적인 컴포넌트 한개는 있어야 한다.


C++액터 클래스에 컴포넌트를 포함시키고자 한다면 다음과 같은 코드를 h, cpp 파일에 추가한다(적색부분 참조)

// Fill out your copyright notice in the Description page of Project Settings.


#pragma once


#include "CoreMinimal.h"

#include "GameFramework/Actor.h"

#include "CubeActor.generated.h"


UCLASS()

class CPP_CUBE_API ACubeActor : public AActor

{

GENERATED_BODY()

public:

// Sets default values for this actor's properties

ACubeActor();


protected:

// Called when the game starts or when spawned

virtual void BeginPlay() override;


public:

// Called every frame

virtual void Tick(float DeltaTime) override;


UPROPERTY(EditAnywhere)

UStaticMeshComponent* Mesh;

};



cpp 코드에 다음과 같이 액터에 컴포넌트를 등록하는 부분을 작성한다

// Fill out your copyright notice in the Description page of Project Settings.


#include "CubeActor.h"



// Sets default values

ACubeActor::ACubeActor()

{

  // 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;

Mesh = CreateDefaultSubobject<UStaticMeshComponent>("MyMesh");

}


// Called when the game starts or when spawned

void ACubeActor::BeginPlay()

{

Super::BeginPlay();

}


// Called every frame

void ACubeActor::Tick(float DeltaTime)

{

Super::Tick(DeltaTime);

FVector NewLoc = GetActorLocation();

NewLoc.Z += 5;

SetActorLocation(NewLoc);

}




위와같이 작성이 되었으면 VS에서 저장하고 UE4에서 [컴파일] 버튼을 누르고 컴파일을 완료한다

이제 컨텐츠 브라우저에서 CubeActor 를 드래그하여 뷰포트에 올리면 뷰포트에 형체는 없지만 방향표시 기즈모가 나타나기 때문에 [디테일] 뷰에서 Mesh 변수에 구체적인 스테틱 메시를 지정해 줄 수 있게 된다




Posted by cwisky