장고는 DB를 모델이라는 개념으로 관리를 한다.

우리는 데이터의 테이블을 하나의 model로 만들어주기만 하면
장고가 테이블도 생성해주고 기타 일을 해준다.

기본적으로 장고는 sqlite db를 사용한다. 나중에 다른 db와 연동하는 것도 공부하여 포스팅할 것이다.

지금은 장고의 모델 개념을 익히는데 집중을 할 것이다.

mange.py의 migrate 명령은 현재 db에서 수정된 부분을 적용시켜주는 역할을 한다. 모델을 확인하고 모델이 수정된 부분이 있으면 그거에 맞춰서 db를 적용시켜준다.

(초기 migrate)


처음 migrate를 하면 위와 같이 나온다.
우리는 아무 모델도 만들지 않고 그랬는데 뭘 적용하는거지?
장고는 기본적인 db 파일들이 있다. 관리자 정보, 인증 정보 세션정보 등등이 있다. 기본적인 db 정보들을 db에 필요하면 테이블도 만들고 알아서 적용시켜준다.

그렇다면 우리가 모델을 한번 직접 만들고 확인해보자.
지난 포스팅에서 우리는 polls 이라는 투표 APP 을 생성했다. 그렇다면 여기에 필요한 DB 테이블이 무엇일까?
기본적으로 질문들을 저장하는 테이블이 필요할 테고, 해당 질문에 대한 선택지들에 대한 테이블이 필요할 것이다.

그렇다면 Question과 Choice 모델을 2개 만들어보자.


(Question, Choice)


두 클래스 모두 model을 상속받아 생성한다.
기본적으로 model의 속성 필드가 있다.
CharField는 문자열이 저장되는 필드
DateTimeField는 날짜 정보가 저장될 수 있는 필드
IntegerField는 정수가 저장되는 필드
등등 있다. 이는 JSP나 다른 웹프로그래밍에서 DB를 생성해보았다면 이해하기 쉬울 것이다. 뒤 인자들도 쉽게 이해할 수 있을 것이다.
다만 다른점중 하나는 첫번째 인자를 선택하에 사람이 읽기 좋은 필등명으로 설정해줄 수 있다.
여기서는 pub_date를 보자. 여기서 필드를 생성할 때 첫번째 인자가 date published 라는 문자열로 설정되어있다. 우리는 해당 필드를 date published 라고 설정한 것이다.

그렇다면 이렇게 모델을 생성해주고 무엇을 해야하나?
여기서 이제 migrate 명령이 필요하다. 하지만 현재 우리 APP이 프로젝트로 포함되어있지 않다.!

포함시켜보자!


(포함시키기)


프로젝트명/프로젝트명/settings.py 에 들어가보면
INSTALLED_APPS 가 있는데 여기에 추가해야한다.
PollsConfig 클래스는 polls/apps.py 파일에 존재한다. 그러므로 경로는
'polls.apps.PollsConfig' 가 된다. 이 경로를 추가해주면 된다.

이제 migrate를 할 것이다.
우리는 직접 migrate하기전에 makemigrations를 이용해 과정을 살펴볼 것이다.

makemigrations 를 하게되면 변경된 부분을 migrate하기 전에 db를 위한 명령들이 있는 파일들로 만든다.


(makemigration)


polls에 대해 makemigrations을 해주니 하나의 파일이 생겼다.
이 파일 속에는 sql 쿼리들이 담겨있다.

확인해보면


(sql 쿼리)


위처럼 쿼리들이 저장되어있고, migrate를 하게 되면 위의 쿼리들이 날라가게 되는 것이다.

migrate!


(migrate)


migrate를 하게 되면 아까 생성된 파일들이 db에 적용이 된다.

이제 생성된 DB에 내용을 추가하는 것을 shell을 이용해 추가해볼 것이다.


(쉘 실행)


그 후 모델을 생성해준다.



(모델 생성)


모델에 내용을 채워서 생성해준다.

q에 Question 객체를 생성했다. 확인해보자.


(Question 객체 확인)


객체를 확인해보면 해당 내용들이 보인다.
save() 명령을 통해 DB에 저장할 수 있다.

객체를 확인해보자.


(객체 확인)


Question object (1) ..
우리가 알아볼 수 없다. 객체를 우리가 알아볼 수 있는 문자열로 보이게 하기 위해 조치를 취할 것이다.

이건 단순히 우리가 쉘에서 보기 편하게 하기 위해서가 아닌 나중에 관리자 페이지에서 해당 DB를 관리할 때 보기 편하게 하기 위해서이다.


(추가 내용)


위에서 만들었던 모델에 __str__ 을 오버라이드한다.

Question 모델에 메서드를 하나 추가해보자.



(메서드 추가)


최근에 발행된 질문인지 확인할 수 있는 메서드이다.

그리고 다시 쉘을 실행한다.


(확인)


아까보다 훨씬 보기 좋게 나온다. (질문내용을 나오게 출력했다.)

그리고 추가한 메서드도 확인해본다.



(추가 메서드 확인)



우리가 추가한 메서드도 잘 동작하는 것을 확인했다.

그렇다면 해당 질문에 선택지를 추가하고 마무리 하겠다.!



(선택지 추가)


선택지를 추가하였다.


( *참조 - https://docs.djangoproject.com/ko/2.0/intro/tutorial02/)


장고의 튜토리얼이다.

투표 APP을 만들 것이다. Project 안에는 여러가지의 APP이 있을 수 있다. 각각의 역할을 하는 APP이다. 여기서는 APP을 만드는 것을 해볼 것이다.



(APP 생성)


APP 생성은 manage.py 를 통해서 한다.

앱을 생성하면 다음과 같은 디렉토리 구조가 생긴다.



(생성된 디렉토리)


polls 디렉토리가 생겼다.

간단하게 웹페이지에서 해당 APP을 확인해 볼 것이다.
보여주는 것에 대한 처리를 담당하는 것이 views.py이다.

그러므로 우리는 views.py를 수정하여 해당 페이지에 들어가면 간단한 문구가 찍혀 나오게 할 것이다.



(views.py 작성)


HttpResponse 를 import하고 사용했다.
우리가 지금 만든 views.py에서 index는 아주아주 간단한 일을 하고 있다.
단순히 요청이 들어오면 위에 간단한 문구를 Http 응답으로 보내는 일이다.
HttpResponse는 Http 응답 페이지를 만들어서 보내주는 함수이다.

그렇다면 우리는 우리가 만든 views.py가 실행되야 해당 페이지에 문구를 띄울 수 있다.
우리는 url을 통해서 해당 페이지에 접근한다.
그러므로 url과 우리가 만든 views.py의 index를 연결해야한다.

urls.py를 만들어야한다.


(urls.py 생성)


해당 urls.py는 polls 디렉토리 안에서 생성하였다. 그리고 아래와 같이 작성한다.



(polls/urls.py)


해당 파일에서는 views 를 import 했다. 우리가 방금 작성한 index를 연결하기 위해서이다.
경로를 보면 '' 으로 아무것도 없다.
url에 아무것도 없으면 views.index랑 연결하라는 뜻이다.
옆에 name 인자는 해당 경로를 index 라는 이름으로 지정하는 것이다.
나중에 해당 url을 우리는 이 이름으로 불러와서 쓸것이다.

우리의 목적은 (우리사이트주소)/polls 에 접속하면 해당 index 페이지가 뜨는 것이다.(간단한 문구를 가진 페이지)
그러나 우리는 현재 빈문자열, 즉 아무것도 없는 경우에 index를 연결하였다.

순서가 조금 바뀌었지만,
이제 우리가 작성한 urls.py를 원본 urls.py에 연결해야한다.

(프로젝트 urls.py)



프로젝트이름/프로젝트이름/urls.py는 url 설정하는 파일이다.

여기에 우리는
path('polls/', include('polls.urls')), 줄을 추가하였다.
이 말은 polls 경로로 요청이 들어오면 include 하라는 뜻이다.

과정을 설명하자면
(우리사이트주소)/polls 로 요청이 들어오게 되면 이 파일에서 검사를 한다.

polls/ 라는 첫번째 패턴에서 걸리게 된다.

그러면 polls/ 까지 잘라버린다. 그렇게 되면 남은 url은 '' 빈 문자열이 된다.
해당 url을 
include하는 polls/urls.py로 전달한다.

그렇게 되면 아까 우리가 설정한 urls.py의 빈문자열에 대한 index 연결이 이해가 될것이다.

그렇다면 테스트 해보자!



(서버 실행)


실행 후, 주소/polls 

를 요청해보자.



(결과)


우리가 원하는 목적을 이루었다.!



(* 참조 - https://docs.djangoproject.com/ko/2.0/intro/tutorial01/)


이번에는 입력을 담당하는 Input Class를 살펴볼 것이다.

Input Class를 만든다.


(Input의 private)


입력에 관한 변수들이다.

public의 함수들을 볼 것이다.



(생성자)


생성자에서는 기초적인 초기화 작업을 진행한다.
키가 눌렸는지에 대한 배열 초기화와 버퍼 초기화, 마우스 정보 변수 초기화를 진행한다.
(* 컨트롤러에 대한 것은 Xbox 컨트롤러를 위한 것이다. 책에 포함되어있는 내용이지만, 나는 Xbox에 관한 설명은 건너 뛸 것이다.)



(소멸자)


Input 클래스가 삭제 될 때 마우스가 캡쳐상태라면 캡쳐 상태를 해제 해준다.

다음은 Input 클래스의 초기화 함수이다.



(초기화 함수)


초기화 과정에서 마우스를 캡쳐 상태로 설정시켜준다.
그리고 그 아래의 코드들은 고정밀 마우스 데이터를 가져오는 작업이다.

이제부터 입력에 관한 함수들이 나온다.


(키 눌림 안눌림)


키가 눌렸는지 안눌렸는지 체크하는 함수이다.
keyDown에서는 키가 눌렸으면 keysDown과 keysPressed 배열의 해당 index에 가서 true로 만들어준다. keysDown은 현재 눌렸는지 확인하는 것이다. 그리고 keyPressed는 현재 게임 루프가 실행되는 동안 눌렸었는지를 체크하기 위한 배열이다.

  따라서 keyUp함수에서는 keysDown만 false로 해준다.



(키 입력)


키 입력에 관한 함수이다. 




(키 체크)


isKeyDown은 현재 키가 눌렸는지 확인하는 함수이고 keysDown 배열을 확인한다.
wasKeyPressed는 현재 루프동안 키가 눌린 경험이 있는지 체크하므로 keysPressed 배열을 확인한다.
그리고 어떤 키든 눌렸었는지 체크하기 위한 함수 anyKeyPressed 함수도 추가해주었다.



(입력 초기화)


입력에 대한 초기화 함수이다. 초기화가 위에서 만든 initialize 함수와 다르다. 단순히 입력에 대한 초기화를 하는 것이다. 

  다음은 입력 중 마우스입력에 관한 함수들이다.



(마우스 입력)


mouseIn 함수로 마우스의 좌표를 가져오고, mouseRawIn을 사용해 마우스의 변화량을 가져온다.
mouseWheelIn으로 마우스 휠 정보를 가져온다.


(* 참조 - 2D 게임 프로그래밍, 찰스 켈리)


게임 엔진의 기초틀을 만들 것이다.

Game Class를 만들 것인데, 추후 다른 게임을 만들 때 이 Class를 상속받아서 만들 것이다.
이제 만들 Game Class는 그래픽을 담당하는 Graphics Class와 입력을 담당하는 Input Class를 포함하고 있다. Graphics Class는 이전 포스팅에서 만들었던 DirectX와 비슷하므로 다른 점을 설명하고 넘어갈 것이다.

Game Class를 만드는 법을 살펴보자!


(Game Class의 include)


Game Class는 graphics 클래스와 input 클래스를 포함한다.

Game Class를 만든다.



(Game Class의 protected)


Game 클래스의 변수들을 담아두었다. private 대신 protected를 사용한 이유는 Game 클래스로부터 상속받은 클래스에게 변수의 직접적인 접근을 허용하게 하기 위해서이다.


public의 메서드들을 만든다.


(생성자와 소멸자)


생성자와 소멸자이다.
생성자는 input 클래스의 객체를 만든다. 다음 포스팅글에서 얘기하겠지만 Input 클래스의 생성자에서 기초적인 입력에 관한 버퍼들을 초기화 한다.
생성자에서 기초적인 초기화를 하고 추가적인 초기화가 없으므로 initialized 라는 변수에 false를 담아둔다. 후에 이 변수를 보고 game 클래스의 초기화를 시켜줄 것이다.

소멸자는 Game 클래스가 삭제될 때 예약된 메모리를 해제하는 순서로 구성되어있다.



(윈도우 메시지 핸들러)


위에는 윈도우 메시지를 처리하는 핸들러 함수를 Game 클래스 안에서 만들었다.
각 이벤트에 대한 처리를 담당한다.

기존에 있던 winmain.cpp 에 있던 WinProc 의 메시지 핸들하는 부분이 Game 클래스로 옮겨 졌으므로 winmain.cpp의 WinProc 도 수정해주어야한다.



(WinProc 수정)


game클래스 안의 messageHandler를 호출해준다.

Game 클래스의 초기화를 담당하는 메서드이다.



(Game클래스의 초기화)



인자로는 윈도우 핸들러를 받는다.
여기서 Graphics 객체를 생성한다. 그 후 그래픽 객체를 초기화 하고, input 객체를 초기화한다.
마지막의 타이머 사용은 frametime을 계산하기 위한 것이다.

  화면에서 움직이는 그래픽 아이템들이 정지된 이미지를 적절한 시간에 연속적으로 화면에 보여주므로써 움직이는 것처럼 보이는 것이다. 이 프레임시간이 너무 빠르면 그래픽 아이템이 너무 빠르게 움직일 것이고, 너무 느리면 그래픽 아이템이 화면에서 너무 느리게 움직일 것이다. 그러므로 우리는 적절한 시간을 간격으로 화면에 출력해주어야한다. (우리가 원하는 속도로)

  그 계산을 위해 고성능의 타이머를 사용한다. 요즘 PC에는 기본적으로 고성능의 타이머를 갖추고 있으므로 이것을 사용할 것이다. QueryPerformanceFrequency 함수를 이용해 timerFreq에 고성능 타이머의 발생 빈도를 저장한다. 그리고 QueryPerformanceCounter함수를 이용해 시간을 가져오는데 이 때 timeStart 변수에 시작 시간을 가져온다. 
  그 후 초기화를 진행 하였으므로 initialized 변수에 true라고 저장한다.

  그 다음 처리해야할 문제는 디바이스 로스트 상태이다.
디바이스 로스트 상태란 Direct3D 그래픽 디바이스를 놓친 상태이다. 예를 들어 우리가 게임을 하다가 Alt-Tab을 눌러서 다른 화면을 보다가 다시 게임을 틀면 게임이 나와야한다. 이 때 게임이 화면에 그려지지 않는 상태가 그래픽 디바이스 로스트 상태이다.

  그렇기에 이러한 상황을 처리해주어야한다. 디바이스 로스트 상태가 되면 리셋하고 리소스를 다시 생성해야한다.



(디바이스 로스트 처리)


  처리 알고리즘은 위와 같다.
이 때 주의할 점은 리셋 함수를 호출하기 전 기존에 할당되었던 모든 리소스를 해제하지 않으면 reset 함수가 실패한다. 

  위에서 사용한 getDeviceState와 reset 함수는 아래와 같다.


(getDeviceState, reset)


getDeviceState는 로스트 상태를 확인하기 위한 함수이다. 함수 내부를 보면 디바이스객체의 TestCooperativeLevel 함수를 사용하는데 이 함수는 매개변수 없이 실행이 되고 성공하면 D3D_OK를 반환하고 로스트 상태를 3가지로 반환한다.
D3DERR_DEVICELOST : 디바이스가 로스트 상태가 되서 현재 복구할 수 없다.
D3DERR_DEVICEENOTRESET : 디바이스를 다시 작동할 수 있다.
D3DERR_DRIVERINTERNALERROR : 디바이스에 내부 에러가 있다. 사용자에게 보고하지만, 할 수 있는 방법이 없다.

또 releaseAll 함수와 resetAll 함수이다.



(releaseAll, resetAll)


비워둔채 메서드만 생성해둔다.

  그 다음 구현해야할 것이 게임 그래픽 렌더링이다. 그래픽을 그리는 과정을 렌더링이라고 부른다.
렌더링은 DirectX의 BeginScene 함수를 호출해 신을 시작하고, 모든 렌더링이 끝난 후 DirectX의 EndScene 함수를 호출해 신을 끝낸다. 이 Scene 밖에서 렌더링을 하면 실패한다.

그러므로 graphics 클래스 안에 beginScene함수와 endScene을 만들어준다.



(beginScene, endScene)


beginScene에서 백버퍼를 지워준다. 그리고 그래픽 디바이스 객체의 BeginScene() 함수를 호출하고 그 결과를 반환한다.

endScene에서는 디바이스 객체의 EndScene() 함수를 호출한다.

  이 두 함수 사이에서 그래픽을 렌더링해야한다. 실제 게임 아이템을 렌더링하는 renderGame() 함수는 아래와 같이 생겼다.



(renderGame 함수)


beginScene과 endScene 사이에서 렌더링을한다.
백버퍼를 지워 준 후 그 백버퍼에 그래픽을 그린다. 그리고 endScene으로 그리는 것을 마무리한다. 그 후 디바이스 로스트를 체크하고 (로스트상태면 처리해주고)
그래픽 객체의 showBackbuffer 함수를 이용해 화면에 그렸던 백버퍼를 출력해준다.

  이제 메인 메시지 루프에서 계속 호출될 게임클래스의 run 함수를 만들 것이다.



(메인 메시지 루프)


지금 만들 Game의 run 함수는 winmain.cpp의 메인 메시지 루프에서 계속 호출 될 것이다.


(Game 클래스의 run 함수)


  run 함수는 위와 같다. 위에서 아래로 코드를 설명하겠다.

처음 부분으로는 그래픽 객체 확인이다.



(그래픽 객체 확인)


  그래픽 객체를 확인하고 없다면 굳이 run 함수를 실행시킬 필요 없으므로 바로 함수를 종료한다.

  그 후 frametime을 계산하기 위한 부분이 나온다.


(frametime 계산)


  루프 시작전 끝 시간을 가져와서 시작시간의 차이를 구해 frametime을 구한다. 그렇다면 루프 시작할 때 시작시간을 설정하고 끝날 때 끝시간을 설정하여 frametime을 구하면 안될까?
-> 그런 방법으로 frametime을 구한다면 게임 루프 밖의 소요되는 시간을 고려하지 못하게 되므로 실제 렌더링 되는 시간이 정확하지 않게 된다.

  위 코드를 보면 frametime의 최소시간과 최대 시간을 정해둔다. 최소시간을 정해두고 그거보다 빠르다면 게임을 잠깐 재워둔다. 왜(?) -> 굳이 게임을 재워야할까 싶긴 하지만 이건 절전을 위한 것이다. 빠르게 처리 되었을 때는 잠깐 쉬게한다면 CPU 부하를 줄여주고 환경 친화적인 프로그램으로 만들어준다. 

이건 앞으로 어떤 프로그램을 만들든지 중요한 부분이다. 우리의 배터리는 소중하므로!

  그 후 frametime을 이용해 평균 fps를 계산해준다. 만약 frametime이 너무 느리다면, 최대 frame 시간으로 frametime을 설정해준다. 보통 이정도 까지 도달할 경우는 거의 없다.

  그 다음부분은 게임동작 관련부분이다.


(게임 함수)


  일시정지 상태가 아니라면 게임이 진행되어야한다.

Game 클래스의 순수 가상함수 update(), ai(), collision() 함수를 실행해준다. 앞으로 만들 게임의 클래스에서 Game 클래스를 상속받을 것인데 그 때 이 함수들을 우리가 채워주는 것이다. 지금은 Game 클래스를 만드는 것으로 게임엔진의 틀을 만드는 과정이다.


(마지막 과정)


다 이루어진 후 renderGame 함수를 이용하여 게임 아이템을 그리고, 컨트롤러의 상태를 읽고 지금까지 받은 키 눌림 입력을 초기화해주면서 끝난다.

(* 참조 - 2D 게임 프로그래밍, 찰스 켈리)


Django는 Python을 이용한 프레임워크이다.

웹호스팅 프로젝트에서 사용할 웹페이지를 Django를 이용하여 만들어 볼 것이다.!

먼저 간단히, 설치하고 서버를 실행해보는 것부터 시작했다!



(django 설치)


pip 을 이용하여 django를 설치한다!




(설치 확인)


python에 들어가서 import django를 해보아서
django가 잘 설치되었는지 확인한다.

간단한 프로젝트를 만들어서 웹 페이지에 접속해 볼것이다.



(프로젝트 생성)


프로젝트 생성은 위와 같다.

이렇게 하게 되면 디렉토리가 만들어지는데 아래와 같다.



(생성된 디렉토리)


앞으로 manage.py를 이용하여 APP도 만들고 관리를 할 것이다.

그러면 실행해서 한번 웹페이지에 접속해보자!



(서버 실행)


서버 실행은 runserver이다.




(결과)


시도한 결과 연결이 거부되었다.

방화벽탓인지 한번 방화벽을 내려보았다.



(방화벽 오픈)



방화벽을 오픈하여도 같은 현상이 있어서 찾아보니

default로는 로컬호스트에서만 접속할 수 있고, 모든 호스트에서 접속이 가능하게 하려면

0.0.0.0:(port) 로 지정하여 서버를 실행해줘야한다고 한다.



(오픈서버 실행)


서버를 실행하고

다시 접속해본다!



(Disallowd)


허용되지 않은 호스트???..

또 검색을 해본다.

검색을 해본 결과 해당 프로젝트 디렉토리의 settings.py 파일을 수정해주어야한다고 한다.



(settings.py)


ALLOWED_HOSTS 를 *로 모든 사용자가 접속하게 한다.



(접속)


접속해보니 이제 제대로 웹페이지에 접속이 되는 것을 확인할 수 있었다.

이번 글에서는 웹 호스팅의 가장 기본인 Apache 서버를 설치하고 테슽트 해볼 것이다.

Apache 서버 httpd 설치



(httpd 설치)


설치 후 httpd 데몬을 실행시켜준다.



(httpd 실행)


실행하고 포트를 확인해본다.



(포트확인)


80포트가 열려있는 것으로 봐서 httpd가 동작하고 있음을 확인 할 수 있다.

이제 시스템 서비스로 등록하여 부팅될 때마다 실행되도록 설정한다.



(설정)


테스트해본다.

웹페이지에 IP주소를 입력하면 내 서버의 테스트 서버가 나올 것이다.


(테스트 페이지)


이 기본페이지 말고 간단하게 홈페이지를 만들어볼 것이다.(아주 간단히)

Apache의 설정 정보는 아래의 위치에 있다.



(설정 정보)


이 파일을 열어서 확인해 본다.




(웹 페이지 디렉토리)


/var/www/html에 있는 파일들이 보이는 것이다.
여기에 내가 만든 페이지들을 업로드 시켜두면 여기 있는 페이지들이 인터넷에 보여지는 것이다.





(index 파일)


기본 설정으로 index.html 파일이 보여지게 되어있다.
나는 home.html으로 바꾸었다. 이제 /var/www/html/home.html 이 화면에 보여질 것이다.




(/var/www/html)


위의 경로로 들어가서 home.html을 만들어보았다.



(간단한 페이지)


위처럼 페이지를 아주 간단히 만들고




(서비스 재실행)


httpd 를 재실행해준다.

그리고 인터넷에서 내 IP를 입력해주면 방금 내가 만든 HTML 화면을 볼 수 있다.



(home.html)


그런데 조금 밋밋하다.
사진도 같이 올리고 싶다.

나의 서버와 파일을 주고 받을 수 있어야한다. 나의 서버에 파일을 업로드 할 경우도 있고,
나의 서버에서 파일을 다운로드 받을 경우도 있기 때문이다.

이를 위해 FTP를 이용할 것인데, FileZilla를 사용할 것이다.

내 컴퓨터에는 Clinet를 설치한다.



(Client 다운, 설치)


FileZilla가 설치가 다 되면 실행해준다.



(편집-설정)


편집-설정 을 클릭해준다.



(키 파일 추가)


왼쪽 항목 SFTP를 클릭한 후 키 파일을 추가한다.

이전 포스팅 글에서 SSH 키를 만들어 접속할 때 쓰는 그 키 파일이다. 여기에는 private_key 파일을 등록한다.



(파일-사이트 관리자)


그 후 사이트 관리자를 들어간다.



(정보 입력)


여기서 새 사이트를 만들어 준 후 
접속할 호스트 IP를 입력한다.
프로토콜은 SFTP로 설정하고 로그온 유형은 인터랙티브 그리고 사용자 id를 입력한다.

그리고 연결을 하면, 우리가 SSH를 이용해 비밀번호 없이 로그인하던 것과 같이 로그인이 된다.



(로그인 된 화면)


이제 이를 이용해 사진을 내 홈페이지에 올려보겠다.

먼저 내 서버에 사진을 옮긴다.



(사진 이동)


옮긴 사진을 아까 home.html 의 위치에 복사한다.



(사진 추가)


그리고 home.html을 조금 수정해준다.

<img> 태그를 이용하여 사진을 올려준다.



(확인)


홈페이지에 귀여운 보노보노가 올라갔다.

DirectX는 고성능 그래픽, 사운드 등의 주변 장치와 상호 작용을 필요로 하는 프로그램(특히 게임)을 작성하는 데 사용하는 API이다.

API 모음이라고 생각하면된다.
그렇다면 DirectX를 이용하여 간단한 프로그램을 만들어 볼 것이다.

윈도우즈 프로그래밍 카테고리에서 만들었던 Hello World 창 만드는 코드에서 덧붙여서 작성할 것이다. 

추가할 것은 헤더파일이다.


(graphics.h 추가)


헤더파일을 추가해주고, graphics 포인터를 만들것이다.



(graphics 포인터)


Graphics라는 클래스를 우리는 아직 만들지 않았다. 이제 만들어 볼 것이다.

헤더 파일을 하나 생성해준다. graphics.h



(include, define 부분)


이 헤더를 중복하여 정의가 되면 에러가 난다. 그러므로 컴파일 할 때 이 파일이 포함되어 있다면 다시 포함시켜 중복정의가 일어나지 않도록 첫부분에서 해주었다.

Graphics 클래스를 만들어준다.



(Graphics_private)


private 영역이다. 사용할 변수들과 initD3Dpp 함수를 선언했다.




(Graphics_public)


public 영역이다.
기본적인 생성자와, 소멸자를 선언했고, releaseAll() 함수는 소멸자에서 사용할 것인데 할당 받은 것들을 전부 반환시키기 위한 함수이다.
initialize 함수는 Graphics 클래스를 만든 후 초기화할 때 사용할 함수이다.
그 다음 showBackbuffer 함수를 선언한다.
자세한 함수 내용은 아래서 코드 설명할 때 할 것이다!

기존 Hello World 프로그램의 WinMain 함수에서 추가한다.



(메모리 누수)


위의 두줄은 메모리 누수를 탐지하기 위한 것이다.
디버그 모드로 빌드 시 메모리 누수를 확인한다.

메모리 누수란 프로그램이 종료될 때 할당받았던 메모리를 잘 반납하지 않고 종료가 되어 메모리가 야금야금 줄어드는 현상이다. 그렇기에 생성한 객체들을 종료할 때는 잘 반납시켜주고 종료해야한다.

윈도우를 생성한 뒤 Graphics 객체를 생성한다.



(Graphics 객체 생성)


아까 만들었던 graphics 변수에 Graphics 객체를 생성한다.
그리고 initialize 함수로 초기화 해준다.

이제 그 함수들을 살펴볼 것이다.



(Graphics 생성자)


Graphics 생성자에서는 사용하는 변수들에 대한 초기 설정을 해준다.
direct3d 라는 변수에는 우리가 Direct3D 객체를 만들 것이다.

device3d 라는 변수는 Direct3D 객체의 함수를 이용하여 디바이스 인터페이스를 만들고 저장할 곳이다.



(initialize 함수)


Direct3DCreate9 함수는 Direct3D 객체를 만들고 객체에게 줄 인터페이스를 얻는 함수이다.
함수 인자로는 해당 SDK의 버전을 넘겨줘야한다.
D3D_SDK_VERSION 값은 DirectX 헤더 파일에 의해 설정되는 값이다. 컴파일 하는 동안 사용되는 DirectX의 버전이 대상 시스템에 설치된 런타임 DLL과 일치하는지 확인하는데 사용되는 값이다.

성공적으로 실행되면 IDirect3D9 인터페이스를 가리키는 포인터를 반환하고 그렇지 않으면 NULL을 반환한다.
그 후 디바이스를 생성하는 코드이다.
CreateDevice 함수는 디바이스 인터페이스를 반환한다.
아래 순서대로 인자의 정보이다.

Adapter : 사용할 디스플레이 어댑터 수. D3DADAPTER_DEFAULT는 기본 디스플레이 어댑터를 지정하는데 사용된다.

DeviceType : D3DDEVTYPE_HAL. 하드웨어 레스터 변환을 지정한다. 이 값으로 지정한 후 CreateDevice가 실패하면 어댑터가 하드웨어 그래픽 가속을 지원하지 않는다는 것을 의미한다.

hFocusWindow : 포커스 윈도우는 Direct3D에게 애플리케이션이 포그라운드모드에서 백그라운드 모드로 전환했다고 알려준다. 창화면만 사용하면 NULL로 설정한다. 일반적으로 윈도우에 대한 핸들을 넘겨준다.

BehaviorFlag : 정점 처리를 어떻게 하는지에 대한 설정이다. 일단 소프트웨어를 이용한 정점 처리로 설정한다. 뒷부분에서 하드웨어가 지원 가능한지 체크하는 코드부분을 추가하고 하드웨어 정점 처리 방식을 추가할 것이다.

*pPresentationParameters : 생성할 디바이스에 대한 속성을 지정하는 D3DPRESENT_PARAMETERS 구조체를 가리키는 포인터이다. 이 구조체를 채우기 위해 initD3Dpp 함수를 만들 것이다.

**ppReturnedDeviceInterface : 반환값이다. 이 주소에 생성된 디바이스의 주소가 적힐 것이다.

그렇다면 이제 D3DPRESENT_PARAMETERS 구조체를 채울 initD3Dpp 함수를 볼 것이다.




(initD3Dpp 함수)


d3dpp 구조체의 값을 설정하는 함수이다.

이제 이렇게 디바이스까지 생성을 했다면 WinMain 함수를 이어서 본다.



(메인 메시지 루프)


처리할 메시지가 없다면 화면을 그려주는 graphics의 showBackbuffer() 함수를 실행한다.




(showBackbuffer 함수)


버퍼를 표시하기 전에 버퍼를 비우는 함수가 나온다.
Clear 함수이다.
인자는 순서대로 다음과 같다.
Count : pRects가 가리키고 있는 배열에 나열돼 있는 지울 사각형의 개수이다. pRects가 NULL이라면 0으로 설정한다.

pRects : 지울 사각형을 기술하는 D3DRECT 구조체 배열을 가리키는 포인터이다. 여기서는 버퍼 전체를 지울것이기 때문에 NULL로 설정한 것이다.

Flags : 비울 표현의 형식을 지정하기 위해 D3DCLEAR에 정의된 하나 이상의 플래그다. 여기서는 D3DCLEAR_TARGET을 사용한다. 이는 버퍼를 비우길 원한다는 것이다.

Color : RGB색상의 값이다.

Z : 깊이 버퍼이다. 0에서 1사이의 부동소수점 숫자로 z값을 정한다. 여기서는 사용하지 않으므로 0.0f로 설정한다.

Stencil : 스텐실 버퍼는 깊이 버퍼와 관련이 있다. 여기서 사용하지 않을 것이므로 0으로 설정한다.

그 후 Present 함수로 화면에 백 버퍼를 표시한다.
아까 우리가 initD3Dpp 함수에서 SwapEffect 변수에 DISCARD라는 값으로 설정했다.
화면에 보이는 버퍼와 화면에 보이지 않는 버퍼(백 버퍼)가 존재한다.
Page Flipping 기술을 쓸 것인데 화면에 표시되고 있을 때 백 버퍼에 그 다음에 출력할 것들을 계산해서 넣어두는 것이다. 그리고 화면에 백버퍼에 있던 것을 보여준다. 그러면 그 전에 화면에 보이던 버퍼가 백버퍼가 된다. 이런식으로 화면을 넘기면서 움직이는 것이다.

DISCARD로 설정한 것은 이전에 보였던 버퍼의 내용을 삭제하라고 하는 것이다. 디스플레이 버퍼의 이전 내용을 삭제하면 페이지 전환에서 최고의 성능을 발휘할 수 있다.

그렇다면 이제 빌드 한 후 프로그램을 실행 시켜보겠다.



(프로그램 실행)


화면처럼 초록색화면이 뜨는 것을 볼 수 있다.

이제는 ESC 키를 추가해 볼 것이다. 전체 화면일 때는 엑스박스가 없으므로 종료 시킬 수가 없다.

윈도우즈 프로그래밍 카테고리에서 키보드 입력 포스팅글에 나온 대로 추가한다.



(ESC키 메시지)


ESC키가 눌리면 프로그램 종료 메세지를 메세지 큐에 보낸다.

다음은 FULLSCREEN 변수를 통해 전체화면으로도 동작 할 수 있도록 만들어 볼 것이다.



(전체화면)


이전에는 WS_OVERLAPPEDWINDOW 로만 설정했었는데, 이제 FULLSCREEN의 값에 따라 전체화면인지 창화면인지 선택 할 수 있다.

더 추가해 볼것은 창 화면의 크기이다.
윈도우를 생성할 때 CreateWindow 함수의 width와 height는 윈도우 타이틀 바와 테두리를 포함하기 때문에 우리가 원하는 정확한 클라이언트 영역의 크기가 아니게 된다. 우리는 게임이 표시되는 테두리 안쪽 부분의 크기를 정확하게 재조정할 필요가 있다.

클라이언트 영역의 크기를 구하기 위해 GetClientRect를 사용한다.
새로 정의될 너비와 높이이다.
새 너비 = 원하는 너비 + ( 원하는 너비 - 클라이언트 너비 )

새 높이 = 원하는 높이 + ( 원하는 높이 - 클라이언트 높이 )



(크기 재조정)


크기를 재조정하는 함수는 MoveWindow 함수를 이용할 것이다.

이제 마지막으로 아까 소프트웨어 정점 처리만 사용한 것을
만약 지원가능한 어댑터를 찾았다면 하드웨어 정점 처리를 사용할 수 있도록 처리할 것이다.

graphics.h 헤더 파일에서 다음을 추가해준다.



(추가)


그리고 isAdapterCompatible 함수를 작성해준다.



(isAdapterCompatible 함수)


이GetAdapterModeCount 함수는 지정된 어댑터에 대해 사용 가능한 디스플레이 모드의 수를 반환하는 함수이다.

그리고 하나씩 돌아보면서 지원가능한 모드를 만나면 true를 반환한다.
EnumAdapterModes 함수를 통해 pMode의 구조체에 하드웨어에 대한 자세한 정보를 얻을 수 있다.

initialize 함수에 추가해 볼 것이다.



(새 initialize 함수)


전체 화면이고 어댑터가 호환이 된다면 refresh 속도를 호환되는 것으로 설정한다.
그리고 하드웨어 정점 처리 기능을 수행할 수 있는 지 체크하기 위해 GetDeviceCaps 메소드를 사용한다. 가능하다면 behavior에 하드웨어 정점처리 플래그를 넣고 가능하지 않다면 소프트웨어 정점처리 플래그를 넣고 behavior를 디바이스 생성할 때 넘겨주면 된다.

(* 참조 - 2D 게임 프로그래밍, 찰스 켈리)


DirectX 개발시
프로젝트의 설정이다.

1. 포함 디렉터리
-> Visual Studio에게 DirectX 헤더 파일을 찾아볼 곳을 알려주는 역할을 한다.

(포함디렉터리1)


구성을 모든 구성으로 만든다.





(포함디렉터리2)


왼쪽 창에서 VC++ 디렉터리를 클릭하고 오른쪽 창의 포함 디렉터리를 클릭한다.





(포함디렉터리3)


오른쪽 드롭다운 화살표를 클릭하고 <편집...>을 클릭한다.





(포함디렉터리4)


위와 같이
$(DXSDK_DIR)\Include
라고 적고 확인을 클릭한다.



2. 라이브러리 디렉터리

-> 링커에게 DirectX 라이브러리 파일을 찾아볼 곳(경로)을 알려준다.



(라이브러리 디렉터리1)


1번 설정에 이어서 오른쪽 창에서 라이브러리 디렉터리를 클릭하고
드롭다운 화살표를 클릭하여 <편집...>을 클릭한다.





(라이브러리 디렉터리2)


위와 같이
$(DXSDK_DIR)\Lib\x86
이라고 적고 확인을 클릭한다.

3. 런타임 라이브러리
-> C 런타임 라이브러리를 정적으로 링크해 C 런타임 라이브러리 DLL이 설치되지 않은 시스템에서도 프로그램이 동작하게 설정할 것이다.


(런타임 라이브러리1)


왼쪽 상단에 구성을 Release로 해준다.




(런타임 라이브러리2)


C/C++ 을 클릭하고 하위 메뉴를 열어준다.
거기에서 코드 생성을 클릭해주고
오른쪽 창에서 런타임 라이브러리를 /MT 로 설정한다.

4. 추가 종속성

-> Visual Studio에게 어떤 DirectX 라이브러리를 추가해야 하는지 알려준다.


(추가 종속성1)


왼쪽 상단의 구성을 모든 구성으로 만든다.
그 후 링커를 클릭하고 하위 메뉴를 열어준다.
그리고 오른쪽 창의 추가 종속성을 클릭하고 드롭다운 화살표를 클릭하여 <편집...>을 클릭한다.




(추가 종속성2)


위와 같이
d3d9.lib
이라고 적어주고 확인을 클릭한다.

(*참조 - 2D 게임 프로그래밍, 찰스 켈리)

프로그램이 두번 열리는 것을 방지하는 것이다.

프로그램을 실행 시키고나서, 의도적이든 실수든 또 실행 시킬 수 있다.
여러 프로그램을 동시에 돌리듯, 한 프로그램을 여러개를 동시에 실행하는 것이다.
그것을 막기 위한 방법이다.

아래와 같은 함수를 사용할 것이다.


(함수 선언)



함수 내용은 다음과 같다.



(mutex)



뮤텍스는 한 번에 하나의 스레드만 소유할 수 있는 객체로써
고유 문자열의 뮤텍스를 생성시킨다. 그리고 뮤텍스가 생성되면 false를, 중복이 되면 ERROR_ALREADY_EXISTS 에러가 뜨는데 이 에러가 나면 true를 return한다.

윈도우즈 창을 생성하기 전에 아래와 같은 코드를 추가하면 된다.



(중복 체크)



중복이지 않을 때
아래의 코드로 넘어가서 윈도우즈 창이 생성되게 된다.

컴파일 후 실행해본다.



(실행)



이 프로그램을 실행시키고, 같은 프로그램을 다시 한번 실행하면 실행되지 않는다.
즉, 하나만 실행이 된다.

(*참조 - 2D 게임프로그래밍, 찰스 켈리)


Windows 창에서 키보드의 입력을 받고 화면에 그리는 작업을 해볼 것이다. 

윈도우즈에서 특정 장치에 접근하기 위해 애플리케이션은 GDI(Graphics Device Interface)에게 적절한 디바이스 드라이버를 로드하고, 그리기 위한 장치를 준비해야한다고 알려준다.

이 과정은 디바이스컨텍스트(DC, Device Context)를 만들어서 수행한다.
DC는 Graphics객체와 속성을 정의하는 구조체이다.
윈도우즈 운영체제는 윈도우를 만들 때, DC를 만들어서 화면에 그린다.

그렇다면 키보드의 입력을 받아 윈도우 창에 입력받은 문자를 그리는 프로그램을 작성해 볼 것이다.

전에 포스팅했던 창 만들기 프로그램에서 다른 부분을 위주로 설명한다.

전역 변수와, 상수 부분이다.


(전역변수, 상수)



WinMain 함수이다.



(WinMain 함수)



동작은 창 만들기 프로그램과 같다.

다만 이제 이벤트 콜백 함수부분이 달라진다.



(추가된 부분)



바로 전에는 WM_DESTROY 메시지만 우리가 처리하였다. (종료되는 메시지)

이제 키보드의 입력을 받을 때인 WM_CHAR 메시지에 대한 처리를 추가해주면 된다.



(키보드 입력)



문자가 아닌 부분의 경우 비프음을 내고, 아무 동작을 하지 않게 끝난다.
하지만 표시할 수 있는 문자의 경우 ch 변수에 문자를 가져온다.

여기서 InvalidateRect 함수가 나오는데, 이는 윈도우즈 프로그램의 특징을 이해해야한다.

WM_PAINT 메시지를 발생시키기 위한 것인데, WM_PAINT 메시지는 flag 타입의 메시지이다. 메시지큐에 들어가서 처리되는 것이 아니라 해당 flag를 표시한다.
그리고 프로그램은 메시지큐를 처리한 후 해당 flag를 확인하고 표시 되어있으면
WM_PAINT를 처리한다.

WM_PAINT는 화면을 다시 그려주라는 메시지이다.
그래서 InvalidateRect 함수를 통해 WM_PAINT flag를 on 해준다.

그렇게 되면 메시지큐가 다 처리된 후 해당 비트를 확인하여 WM_PAINT를 처리할 때 아래와 같이 처리하도록 처리한다.



(WM_PAINT)



DC 핸들을 가져온다.
hdc에 가져오는 것이다.
그리고 윈도우 창에 사각형을 가져온다.

이제 TextOut을 통해 hdc를 가지고 화면에 그려준다.

다음은 CreateMainWindow 함수인데, 이전 창 만들기의 부분과 같다.




(CreateMainWindow)



등록까지 마쳤으면

윈도우를 생성하고 화면에 표시하는 부분이다.



(창 생성)



컴파일 후 실행해본다.



(실행)



해당 화면은 D 라는 키보드를 입력했을 때의 모습이다.
잘 동작한다.


그렇다면 키를 누르고 있는 것은 어떻게 인식시켜야할까?
바로 윈도우 메시지 중 WM_KEYDOWN, WM_KEYUP을 처리해주면 된다.

키보드를 눌렀을 때 현재 눌린 것을 T로 표시, 그렇지 않은 것을 F로 표시하는 프로그램을 작성해본다.

번저 키 입력에 대한 배열을 만들어준다.



(키 배열)



main함수에서 처음에 초기화 과정을 거친다.


(초기화)



WM_CREATE 부분이다.


(WM_CREATE)



WM_CREATE는 창이 생성될 때 발생하는 메시지로
창이 생성될 때, 화면에 배열을 그려주는 역할을 한다.

그 다음 키가 눌릴 때이다.



(키가 눌릴 때)



위 사진은 키가 눌렸을 때의 처리이다.
아래 스위치는 shift 키와 control 키를 누르는 것에 대한 처리이다.
아래 if문은 왼쪽과 오른쪽 shift 키를 구분하기 위한것이다.


(키 땔때)



키를 땔때의 메시지 처리는 해당 키 배열값을 false로 만들어준다.

WM_PAINT 메시지 처리 중 다른점은 배열 출력이다.



(WM_PAINT)



기존에는 해당 문자를 출력하는 것이었지만

이젠 배열에서 눌렸으면 T를 안눌렸으면 F를 출력하는 것으로 다르다.



(*참조 - 2D 게임 프로그래밍, 찰스 켈리)

+ Recent posts