반응형

Ebiten으로 간단한 게임을 구현해보려고 하는데 이번에는 그래픽 리소스를 직접 만들어보고 싶었다.

개인적으로 픽셀 감성을 좋아해서 픽셀을 찍어보려고 하기는 하는데 역시 쉽지 않다;;

뭐가 되었든 테스트용으로 간단한 픽셀 스프라이트를 Asesprite로 찍을 수 있었다.

 

Asesprite를 export 하면 json 파일과 png 파일을 뽑을 수 있는데

이 데이터를 읽어서 스프라이트 애니메이션을 구현해야 한다.

뽑힌 데이터들을 보니 애니메이션을 구현할만한 정보는 모두 포함되어 있었다.

 

그래서 일단 json 정보를 읽어보는것을 먼저 해보기로 했다.

검색해보니 기본적으로 'encoding/json' 패키지를 제공하고 있었고 다른 분이 만든 gson도 유명해보였다.

goasesprite라는 패키지도 있는데 (내부에서 gson을 이용해서 파싱) 테스트해보니 바로 애니메이션이 잘 동작했지만

json 읽는 부분도 연습해볼겸 직접 로드해보기로 했다.

 

일단 기본 흐름은 os.Open()으로 파일을 읽고 ioutil.ReadAll()로 []byte로 변경한뒤 json.Unmarshal()로 읽어오면된다.

Unmarshal을 하기 위해 json struct를 만들어주고 json의 key값을 연결해주어야 한다.

 

예를 들어 asesprite json에 있는 Meta 부분의 정보를 읽어오려면

type FrameTag struct {
...
}

type Meta struct {
  App string `json:"app"`	// meta.app을 읽어온다.
  ...
  Img string `json:"image"`	// meta.image를 읽어온다.
  ...
  FrameTags []FrameTag `json:"FrameTag"`	// meta.FrameTag을 읽어온다. (배열)
}

위와 같이 `json:"app"`과 같이 json의 키를 직접 입력해주어야 한다.

FrameTag와 같이 기본 타입이 아니라면 그에 맞는 struct를 추가해서 읽어줘야 하고 배열이라면 배열로 잡아야 한다.

 

Meta정보와 다르게 Frame은 배열이 아니라 Map의 형태로 되어있다.

이때는 Frames map[string]Frame `json:"frames"` 와 같이 map으로 읽으면 된다.

 

이 정도만 되어도 일단 원하는 데이터는 다 읽어올 수 있었다.

 

다음에는 이 데이터를 이용해서 애니메이션을 구현해봐야겠다.

반응형

'프로그래밍 > Go' 카테고리의 다른 글

Go - Ebiten TTF 폰트 사용하기  (0) 2022.04.17
Go 언어 환경설정 간단 정리(IDE - VS Code)  (0) 2022.03.19
Posted by msparkms
,
반응형

요즘 Go의 Ebiten을 조금씩 보고 있다.

아직 Go 언어가 익숙하지는 않지만 조금씩 기능을 추가해가면서 공부해보려고 한다.

 

일단 가장 기본적인 기능으로 텍스트 출력을 해보고 싶었다. (FPS 출력;;)

그래서 무료 라이센스의 TTF 파일을 우선 찾았고 이 파일을 어떻게 사용할까 찾아보았다.

ebiten에서 텍스트를 사용하는 기능은 지원하고 있어서 TTF를 읽어서 font.Face를 만들어내기만 하면 되었다.

 

TTF를 읽기 위해서 freetype 패키지를 설치하였다.

go get github.com/golang/freetype

 

그리고 아래와 같이 파일 경로를 통해서 파일을 읽고 해당 파일을 바이트로 변경하고 truetype으로 파싱해서 font.Face를 생성하는 함수를 만들었다. 나는 utils 패키지를 만들어서 ttfLoader.go 파일로 빼놓았다.

 

func Load_TTF(fontPath string) font.Face {
	f, err := ebitenutil.OpenFile(fontPath)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	b, err := ioutil.ReadAll(f)
	if err != nil {
		log.Fatal(err)
	}

	tt, err := truetype.Parse(b)
	if err != nil {
		log.Fatal(err)
	}

	const dpi = 72
	return truetype.NewFace(tt, &truetype.Options{
		Size:    24,
		DPI:     dpi,
		Hinting: font.HintingFull,
	})
}

 

update 함수에서 ebiten.CurrentFPS() 를 이용하여 현재 FPS를 얻어내 문자열을 만들었고

draw 함수에서 text.Draw()로 텍스트를 출력해보았다.

 

이렇게 조금 조금씩 코드를 추가해나가면서 간단한 게임 엔진을 만들어보려고 하는데

간단히 정리할 수 있는 내용이 있다면 블로그에 올리도록 하겠다.

반응형

'프로그래밍 > Go' 카테고리의 다른 글

Go - Asesprite json 파일 읽기  (0) 2022.05.01
Go 언어 환경설정 간단 정리(IDE - VS Code)  (0) 2022.03.19
Posted by msparkms
,
반응형

Golang을 익혀보기 위해서 기본적으로 환경설정이 필요하다.

 

일단 Golang을 다운 받는다.

 

https://go.dev/dl/

 

Downloads - The Go Programming Language

Downloads After downloading a binary release suitable for your system, please follow the installation instructions. If you are building from source, follow the source installation instructions. See the release history for more information about Go releases

go.dev

 

에서 각자 OS에 맞게 설치 파일을 받아서 설치해주면 된다.

 

IDE는 VS Code를 사용할 예정이다. VS Code는 이미 설치되어 있어서 따로 받지는 않았다.

코드를 작성할 폴더를 하나 만들어주고 해당 폴더를 열어준다.

 

Golang은 기본 설정은 같은 Workspace를 두고 개발하기 때문에

모든 프로젝트에서 같은 패키지를 사용한다고 한다.

같은 패키지를 사용하면 공간도 절약되고 따로 설치를 하지 않아도 되서 효율적이기는 할 것 같은데

프로젝트마다 다른 버전의 패키지를 사용해야 된다면 조금 불편할 것 같기도 하다.

다른 언어들도 프로젝트마다 따로 패키지 설치를 지원하거나 virtual 환경을 만들어서 설치하고는 한다.

VS Code Settings만 조금 수정하면 VS Code를 통해서 패키지를 설치시 각 폴더에 설치할 수 있다.

Settings.json에 아래와 같이 입력하여 GOPATH를 내 폴더로 처리하도록 하면 된다.
(아니면 프로젝트 사용할때 마다 GOPATH를 직접 수정해줘도 될듯??)

 

"go.gopath": "${workspaceFolder}",
"go.inferGopath": true,
"terminal.integrated.env.windows": {
    "GOPATH": "${workspaceFolder}"
},
"terminal.integrated.env.osx": {
    "GOPATH": "${workspaceFolder}"
},

대신 이렇게 되면 내 폴더에서 이것저것 많이 생성되므로 .gitignore등을 잘 설정해줘야 될 것 같다.

 

근데 작업하다보니까 그냥 글로벌하게 설치해놓는것도 괜찮은것 같다

go mod를 이용해서 패키지들의 버전 관리를 해주면 될 것 같다

ebiten 같은 패키지는 거의 모든곳에서 사용할 것이기 때문에 한곳에 있는게 효율적일것 같기도 하고;;

 

그 뒤에는 VS Code의 Extension에서 Go를 설치해주자.

그 후에 Ctrl + Shift + P를 누르고 Go: Install/Update Tools를 선택해주면 몇가지 설치하면 좋은 Tool들이 나오는데

각자 필요한것을 골라서 설치하면 된다.

 

나는 일단 go-outline, gopls, staticcheck를 설치하였다.

gopls가 제대로 동작을 안한다면 아래 링크를 참고하여 delve를 설치해주면 된다.

https://github.com/go-delve/delve/tree/master/Documentation/installation

 

GitHub - go-delve/delve: Delve is a debugger for the Go programming language.

Delve is a debugger for the Go programming language. - GitHub - go-delve/delve: Delve is a debugger for the Go programming language.

github.com

go install github.com/go-delve/delve/cmd/dlv@latest 를 입력해주면 설치된다.

 

go 파일로 간단한 파일을 만든 뒤 F5로 실행했을때 go.mod ~ 하면서 실행이 안된다면

터미널에서 go env -w GO111MODULE=auto를 입력하여 환경 변수를 변경해주면 된다.

 

일단 이정도만 해놓으면 Golang 공부하는데 문제는 없을 것이다.

반응형

'프로그래밍 > Go' 카테고리의 다른 글

Go - Asesprite json 파일 읽기  (0) 2022.05.01
Go - Ebiten TTF 폰트 사용하기  (0) 2022.04.17
Posted by msparkms
,
반응형

C++ 코드를 보다보니 [[ ~ ]] 와 같은 형식으로 작성된 부분 있었다.

 

뭔가 싶어서 검색해보니 컴파일러에게 코드에 대해서 좀 더 명확하게 힌트를 제공하는 기능이라고 한다.

 

그중에서 자주 사용할것 같은 attribute만 간단하게 정리해보고자 한다. (개인적인 기준;;)

 

1. [[nodiscard]] - C++17

 

이 attribute가 붙은 함수나 attibute가 붙은 enum, class등을 리턴하는 함수를 만들었다면

해당 함수를 호출할때는 리턴값을 받아야 한다고 컴파일러에 알려주는 것이다.

이는 컴파일 시점에 워닝으로 알려준다.

 

[[nodiscard]] int strategic_value(int x, int y) { return x^y; }

int main()
{
  strategic_value(); // warning
  auto v = strategic_value(); // ok

  return 0;
}

 

[[nodiscard("string"]] - C++ 20은 워닝 메시지에 "string" 부분도 같이 남겨준다.

 

2. [[deprecated]] / [[deprecated("string") - C++14

 

해당 attribute가 붙은 함수를 사용하면 사용은 가능하지만 앞으로 제거될 수 있음을 워닝으로 알려준다.

엔진이나 라이브러리를 만들때 많이 사용될 것 같다.

언리얼 엔진에서도 버전업 하고 빌드해보면 나올때가 있다. 그러면 최신에 맞게 코드를 수정해주곤 한다.

 

[[deprecated]]
void TriassicPeriod() {
    std::clog << "Triassic Period: [251.9 - 208.5] million years ago.\n";
}
 
int main()
{
    TriassicPeriod(); // warning
    return 0;
}

 

3. [[fallthrough]] - C++17

 

switch / case 문을 사용하다보면 fallthrough 기능을 사용하곤한다.

컴파일러에 따라서 해당 부분을 워닝처리할때가 있는데 컴파일러에 명확하게

내가 fallthrough를 한거니까 워닝처리를 하지 말라고 하는 것이다.

 

void f(int n) {
  void g(), h(), i();
  switch (n) {
    case 1:
    case 2:
      g();
     [[fallthrough]];
    case 3: // no warning on fallthrough
    ..
  }
}

몇가지 더 있기도 하고 추가될 예정인것 같지만 일단 이정도만 알고 사용해도 되지 않을까 싶다.

반응형
Posted by msparkms
,
반응형

std::string을 사용하면서 printf처럼 formatting 기능을 사용하고 싶었다.

혹시 새로운 기능이 없을까 하고 검색해보니 c++20에 std::format이라는 기능이 추가되었다고 한다.

std::format은 아래와 같이 std::string_view를 인자로 받는다.

template<class... Args>
string format(string_view fmt, const Args&... args);

string_view이기 때문에 그냥 const char*를 넘겨도 된다.

 

기본 사용방법은 아래와 같다.

std::format("{} {}!", "Hello", "world");	// Hello world!

{} 위치에 배치된다고 보면 된다.

 

{} 이곳에 들어갈 여러가지 기능들은 너무 많아서 링크로 대신한다.

필요할때 마다 검색해서 사용해봐야 될 것 같다.

https://en.cppreference.com/w/cpp/utility/format/formatter#Standard_format_specification

 

std::formatter - cppreference.com

template struct formatter; (since C++20) The enabled specializations of formatter define formatting rules for a given type. Enabled specializations meet the Formatter requirements. In particular, they define callables parse and format. For all types T and

en.cppreference.com

 

format을 사용하기 위해서는 Visual Studio 2019를 16.10 버전 이상으로 올려야 하고

C++ 버전은 latest로 변경해야 한다.

 

Vector3를 String으로 변경하는 간단한 코드를 예제로 남기고 마무리하면 될 것 같다.

class Vector3
{
public:
  float x_, y_, z_;
  
public:
  std::string toString()
  {
    return std::format("( {:.2f}, {:.2f}, {:.2f} )", x_, y_, z_);
  }
};
반응형
Posted by msparkms
,
반응형

최근에 GitHub에 Repository를 하나 파고 작업하다가 다른 컴퓨터에서 clone을 받으려고 했다.

 

그런데 Password Authenticiation 제공이 없어져서 Personal Access Token 발급이 필요하다고 나왔다.

 

그래서 찾아보니 발급 방법과 적용방법이 간단해서 여기에도 정리해본다.

 

GitHub에 로그인 후에 우상단에 있는 내 아이콘을 클릭 후 Settings로 들어간다.

 

Developer Settings를 선택후 Personal Access Token을 클릭한다.

 

내 경우에는 다른 컴퓨터에서 Repository를 생성할 때 토큰이 자동으로 만들어져 있었다.

 

Generate New Token을 클릭하고

 

Note에 명칭을 입력해놓고 (나는 그냥 노트북 이름;;)

 

밑에 원하는 권한을 클릭해주면 되는데 그냥 코드 관리만 할거여서 repo만 클릭해줬다.

 

생성하면 토큰이 하나 발급되는데

 

그 상태에서 다시 Clone을 시도하면 웹 로그인 혹은 토큰을 입력하라고 GUI가 나온다.

 

토큰을 넣어주니 Clone이 잘 되었다.

 

 

반응형
Posted by msparkms
,
반응형

C++은 메모리를 직접 관리할 수 있는 언어이다.

직접 관리는 자유를 주는 대신 책임도 따른다.

메모리를 직접 관리하면 실수에 의해 누수가 일어날 수 있다.

이를 막기 위해 가비지 컬렉션등 메모리 관리를 하는 알고리즘을 구현하거나 라이브러리등을 가져와야한다.

 

C++의 스마트 포인터를 이용하면 포인터를 사용하는 것 처럼 사용할 수 있으면서 (연산자 오버로딩)

메모리 관리는 스마트 포인터 객체에게 맡길 수 있다.

 

스마트 포인터 종류를 간단히 정리해보자.

보통 메모리 누수가 발생하는 이유는 해당 메모리를 바라보는 포인터들이 여러개일 경우

맨 마지막에 해당 메모리에 접근한 포인터가 메모리를 해제해야하는데 이를 관리하기가 쉽지 않다.

 

그래서 사용하는 전략이 대표적으로 두가지가 있는데

해당 메모리를 바라보는 포인터는 무조건 하나만 존재하게 만드는 고유 소유권 방식과

해당 메모리를 바라보는 포인터가 생기면 카운트를 올려주고 해제하면 카운트를 내려서

카운트가 모두 제거되었을때 실제 메모리를 제거해주는 레퍼런스 카운팅 방식이 있다.

 

다른 이유는 delete를 하기전에 예외등이 발생하여 delete가 실행이 안되는 경우이다.

이때는 스마트 포인터 객체로 감싸져 있기 때문에

예외가 발생했을때 해당 객체가 제거되고 메모리 해제부분이 실행되어 해결이 된다.

 

unique_ptr은 고유 소유권 방식의 스마트 포인터이다.

C++14부터 잘 지원되는 auto와 std::make_unique()를 이용하면 쉽게 unique_ptr을 생성할 수 있다.

make_unique()를 이용하면 생성자의 인자를 바로 넘겨줄 수 있어 간단하고

한번 감싸져 있는 형태여서 예외가 발생했을때 누수가 발생할 염려도 없다.

 

기본적인 사용법은 아래와 같다.

기본적으로 포인터와 같은 연산자를 이용할 수 있다.

get()을 통해 내부의 포인터를 직접 얻어올 수 있다.

reset()을 통해 내부 포인터를 해제하거나 내부 포인터를 다른 포인터로 교체할 수 있다.

release()는 내부 포인터를 돌려주며 소유권을 끊어주어서 직접 해제해야 한다.

class Test
{
public:
	Test(int x) : x_(x) {};

	void Print()
	{
		std::cout << x_ << std::endl;
	}

private:
	int x_;
};

void PrintTest(std::unique_ptr<Test> testPtr)
{
	testPtr->Print();
}

int main()
{
	auto myTest = std::make_unique<Test>(1);
	myTest->Print();

	PrintTest(std::make_unique<Test>(2));

	myTest.get()->Print();		// internal pointer
	myTest.reset();			// to nullptr
	if (!myTest)
	{
		std::cout << "myTest is empty" << std::endl;
	}
	myTest.reset(new Test(3));	// to new pointer
	myTest->Print();

	auto myInternalTest = myTest.release();
	if (!myTest)
	{
		std::cout << "myTest is empty" << std::endl;
	}
	myInternalTest->Print();
	delete myInternalTest;		// manually delete

	return 0;
}
반응형
Posted by msparkms
,
반응형

C++11에 유니폼 초기화가 추가되었다.

{}를 이용한 초기화여서 Brace-Initialization 이라고도 부르는것 같다.

 

C++11에 {}를 이용해서 초기화 문법을 따로 추가한 이유는 무엇일까??

사실 이전부터 일부 상황에서는 {}를 이용한 초기화를 사용하고 있었고

일부 상황에서는 ()를 이용한 초기화를 사용하고 있었다.

 

구조체와 배열의 경우에는 {}를 이용해서 초기화를 하고 있었고

하지만 클래스는 ()를 이용해서 생성자를 호출해서 초기화 하는식으로 처리가 되고 있었다.

struct A
{
  int x, y;
};

class B
{
  public:
    B(int x, int y) : mX(x), mY(y) {}
  private:
    int mX, mY;
};


A a = {10, 20};			// {}
B b(10, 20);			// ()

int c[4] = {1, 2, 3, 4};	// {}

C++11에서는 이 부분을 모두 {}를 이용하는 유니폼 초기화로 문법을 통일시켰다고 보면 될 것 같다.

=을 붙이거나 붙이지 않거나 모두 유니폼 초기화를 사용할 수 있다.

A a1 = {10, 20};
B b1 = {10, 20};

A a2{10, 20};
B b2{10, 20};

유니폼 초기화는 일반 자료형에도 사용 가능하다. 아래 예제에서 {}로 초기화할 경우 0이 들어간다.

int a = 1;
int b(1);

// uniform-initialization
int c = {1};	
int d{1};
int e{};	// 0

기왕 유니폼 초기화 기능을 넣고 문법을 통일하면서 한가지 기능을 더 추가하였는데

바로 축소 변환 방지(narrowing) 기능이다.

이는 암묵적 캐스팅이 진행될 때 기존 데이터의 손실이 발생할 경우 에러를 처리해주는 기능을 말한다.

예를 들자면 int에 double 값을 넣을 경우에 소수부분은 손실되게 되는데 이러한 상황을 막는 것이다.

void PrintInt(int x)
{
  std::cout << "Num : " << x << std::endl;
}

A i { 3.14, 3.14 };	// warning
B j { 3.14, 3.14 };	// error
int k { 3.14 };		// error

PrintInt(3.14);		// warning
PrintInt({ 3.14 });	// error

내가 테스트한 환경에서는 구조체의 경우에는 warning 처리로 끝나기는 했고 나머지의 경우에는 모두 축소 변환 에러가 발생했다. (VS 2019 - C++17)

 

클래스 멤버 변수를 선언하면서 바로 초기화 하거나 초기화 리스트에서 배열을 초기화 할때도 사용할 수 있다.


다음은 직접 리스트 초기화와 복제 리스트 초기화에 대한 부분을 간단히 정리하고 넘어가려고 한다.

위에 예제를 보면 a = {}의 형식이거나 아니면 a {} 형식으로 초기화를 하였다.

 

a = {}와 같은 형태를 복제 리스트 초기화라고 하고

a {}와 같은 형태를 직접 리스트 초기화라고 한다.

 

그냥 생성자 호출과 복사 생성자 호출 (= 연산자) 의 느낌이라고 대충 생각해도 될 것 같다.

 

여기서 이야기할 내용은 {}를 auto로 받았을 경우에 타입이 무엇이냐에 대한 것이다.

{}를 auto로 받으면 initializer_list<>으로 변경되고 처리가 되는데 C++17 이전과 이후에 약간 변화가 있어서 정리하려고 한다.

 

C++17 이전은 모두 initializer_list<>로 처리가 되고 C++17은 복제 리스트 초기화만 initializer_list<>로 처리된다.

// before C++ 17
auto a1 = { 1 };	// initializer_list<int>
auto a2 = { 1, 2 };	// initializer_list<int>
auto a3{ 1 };		// initializer_list<int>
auto a4{ 1, 2 };	// initializer_list<int>
auto a5 = { 1, 1.1 };	// error

// after C++ 17
auto a1 = { 1 };	// initializer_list<int>
auto a2 = { 1, 2 };	// initializer_list<int>
auto a3{ 1 };		// int
auto a4{ 1, 2 };	// error
auto a5 = { 1, 1.1 };	// error

C++17 이후 a4의 경우에는 초기화 리스트가 아닌 타입을 추론하게 되는데

이때 하나의 값에 의한 추론만 진행하기 때문에 값이 여러개라고 에러가 발생하게 된다.

그리고 a5의 경우에는 복제 초기화 리스트라도 다른 타입의 값을 넘긴다면 추론에 실패한다.

 

참고로 내가 테스트한 환경에서는 C++14로 낮춰도 a4 부분이 에러가 발생하기는 했다. (VS2019 - C++14)

 

마지막으로 어떤 타입인지 직접 출력해보고 싶으신 분들은

typeinfo의 typeid를 이용해서 콘솔로 출력해보면 좋을 것 같다.

반응형
Posted by msparkms
,
반응형

C++ 17에 std::string_view가 추가되었다.

기존에 std::string이 있는데 std::string_view가 따로 추가된 이유는 무엇일까?

 

문자열을 다루는 함수를 만든다고 해보면 매개변수를

const char* 혹은 const std::string& 으로 만드는 것이 보통이다.

이 두가지 선택은 각각 장단점을 가지게 된다.

 

const char*를 선택했다면 문자열 리터럴을 포함한 문자열 포인터를 받는데 용이하다.

대신 std::string에서 제공해주는 많은 기능을 사용할 수 없다.

 

const std::string&을 선택했다면 std::string의 기능을 사용할 수 있지만

문자열 리터럴을 받는다면 std::string 객체가 생성된다.

 

함수를 오버로딩해서 const char*, const std::string& 함수를 모두 만들면 해결은 되지만

뭔가 좋아보이지는 않는다.

 

두가지 장점을 모두 가져가기 위해 추가된 것이 std::string_view이다.

std::string_view는 내부에 포인터와 길이정보를 가진다.

그래서 문자열 포인터를 바로 받을 수 있으면서 std::string의 기능을 제공해준다.

포인터와 길이 정보만 가지고 있기 때문에 보통 레퍼런스가 아닌 값을 전달하는 방식으로 구현한다.

using namespace std::string_view_literals;

void StringViewTest(std::string_view strv)
{
    std::cout << "str : " << strv.data() << " length : " << strv.length() << std::endl;
}

StringViewTest("abcdefg");

std::string TestString("Test");
StringViewTest(TestString);

auto sv = "My Test String View"sv;
StringViewTest(sv);

간단한 예제는 위와 같다.

std::string_view를 매개변수로 받는 함수로 문자열 리터럴, std::string, std::string_view를 처리할 수 있다.

 

참고로 using namespace std::string_view_literals;를 추가하면

문자열 뒤에 sv를 붙여줌으로써 string_view 리터럴을 사용할 수 있다.

반응형
Posted by msparkms
,
반응형

3D 게임 수학책으로 유명한 3D Math Primer for Graphics and Game Development 책이 웹 버전으로 출시가 되었다.

 

이 웹 버전은 심지어 무료로 제공된다!

 

이번 기회를 통해서 한번 처음부터 끝까지 읽어봐야겠다.

 

혹시나 안 읽어보신 분들이 계시다면 읽어보시는걸 추천드린다.

 

찾아보니 최근에 번역판이 나온 GAME ENGINE BLACK BOOKS도 PDF가 무료로 공개되어 있다는 것을 알게되었다.

 

Doom과 울펜슈타인 모두 무료로 공개되어있다.

 

차근 차근 시간날때 읽어보면 좋을 것 같다.

 

3D Math Primer 링크

 

3D Math Primer for Graphics and Game Development

This is the home page of the book 3D Math Primer for Graphics and Game Development. You can now read the entire book for free! Please note: I'm still working through numerous issues with the conversion process from the original LaTeX manuscript, especialy

gamemath.com

Game Engine Black Books 링크

 

Game Engine Black Books Update

May 17, 2019 Game Engine Black Books Update I have kept myself busy with the Game Engine Black Books and I have plenty of news. Most importantly, they have been revised. The DOOM book is now in version 1.1 while the WOLF3D book is entering v2.1. And there

fabiensanglard.net

 

반응형

'프로그래밍' 카테고리의 다른 글

GitHub Personal Access Tokens 로그인  (0) 2021.08.29
SSAO  (0) 2015.01.15
Breakpoint Condition 기능 활용  (0) 2013.08.27
SQL 명령어 정리(스키마 분석하면서..)  (0) 2013.04.05
Bullet Helper 클래스  (0) 2012.11.26
Posted by msparkms
,