posted by REDFORCE 2017. 5. 18. 22:14

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics

 + Image - SpriteData

 + Layer  

 + Input (현재 글)

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


-----------------------------------------------------------------------


이번글에서 설명 할 내용은 Input Class 입니다.


Input 클래스는 엔진에서의 모든 키입력 처리에 대한 것을 담당하는 클래스입니다.


일반적인 키보드 입력, 마우스 입력 부터 XBox Pad의 입력에 대한 처리를 담당합니다.




먼저 헤더에서의 주요 내용들을 살펴보도록 하겠습니다.


헤더에서 다룰 내용 목록은 다음과 같습니다.


1. 키 입력을 위한 주요 변수들

2. Input 클래스의 주요 함수 목록 


본 글에서는 자세한 게임패드 입력처리에 대한 설명은 하지 않습니다.

게임패드 입력처리에 대한 자세한 내용은 다음에 기회가 되면 다루도록 하겠습니다.

(#include <XInput>제목으로 별도의 포스팅을 통해 다룰 예정입니다.)




1. 먼저 Input.h 의 상단에 위치한 내용들을 보도록 하겠습니다.


#pragma once
#ifndef _INPUT_H // Prevent multiple definitions if this
#define _INPUT_H // file is included in more than one place
#define WIN32_LEAN_AND_MEAN
class Input;
#include <windows.h>
#include <WindowsX.h>
#include <string>
#include <XInput.h>
#include "constants.h"
#include "gameError.h"
// for high-definition mouse
#ifndef HID_USAGE_PAGE_GENERIC
#define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)
#endif
#ifndef HID_USAGE_GENERIC_MOUSE
#define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)
#endif
//--------------------------
namespace inputNS
{
const int KEYS_ARRAY_LEN = 256; // size of key arrays
// what values for clear(), bit flag
const UCHAR KEYS_DOWN = 1;
const UCHAR KEYS_PRESSED = 2;
const UCHAR MOUSE = 4;
const UCHAR TEXT_IN = 8;
const UCHAR KEYS_MOUSE_TEXT = KEYS_DOWN + KEYS_PRESSED + MOUSE + TEXT_IN;
}
const DWORD GAMEPAD_THUMBSTICK_DEADZONE = (DWORD)(0.20f * 0X7FFF); // default to 20% of range as deadzone
const DWORD GAMEPAD_TRIGGER_DEADZONE = 30; // trigger range 0-255
const DWORD MAX_CONTROLLERS = 4; // Maximum number of controllers supported by XInput
// Bit corresponding to gamepad button in state.Gamepad.wButtons
const DWORD GAMEPAD_DPAD_UP = 0x0001;
const DWORD GAMEPAD_DPAD_DOWN = 0x0002;
const DWORD GAMEPAD_DPAD_LEFT = 0x0004;
const DWORD GAMEPAD_DPAD_RIGHT = 0x0008;
const DWORD GAMEPAD_START_BUTTON = 0x0010;
const DWORD GAMEPAD_BACK_BUTTON = 0x0020;
const DWORD GAMEPAD_LEFT_THUMB = 0x0040;
const DWORD GAMEPAD_RIGHT_THUMB = 0x0080;
const DWORD GAMEPAD_LEFT_SHOULDER = 0x0100;
const DWORD GAMEPAD_RIGHT_SHOULDER = 0x0200;
const DWORD GAMEPAD_A = 0x1000;
const DWORD GAMEPAD_B = 0x2000;
const DWORD GAMEPAD_X = 0x4000;
const DWORD GAMEPAD_Y = 0x8000;
struct ControllerState
{
XINPUT_STATE state;
XINPUT_VIBRATION vibration;
float vibrateTimeLeft; // mSec
float vibrateTimeRight; // mSec
bool connected;
};
view raw input.h hosted with ❤ by GitHub


 (1) namespace inputNS를 보시면 사용 될 모든 키들에 대한 값들을 위해 KEYS_ARRAY_LEN이 있습니다. 사용 할 키들의 사이즈 크기라 보시면 됩니다. (즉, 256 개의 키를 지원)

 (2) 다음 키다운 이나 눌러졌다를 판단할 용도의 KEYS_DOWN / KEYS_PRESSED 플래그

 (3) 마우스에 대한 플래그 값 MOUSE와 텍스트 입력에 대한 플래그 값 TEXT_IN 플래그

 (4) 모든 키들을 Clear시킬 용도로 각 플래그 값들을 합친 KEYS_MOUSE_TEXT 플래그


그리고 그 아래에는 게임 패드를 사용하는 것들에 대한 키 플래그값들이 나열되어 있습니다.

게임패드에 대한 입력을 처리할 목적으로 선언되어 있으나 패드를 지원하는 게임을 만들 게 아니라면 딱히 사용할 일은 없습니다.


게임 패드에 대한 선언은 본 글에서는 생략하고 추후 XInput에 대한 것을 다룰 때 다시 설명하도록 하겠습니다.



다음, Input 클래스에서 키 입력 처리를 위해 사용 되어지는 주요 변수들 입니다.


class Input
{
private:
bool keysDown[inputNS::KEYS_ARRAY_LEN]; // true if specified key is down
bool keysPressed[inputNS::KEYS_ARRAY_LEN]; // true if specified key was pressed
bool mouseWheelRoll; // true if mouseWheel rolling
bool mouseWheelRollUp; // true if mouseWheel zDelta > 0
bool mouseWheelRollDown; // true if mouseWheel zDelta < 0
std::string textIn; // user entered text
char charIn; // last character entered
bool newLine; // true on start of new line
int mouseX, mouseY; // mouse screen coordinates
int mouseRawX, mouseRawY; // high-definition mouse data
RAWINPUTDEVICE Rid[1]; // for high-definition mouse
bool mouseCaptured; // true if mouse captured
bool mouseLButton; // true if left mouse button down
bool mouseMButton; // true if middle mouse button down
bool mouseRButton; // true if right mouse button down
bool mouseX1Button; // true if X1 mouse button down
bool mouseX2Button; // true if X2 mouse button down
ControllerState controllers[MAX_CONTROLLERS]; // state of controllers
}
view raw Input.h hosted with ❤ by GitHub

(private: 접근한정자 안에 선언 된 주요 변수들을 먼저 코드로 올렸습니다. 실제 전체 코드는 public: 영역도 포함하여 최 하단에 업로드 되어있습니다.)



 (1) keysDown[KEYS_ARRAY_LEN] / keysPressed[KEYS_ARRAY_LEN] (키 입력 여부를 판단할 목적의 bool 변수)

 (2) bool mouseWheelRoll / Rollup/ RollDown (마우스 휠을 굴렸을 때의 이벤트 판단목적)

 (3) std::string textIn (텍스트 입력값들을 저장할 목적)

 (4) char charIn (텍스트 입력 시 한 글자 값들을 저장)

 (5) bool newLine (줄바꿈 여부를 판단할 목적)

 (6) int mouseX / Y (현재 마우스 커서의 x,y 위치)

 (7) int mouseRawX / Y (현재 마우스커서의 고감도 x, y 위치)

 (8) RAWINPUTDEVICE Rid[1] (고감도 마우스 지원 여부 판단 목적의 변수)

 (9) bool mouseCaptured (마우스 위치 캡쳐 목적의 플래그 값)

 (10) bool mouseL/M/RButton (마우스 좌/우/중앙 키 입력 플래그 값)

 (11) bool mouseX1/X2Button (마우스 특수버튼 1,2 키 플래그 값)

 (12) ControllerState controllers[MAX_CONTROLLERS] (게임 패드 이용 시 사용하는 플래그 값)


들로 주요 변수들이 이루어져 있습니다.


일반적인 게임 키처리에 대한 변수들과 동일한 구조 형태라 아마 이름만봐도 아 저변수가 무슨용도구나 하고 이해하시는데는 불편함이 없을거라 생각합니다.



그럼 마지막으로 각 키입력에 대한 함수들의 구조를 보겠습니다.


2. 주요 함수 목록


class Input
{
public:
// Constructor
Input();
// Destructor
virtual ~Input();
// Initialize mouse and controller input.
// Throws GameError
// Pre: hwnd = window handle
// capture = true to capture mouse.
void initialize(HWND hwnd, bool capture);
// Save key down state
void keyDown(WPARAM);
// Save key up state
void keyUp(WPARAM);
// Save the char just entered in textIn string
void keyIn(WPARAM);
// Returns true if the specified VIRTUAL KEY is down, otherwise false.
bool isKeyDown(UCHAR vkey) const;
// Return true if the specified VIRTUAL KEY has been pressed in the most recent frame.
// Key presses are erased at the end of each frame.
bool wasKeyPressed(UCHAR vkey) const;
// Return true if any key was pressed in the most recent frame.
// Key presses are erased at the end of each frame.
bool anyKeyPressed() const;
// Clear the specified key press
void clearKeyPress(UCHAR vkey);
void mouseWheelIn(int zDelta);
bool isMouseWheelUp();
bool isMouseWheelDown();
// Clear specified input buffers where what is any combination of
// KEYS_DOWN, KEYS_PRESSED, MOUSE, TEXT_IN or KEYS_MOUSE_TEXT.
// Use OR '|' operator to combine parmeters.
void clear(UCHAR what);
// Clears key, mouse and text input data
void clearAll() {clear(inputNS::KEYS_MOUSE_TEXT);}
// Clear text input buffer
void clearTextIn() {textIn.clear();}
// Return text input as a string
std::string getTextIn() {return textIn;}
// Return last character entered
char getCharIn() {return charIn;}
// Reads mouse screen position into mouseX, mouseY
void mouseIn(LPARAM);
// Reads raw mouse data into mouseRawX, mouseRawY
// This routine is compatible with a high-definition mouse
void mouseRawIn(LPARAM);
// Save state of mouse button
void setMouseLButton(bool b) { mouseLButton = b; }
// Save state of mouse button
void setMouseMButton(bool b) { mouseMButton = b; }
// Save state of mouse button
void setMouseRButton(bool b) { mouseRButton = b; }
// Save state of mouse button
void setMouseXButton(WPARAM wParam) {mouseX1Button = (wParam & MK_XBUTTON1) ? true:false;
mouseX2Button = (wParam & MK_XBUTTON2) ? true:false;}
// Return mouse X position
int getMouseX() const { return mouseX; }
// Return mouse Y position
int getMouseY() const { return mouseY; }
// Return raw mouse X movement. Left is <0, Right is >0
// Compatible with high-definition mouse.
int getMouseRawX() const { return mouseRawX; }
// Return raw mouse Y movement. Up is <0, Down is >0
// Compatible with high-definition mouse.
int getMouseRawY() const { return mouseRawY; }
// Return state of left mouse button.
bool getMouseLButton() const { return mouseLButton; }
// Return state of middle mouse button.
bool getMouseMButton() const { return mouseMButton; }
// Return state of right mouse button.
bool getMouseRButton() const { return mouseRButton; }
// Return state of X1 mouse button.
bool getMouseX1Button() const { return mouseX1Button; }
// Return state of X2 mouse button.
bool getMouseX2Button() const { return mouseX2Button; }
}
view raw input.h hosted with ❤ by GitHub


지금 위에 언급된 함수 외에도 게임패드관련 함수가 상당히 많이 있습니다만, 분량이 너무 길어지는 관계로 위 코드에는 올리지 않았습니다. 최하단 전체 코드에는 게임패드 관련 함수도 같이 들어있습니다.


헤더에서 함수가 뭐 이렇게 많아!? 하고 겁먹으실 필요는 없습니다.

어짜피 엔진을 쓰면서 사용하게 될 함수는 8~90% isKeyDown / wasKeyPressed 밖에 없으니까요. 게임패드 관련 함수를 뺏음에도 상당히 부담이 가신다면 아래와 같이 간단히 이런 게 있다 라고만 보시면 될 것 같습니다.


 (1) 생성자 / 소멸자

 (2) KeyDown / Up / In (키입력에 대한 판단 함수)

 (3) isKeyDown (키가 눌러졌냐~? 라고 물어보는 함수)

 (4) wasKeyPressed (최근 프레임 1~2사이에 키가 눌려졌냐~? 라고 물어보는 함수)

 (5) anyKeyPressed (아무키나 눌러졌었냐~? 라고 물어보는 함수)

 (6) clearKeyPressed (눌려진적이 있는 키를 다시 false로 초기화 하는 함수)

 (7) mouseWheel In / Up / Down (마우스 휠이 굴려졌을때 위로굴렸냐 아래로 굴렸냐 체크하는 함수)

 (8) clear / clearAll / clearTextIn 각각 키 입력에 대한 Clear 함수

 (9) getTextIn / getCharIn (문장 / 한 글자 입력 시 내용 받아오는 함수)

 (10) set/ get Mouse L/M/R/X Button (마우스 키 입력 또는 받아오는 함수)

 (11) getMouseX / Y , getMouseRawX /Y (마우스 좌표 받아오는 함수)


키 입력에 대한 처리를 하는 함수들은 위와 같이  11가지가 있습니다.

결과적으로는 모두 다 사용하는 함수들이기 때문에 대충 어떤목적에 사용한다 라는 것만 알고 넘어간 뒤 엔진을 사용하면서 쓰다보면 자연스레 본인이 쓰고 있는 모습을 볼 수 있습니다.


자세한 각 함수들에 구현 내용은 .cpp로 이어서 적도록 하겠습니다.



아래는 전체 헤더파일 내용입니다.


#pragma once
#ifndef _INPUT_H // Prevent multiple definitions if this
#define _INPUT_H // file is included in more than one place
#define WIN32_LEAN_AND_MEAN
class Input;
#include <windows.h>
#include <WindowsX.h>
#include <string>
#include <XInput.h>
#include "constants.h"
#include "gameError.h"
// for high-definition mouse
#ifndef HID_USAGE_PAGE_GENERIC
#define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)
#endif
#ifndef HID_USAGE_GENERIC_MOUSE
#define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)
#endif
//--------------------------
namespace inputNS
{
const int KEYS_ARRAY_LEN = 256; // size of key arrays
// what values for clear(), bit flag
const UCHAR KEYS_DOWN = 1;
const UCHAR KEYS_PRESSED = 2;
const UCHAR MOUSE = 4;
const UCHAR TEXT_IN = 8;
const UCHAR KEYS_MOUSE_TEXT = KEYS_DOWN + KEYS_PRESSED + MOUSE + TEXT_IN;
}
const DWORD GAMEPAD_THUMBSTICK_DEADZONE = (DWORD)(0.20f * 0X7FFF); // default to 20% of range as deadzone
const DWORD GAMEPAD_TRIGGER_DEADZONE = 30; // trigger range 0-255
const DWORD MAX_CONTROLLERS = 4; // Maximum number of controllers supported by XInput
// Bit corresponding to gamepad button in state.Gamepad.wButtons
const DWORD GAMEPAD_DPAD_UP = 0x0001;
const DWORD GAMEPAD_DPAD_DOWN = 0x0002;
const DWORD GAMEPAD_DPAD_LEFT = 0x0004;
const DWORD GAMEPAD_DPAD_RIGHT = 0x0008;
const DWORD GAMEPAD_START_BUTTON = 0x0010;
const DWORD GAMEPAD_BACK_BUTTON = 0x0020;
const DWORD GAMEPAD_LEFT_THUMB = 0x0040;
const DWORD GAMEPAD_RIGHT_THUMB = 0x0080;
const DWORD GAMEPAD_LEFT_SHOULDER = 0x0100;
const DWORD GAMEPAD_RIGHT_SHOULDER = 0x0200;
const DWORD GAMEPAD_A = 0x1000;
const DWORD GAMEPAD_B = 0x2000;
const DWORD GAMEPAD_X = 0x4000;
const DWORD GAMEPAD_Y = 0x8000;
struct ControllerState
{
XINPUT_STATE state;
XINPUT_VIBRATION vibration;
float vibrateTimeLeft; // mSec
float vibrateTimeRight; // mSec
bool connected;
};
class Input
{
private:
bool keysDown[inputNS::KEYS_ARRAY_LEN]; // true if specified key is down
bool keysPressed[inputNS::KEYS_ARRAY_LEN]; // true if specified key was pressed
bool mouseWheelRoll; // true if mouseWheel rolling
bool mouseWheelRollUp; // true if mouseWheel zDelta > 0
bool mouseWheelRollDown; // true if mouseWheel zDelta < 0
std::string textIn; // user entered text
char charIn; // last character entered
bool newLine; // true on start of new line
int mouseX, mouseY; // mouse screen coordinates
int mouseRawX, mouseRawY; // high-definition mouse data
RAWINPUTDEVICE Rid[1]; // for high-definition mouse
bool mouseCaptured; // true if mouse captured
bool mouseLButton; // true if left mouse button down
bool mouseMButton; // true if middle mouse button down
bool mouseRButton; // true if right mouse button down
bool mouseX1Button; // true if X1 mouse button down
bool mouseX2Button; // true if X2 mouse button down
ControllerState controllers[MAX_CONTROLLERS]; // state of controllers
public:
// Constructor
Input();
// Destructor
virtual ~Input();
// Initialize mouse and controller input.
// Throws GameError
// Pre: hwnd = window handle
// capture = true to capture mouse.
void initialize(HWND hwnd, bool capture);
// Save key down state
void keyDown(WPARAM);
// Save key up state
void keyUp(WPARAM);
// Save the char just entered in textIn string
void keyIn(WPARAM);
// Returns true if the specified VIRTUAL KEY is down, otherwise false.
bool isKeyDown(UCHAR vkey) const;
// Return true if the specified VIRTUAL KEY has been pressed in the most recent frame.
// Key presses are erased at the end of each frame.
bool wasKeyPressed(UCHAR vkey) const;
// Return true if any key was pressed in the most recent frame.
// Key presses are erased at the end of each frame.
bool anyKeyPressed() const;
// Clear the specified key press
void clearKeyPress(UCHAR vkey);
void mouseWheelIn(int zDelta);
bool isMouseWheelUp();
bool isMouseWheelDown();
// Clear specified input buffers where what is any combination of
// KEYS_DOWN, KEYS_PRESSED, MOUSE, TEXT_IN or KEYS_MOUSE_TEXT.
// Use OR '|' operator to combine parmeters.
void clear(UCHAR what);
// Clears key, mouse and text input data
void clearAll() {clear(inputNS::KEYS_MOUSE_TEXT);}
// Clear text input buffer
void clearTextIn() {textIn.clear();}
// Return text input as a string
std::string getTextIn() {return textIn;}
// Return last character entered
char getCharIn() {return charIn;}
// Reads mouse screen position into mouseX, mouseY
void mouseIn(LPARAM);
// Reads raw mouse data into mouseRawX, mouseRawY
// This routine is compatible with a high-definition mouse
void mouseRawIn(LPARAM);
// Save state of mouse button
void setMouseLButton(bool b) { mouseLButton = b; }
// Save state of mouse button
void setMouseMButton(bool b) { mouseMButton = b; }
// Save state of mouse button
void setMouseRButton(bool b) { mouseRButton = b; }
// Save state of mouse button
void setMouseXButton(WPARAM wParam) {mouseX1Button = (wParam & MK_XBUTTON1) ? true:false;
mouseX2Button = (wParam & MK_XBUTTON2) ? true:false;}
// Return mouse X position
int getMouseX() const { return mouseX; }
// Return mouse Y position
int getMouseY() const { return mouseY; }
// Return raw mouse X movement. Left is <0, Right is >0
// Compatible with high-definition mouse.
int getMouseRawX() const { return mouseRawX; }
// Return raw mouse Y movement. Up is <0, Down is >0
// Compatible with high-definition mouse.
int getMouseRawY() const { return mouseRawY; }
// Return state of left mouse button.
bool getMouseLButton() const { return mouseLButton; }
// Return state of middle mouse button.
bool getMouseMButton() const { return mouseMButton; }
// Return state of right mouse button.
bool getMouseRButton() const { return mouseRButton; }
// Return state of X1 mouse button.
bool getMouseX1Button() const { return mouseX1Button; }
// Return state of X2 mouse button.
bool getMouseX2Button() const { return mouseX2Button; }
// Update connection status of game controllers.
void checkControllers();
// Save input from connected game controllers.
void readControllers();
// Return state of specified game controller.
const ControllerState* getControllerState(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return &controllers[n];
}
// Return state of controller n buttons.
const WORD getGamepadButtons(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return controllers[n].state.Gamepad.wButtons;
}
// Return state of controller n D-pad Up
bool getGamepadDPadUp(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return ((controllers[n].state.Gamepad.wButtons&GAMEPAD_DPAD_UP) != 0);
}
// Return state of controller n D-pad Down.
bool getGamepadDPadDown(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return ((controllers[n].state.Gamepad.wButtons&GAMEPAD_DPAD_DOWN) != 0);
}
// Return state of controller n D-pad Left.
bool getGamepadDPadLeft(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return ((controllers[n].state.Gamepad.wButtons&GAMEPAD_DPAD_LEFT) != 0);
}
// Return state of controller n D-pad Right.
bool getGamepadDPadRight(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_DPAD_RIGHT) != 0);
}
// Return state of controller n Start button.
bool getGamepadStart(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_START_BUTTON) != 0);
}
// Return state of controller n Back button.
bool getGamepadBack(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_BACK_BUTTON) != 0);
}
// Return state of controller n Left Thumb button.
bool getGamepadLeftThumb(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_LEFT_THUMB) != 0);
}
// Return state of controller n Right Thumb button.
bool getGamepadRightThumb(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_RIGHT_THUMB) != 0);
}
// Return state of controller n Left Shoulder button.
bool getGamepadLeftShoulder(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_LEFT_SHOULDER) != 0);
}
// Return state of controller n Right Shoulder button.
bool getGamepadRightShoulder(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_RIGHT_SHOULDER) != 0);
}
// Return state of controller n A button.
bool getGamepadA(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_A) != 0);
}
// Return state of controller n B button.
bool getGamepadB(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_B) != 0);
}
// Return state of controller n X button.
bool getGamepadX(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_X) != 0);
}
// Return state of controller n Y button.
bool getGamepadY(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return bool((controllers[n].state.Gamepad.wButtons&GAMEPAD_Y) != 0);
}
// Return value of controller n Left Trigger.
BYTE getGamepadLeftTrigger(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return (controllers[n].state.Gamepad.bLeftTrigger);
}
// Return value of controller n Right Trigger.
BYTE getGamepadRightTrigger(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return (controllers[n].state.Gamepad.bRightTrigger);
}
// Return value of controller n Left Thumbstick X.
SHORT getGamepadThumbLX(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return (controllers[n].state.Gamepad.sThumbLX);
}
// Return value of controller n Left Thumbstick Y.
SHORT getGamepadThumbLY(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return (controllers[n].state.Gamepad.sThumbLY);
}
// Return value of controller n Right Thumbstick X.
SHORT getGamepadThumbRX(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return (controllers[n].state.Gamepad.sThumbRX);
}
// Return value of controller n Right Thumbstick Y.
SHORT getGamepadThumbRY(UINT n)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
return (controllers[n].state.Gamepad.sThumbRY);
}
// Vibrate controller n left motor.
// Left is low frequency vibration.
// speed 0=off, 65536=100 percent
// sec is time to vibrate in seconds
void gamePadVibrateLeft(UINT n, WORD speed, float sec)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
controllers[n].vibration.wLeftMotorSpeed = speed;
controllers[n].vibrateTimeLeft = sec;
}
// Vibrate controller n right motor.
// Right is high frequency vibration.
// speed 0=off, 65536=100 percent
// sec is time to vibrate in seconds
void gamePadVibrateRight(UINT n, WORD speed, float sec)
{
if(n > MAX_CONTROLLERS-1)
n=MAX_CONTROLLERS-1;
controllers[n].vibration.wRightMotorSpeed = speed;
controllers[n].vibrateTimeRight = sec;
}
// Vibrates the connected controllers for the desired time.
void vibrateControllers(float frameTime);
};
#endif
view raw input.h hosted with ❤ by GitHub


'MyProject > SephyEngine' 카테고리의 다른 글

June_Engine #3_LayerManager Class .cpp  (0) 2017.04.21
June_Engine #3_LayerManager Class .h  (0) 2017.04.21
June_Engine #3_Layer Class  (0) 2017.04.21
June_Engine #3_Image Class .cpp (2)  (0) 2017.04.20
June_Engine #3_Image Class .cpp (1)  (0) 2017.04.19
posted by REDFORCE 2017. 4. 21. 20:39

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics

 + Image - SpriteData

 + Layer  (현재 글)

 + Input

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


-----------------------------------------------------------------------


LayerManager Class .cpp 내용을 살펴보도록 하겠습니다.



1. 간단히 생성자와 소멸자 그리고 initialize 함수를 보도록 하겠습니다.


//=============================================================================
// LayerManager Class Code Start
//=============================================================================
LayerManager::LayerManager()
{
curLayerState = enLayerList::LAYER_NONE;
}
LayerManager::~LayerManager()
{
release();
}
HRESULT LayerManager::initialize()
{
addLayer(LayerManagerNS::none, new Layer(enLayerList::LAYER_NONE));
addLayer(LayerManagerNS::first, new Layer(enLayerList::LAYER_FIRST));
addLayer(LayerManagerNS::background, new Layer(enLayerList::LAYER_BACKGROUND));
addLayer(LayerManagerNS::default, new Layer(enLayerList::LAYER_DEFAULT));
addLayer(LayerManagerNS::ui, new Layer(enLayerList::LAYER_UI));
addLayer(LayerManagerNS::last, new Layer(enLayerList::LAYER_LAST));
return S_OK;
}
void LayerManager::release()
{
for (auto iter : layerList)
{
if (iter.second == NULL)
continue;
SAFE_DELETE(iter.second);
}
}


생성자 부분에서는 현재 LayerState 값이 LAYER_NONE 값으로 초기화 됩니다.

소멸자는 release 함수 호출뿐이군요.


initialize 함수에서는 미리 네임스페이스로 만들어두었던 기본 LAYER들 값들에 맞춰서

addLayer 함수를 호출하여 레이어를 등록하도록 되어있습니다.


반복문으로 처리하면 안되~? 라는 생각도 해봤습니다만


= _=아마 당시에 만들면서 귀찮아 그냥 몇개 안되니까 하드코딩으로 만들자

라고 생각했던 것 같네요.


enum Class 타입의 값을 반복문으로 알아서 처리되게끔 고치실 수 있다면 initialize 코드를 개선해보시는것도 좋을 것 같습니다.



release 함수에서는 각각 Layer들이 new 로 할당되어 등록되었기 때문에


해지해주는 과정이 이루어지고 있습니다.

(SAFE_DELETE 매크로를 이용하여 해지합니다. SAFE_DELETE 매크로는 graphics 글에서 언급했습니다.)



initialize 함수의 addLayer 과정을 통해 다음 그림과 같이 Layer가 layerVec에 쌓이게 됩니다.


레이어 순서는 번호 순서대로 위에서 아래로 렌더링 순서가 정해집니다.

enum class 형태의 값 순서대로 읽어 가기 때문인데요. 


여기서 중요한 점은 AddLayer 함수를 호출하면 레이어가 enum Class 값 형식에 맞춰서 정렬됩니다.


만약 같은 Layer 타입이라면 딱히 같은 타입들사이에서는 정렬 될 필요가 없다 느꼈기 때문에


다른 타입들간의 순서만 정렬시켜줍니다.



그럼 이제 AddLayer 함수를 살펴보겠습니다.


2. addLayer


addPlayer 함수는 initialize() 에서 Layer를 등록하면서 호출 되었덩 함수입니다.


코드를 먼저 보겠습니다.


bool LayerManager::addLayer(std::string layerKey, Layer * layer)
{
bool success = false;
// if layerKey is meaning none. return false
if (layerKey.compare(LayerManagerNS::none) == 0)
return success;
//if same layerName value in layerList. return false
for (auto iter : layerList)
{
if (iter.first.compare(layerKey) == 0)
return success;
}
layerList.emplace(layerKey, layer);
success = true;
sortLayer();
return success;
}


// 레이어 등록이 성공적으로 끝났다면 true값으로 바뀝니다.

Line 3 : bool success = false;   


// 만약 레이어 타입이 네임스페이스LayerManagerNS::none 과 같다면 false를 리턴합니다.

Line 6 : if (layerkey.compare (LayerManagerNS::none) == 0) 


Line 10 ~ 15 : 현재 레이어 리스트에 등록된 같은 레이어가 있는지 확인하여 있다면 false를 리턴합니다.


Line 17 : 여기까지 왔는데 아무이상없었으면 Layer를 등록합니다. 


Line 18 : success 값을 true 로 변경합니다.


Line 20 : 레이어 정렬 함수를 호출합니다.



아마 위에 적어둔 설명을 보시면 아마 AddLayer 함수 내용자체는 별로 어려운 것이 없을 것 입니다.


★주의 할 점은 현재 layerList 라고 하는 MAP 형태의 컨테이너에만 

Layer를 담았다는 것을 꼭 확인하시기 바랍니다.



3. deleteLayer


DeleteLayer() 함수는 AddLayer 함수와 반대로 등록 된 레이어를 삭제하는 함수입니다.


bool LayerManager::deleteLayer(std::string layerKey)
{
bool success = false;
for (auto iter : layerList)
{
if (iter.first.compare(layerKey) != 0)
continue;
layerList.erase(layerKey);
success = true;
return success;
}
return success;
}


간단히 layerList 에 등록 된 레이어 중에 파라미터로 받은 레이어 이름과 비교하여

등로 된 레이어가 있다면 layerList에서 해당 항목의 레이어를 erase 시킨 후

sortLayer() 함수를 호출하여 줍니다.



4. sortLayer


네 번째로, addLayer과 deleteLayer에서 호출한 sortLayer 함수를 살펴보겠습니다.


sortLayer는 함수명 그대로 레이어들을 정렬시키는 함수입니다.


void LayerManager::sortLayer()
{
std::vector<std::pair<std::string, Layer*>> vec(layerList.begin(), layerList.end());
std::sort(vec.begin(), vec.end(), compare_pair_second<std::less>());
layerVec = vec;
}



위에서 주의할 점으로 layerList 라고 하는 Map 타입의 컨테이너에 Layer를 담았었다는 것을 보셨을 겁니다.


그 이유는 바로 layerList (Map 컨테이너)에서 정렬을 수행 한 후

진짜 컨테이너인 layerVec (Vector 타입의 컨테이너) 에 정렬 시킨 후 담아두기 위함입니다.


sortLayer( ) 함수 자체는 간단하게 3줄로 되어있습니다.


1. std::vector<std::pair<std::string, Layer*>> 형태의 벡터 컨테이너를 먼저 만듭니다.

그리고 생성과 동시에 layerList에 담겨있는 Layer들을 그대로 begin 부터 end까지 넣어줍니다.


2. 미리 헤더에 생성해둔 Sort 템플릿을 이용하여 정렬시킵니다.


3. layerVec 의 값을 위에서 정렬 시킨 컨테이너로 넣어줍니다.



그리고 SortLayer에서 중요한 점은 아래의 템플릿을 이해를 하셔야 합니다.


template<template <typename> class P = std::less >
struct compare_pair_second {
template<class T1, class T2> bool operator()
(const std::pair<T1, T2>&left, const std::pair<T1, T2>&right)
{
return P<T2>()(left.second, right.second);
}
};
view raw layerManager.h hosted with ❤ by GitHub



방법은 간단히 좌우 값을 비교하여 작은 값 순서대로 정렬을 시킵니다.


아마 정렬하는 로직은 간단해서 이해하시기 어려움이 없을 것이오나, 템플릿을 사용하는 점에대해서는 스스로 템플릿이 뭔지 알고보셔야 아마 이해하시는데 문제가 없을거라 보입니다.



'MyProject > SephyEngine' 카테고리의 다른 글

June_Engine #2_Input .h  (0) 2017.05.18
June_Engine #3_LayerManager Class .h  (0) 2017.04.21
June_Engine #3_Layer Class  (0) 2017.04.21
June_Engine #3_Image Class .cpp (2)  (0) 2017.04.20
June_Engine #3_Image Class .cpp (1)  (0) 2017.04.19
posted by REDFORCE 2017. 4. 21. 07:18

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics

 + Image - SpriteData

 + Layer  (현재 글)

 + Input

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


-----------------------------------------------------------------------



이어서 LayerManager Class에 대해서 설명하도록 하겠습니다.



Layer Class를 보셨다면 헤더 파일 내용 중에

enum class 타입으로 선언되어있는 Layer 리스트를 보셨을 겁니다.


위 그림은 해당 리스트 내용을 그대로 순서대로 나열한 것 입니다.



다시 뒤로 돌아가서 코드 보기 귀찮으신 분들을 위해 enum Class만 다시 올려드립니다.


enum class enLayerList {
LAYER_NONE = 0,
LAYER_FIRST = 1,
LAYER_BACKGROUND,
LAYER_DEFAULT,
LAYER_UI,
LAYER_LAST
};
view raw layer.cpp hosted with ❤ by GitHub


보는 바와 같이 LAYER_NONE 부터 LAYER_LAST 까지.


 (1) LAYER_NONE : 초기화용 레이어 타입

 (2) LAYER_FIRST : 첫 번째 레이어

 (3) LAYER_BACKGROUND : 배경 레이어

 (4) LAYER_DEFAULT : 일반 레이어

 (5) LAYER_UI : UI 용 레이어

 (6) LAYER_LAST : 마지막 레이어


순으로 총 6가지의 레이어 타입이 있습니다.


만약 무엇보다 우선순위를 높여서 출력하고 싶으신 것이 있다면 첫 번째 레이어로 이미지 클래스에서 타입을 결정시키면 되고.


일반적인 이미지 출력(캐릭터, 아이템, 애니메이션 등)은 LAYER_DEFAULT 를 이용하시면 됩니다.

그 외에 레이어들은 다른 레이어와 똑같이 직관적이므로 설명은 생략하겠습니다.


만약 어떤 설정을 안한다면 모든 이미지들은 LAYER_DEFAULT 값으로 들어갑니다.



그럼 Layer에 대한 복습이 다시 됐으리라 생각하고, 

LayerManager를 바로 살펴보도록 하겠습니다.


Layer Manager 헤더 내용입니다.


#ifndef _LAYERMANAGER_H
#define _LAYERMANAGER_H
class LayerManager;
#include "cSingletonBase.h"
#include "layer.h"
#include <algorithm>
#include <string>
#include <vector>
#include <map>
//LayerManager enum enLayerList Matched layerName List
namespace LayerManagerNS
{
const std::string none = "LAYER_NONE";
const std::string first = "LAYER_FIRST";
const std::string background = "LAYER_BACKGROUND";
const std::string default = "LAYER_DEFAULT";
const std::string ui = "LAYER_UI";
const std::string last = "LAYER_LAST";
}
//Layer Manager Class has Singleton pattern
class LayerManager : public cSingletonBase<LayerManager>
{
private:
std::map<std::string, Layer*> layerList;
std::vector<std::pair<std::string, Layer*>> layerVec;
enLayerList curLayerState = enLayerList::LAYER_NONE;
public:
LayerManager();
~LayerManager();
//Manager Initialize
HRESULT initialize();
// Manager Release
void release();
// layerList Sorting -> layerVec
// LayerManager using layerVec
// Auto Sorting when AddLayer
void sortLayer();
//get All Layer Vector
std::vector<std::pair<std::string, Layer*>> getAllLayer() { return layerVec; }
//get a layer parameter(int layerNum)
Layer* getLayer(int layerNum);
//get a Layer parameter(std::string layerName)
Layer* getLayer(std::string layerName);
//get a Layer parameter(enum enlayerList)
Layer* getLayer(enLayerList layerState);
enLayerList getCurrentLayerState() { return curLayerState; }
//set currentLayer State
void setCurrentLayerState(enLayerList layerState) { curLayerState = layerState; }
void setLayerLoop();
//add Layer Function
bool addLayer(std::string layerKey, Layer* layer);
//delete Layer Function
bool deleteLayer(std::string layerName);
void printAllLayer();
};
template<template <typename> class P = std::less >
struct compare_pair_second {
template<class T1, class T2> bool operator()
(const std::pair<T1, T2>&left, const std::pair<T1, T2>&right)
{
return P<T2>()(left.second, right.second);
}
};
#define LAYERMANAGER LayerManager::GetInstance()
#endif // !_LAYERMANAGER_H
view raw layerManager.h hosted with ❤ by GitHub


1. LayerManagerNS


먼저 namespace 를 살펴보시면 const std::string 타입으로 미리 구성 된 LAYER의 이름들이 들어가있습니다.



2. 변수들


그리고 private: 에 선언되어있는 변수를 확인해주시기 바랍니다.


 (1) std::map<std::string, layer*> layerList;  (레이어 정렬 전 MAP 컨테이너)

 (2) std::vector<std::pair<std::string, layer*>> layerVec; (레이어 정렬 된 VECTOR 컨테이너)

 (3) enLayerList curLayerState; (현재 렌더링 해야할 레이어)


이렇게 3가지의 변수가 있습니다.



3. 함수들


다음  public: 에 여러가지 함수들이 있습니다만...

역시나 getter/setter함수들이 주로 있을 뿐, 현재 눈여겨 봐야 될 함수는 SortLayer( ) 밖에 없습니다.



4. 정렬용 템플릿


네 번째로 확인할 점은 레이어들을 정렬 시키기 위해 만들어 둔 템플릿 입니다.

std::less를 이용하여 받아온 매개변수를 left / right 비교를 통해 true/false를 뱉어줍니다.




헤더에서 일단 살펴볼 내용들은 위에 언급한 정도이고,

자세한 설명은 .cpp에서 이어가도록 하겠습니다.

'MyProject > SephyEngine' 카테고리의 다른 글

June_Engine #2_Input .h  (0) 2017.05.18
June_Engine #3_LayerManager Class .cpp  (0) 2017.04.21
June_Engine #3_Layer Class  (0) 2017.04.21
June_Engine #3_Image Class .cpp (2)  (0) 2017.04.20
June_Engine #3_Image Class .cpp (1)  (0) 2017.04.19
posted by REDFORCE 2017. 4. 21. 04:00

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics

 + Image - SpriteData

 + Layer  (현재 글)

 + Input

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


-----------------------------------------------------------------------


이번 글에서는 Layer Class에 대해 다루도록 하겠습니다.


Layer Class는 Image Class 내부에 들어가서 사용 되는 클래스 입니다.

즉, 어떤 이미지 또는 애니메이션을 돌릴 때 사용되어지는 모듈 입니다.



Q. 그럼 Layer Class가 대체 무엇인가?

A. 포토샵의 레이어와 비슷하다 생각하시면 될 것 같습니다.


흔히 레이어라 하면 겹겹이 쌓여서 출력되어지는 그런 계층 구조의 기능을 상상하실 수 있을 것 입니다.


그러나 보시고 계신 현재 Layer Class 는 Scene의 Render에서 

해당 Layer 단계의 출력 여부(true / false) 를 체크하여 Rendering 해줍니다.


Layer Class 자체는 Layer가 활성화 됐냐 안됐냐 여부만 갖고 있습니다.


그리고 활성화 여부에 맞춰,

씬의 Render 에서는 활성화 된 Layer만 Rendering을 해주게 됩니다.


Layer Class의 헤더 부터 살펴볼까요.


#ifndef _LAYER_H
#define _LAYER_H
#define WIN32_LEAN_AND_MEAN
class Layer;
#include "image.h"
#include <vector>
enum class enLayerList {
LAYER_NONE = 0,
LAYER_FIRST = 1,
LAYER_BACKGROUND,
LAYER_DEFAULT,
LAYER_UI,
LAYER_LAST
};
// Layer Class. This Class has Only LayerState Variables
class Layer
{
private:
enLayerList layer = enLayerList::LAYER_DEFAULT;
bool bRenderSwitch = TRUE;
public:
Layer(enLayerList layerState);
~Layer();
//Set layer Function(enLayerList layerState)
//This Functions Catch Only <enum enLayerList>
void setLayerState(enLayerList layerState) { layer = layerState; }
//Get layer Function return enLayerList Type
enLayerList getLayerState() { return layer; }
//Set Switch Function
//if u want to change render on/off
//use true/false flag value
//default value = true
void setSwitch(bool bLayerSwitch) { bRenderSwitch = bLayerSwitch; }
//Get bRenderSwitch value
//default value = true
bool getSwitch() { return bRenderSwitch; }
};
#endif // !_LAYER_H
view raw layer.h hosted with ❤ by GitHub


헤더에서 보면 


Class Layer

{

private:

enLayerList layer = enLayerList::LAYER_DEFAULT;

bool bRenderSwitch = TRUE;

}


로 되어있습니다.


첫 번째 enum class 형 layer는 현재 Layer클래스가 어떤 레이어 타입인지를 나타내고,

두 번째 bRenderSwitch 값은 레이어의 활성화 여부를 나타냅니다.


기본적으로 두 값은 Default와 true 값으로 초기화되어 들어갑니다.

클래스가 만들어 진 시점에서 일단 한번 바로 활성화하기 위해서 그렇습니다.


만약 레이어만 만들고 쓰고 싶지 않다면


클래스 생성 후 바로 set함수를 이용하여 비활성화 시키면 됩니다.


.cpp 파일의 내용 또한 별게 없습니다.


다만 주의할 점은 생성자가 enum class 형 enLayerList layerstate값을 받게 되어있습니다.



여기까지 봐서는 무슨 말인지 잘 이해가 안가실 수 있으나


간단히 예제 상황을 들면,


액션 게임을 할 때 캐릭터가 필살기를 쓰면 배경이 검은색으로 바뀌면서 배경이나 UI가 사라지고 캐릭터에 카메라 포커스가 맞춰지는 경우를 보신 적이 있을 것 입니다.


위 경우와 같이 어떤 레이어 계층의 렌더링 자체를 껏다 켰다 하실 때 사용 할 수 있습니다.


별로 이런거 쓸일이 없다. 그러면 딱히 Layer Class를 활용하지 않아도 상관없습니다.



레이어 클래스 자체는 여기까지만 설명드리면 어느정도 목적과 그 이용 사례를 스스로 생각하실 수 있으리라 예상 됩니다.


그럼 다음 글 LayerManager Class 를 이어서 설명하도록 하겠습니다.

posted by REDFORCE 2017. 4. 20. 05:44

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics

 + Image - SpriteData  (현재 글)

 + Layer

 + Input

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


-----------------------------------------------------------------------


Image Class .cpp (1) 번 글에 이어서 (2)번 글 입니다.


이번 글은 주목할 내용이 Update 함수 뿐이라서 짧습니다.



1. Update


Update 라는 함수 이름으로 알 수 있듯이, 이 함수는 게임이 진행 되는


매 프레임마다 호출되는 함수입니다.


왜 Update 라는 함수가 필요한가? 에 대해서 의문이신 분은 Image class가 궁극적으로

무엇을 하기 위한 클래스 였는지 한번 상기해볼 필요가 있습니다.


이유인즉, Image Class는 spriteData와 같이 단순한 이미지 출력 용이 아닌,

애니메이션을 돌리기 위한 용도이기 때문입니다.


따라서 매 프레임 어떤 애니메이션 동작이 나올지 계속 갱신해줘야 하기 때문에 Update 함수를 갖고 있습니다.


다음 코드가 update 함수 전체 입니다.


//=============================================================================
// update
// typically called once per frame
// frameTime is used to regulate the speed of movement and animation
//=============================================================================
void Image::update(float frameTime)
{
if (endFrame - startFrame > 0) // if animated sprite
{
animTimer += frameTime; // total elapsed time
if (animTimer > frameDelay)
{
animTimer -= frameDelay;
currentFrame++;
if (currentFrame < startFrame || currentFrame > endFrame)
{
if(loop == true) // if looping animation
currentFrame = startFrame;
else // not looping animation
{
currentFrame = endFrame;
animComplete = true; // animation complete
}
}
setRect(); // set spriteData.rect
}
}
}
view raw image.cpp hosted with ❤ by GitHub


update 내용을 보면,


8 Line : if (endFrame - startFrame > 0) 


만약 끝 프레임에서 시작프레임을 뺐는데 값이 0 보다 크다는 말은

프레임이 1개 이상이라는 뜻이니 애니메이션을 업데이트 해야 한다라고 인식합니다.


10 Line : animTimer += frameTime;


위 뜻은 animation을 돌릴 때 설정해둔 딜레이와 계산해서 

딜레이 보다 작다면 애니메이션을 업데이트 하지 않고 크다면 애니메이션을 업데이트 시킵니다.

즉, 딜레이가 길다는 뜻은 애니메이션 동작을 펼치는데 오래걸린다는 뜻 입니다.


예를 들어서 느릿느릿 움직이게 하고 싶은 애니메이션은 딜레이를 길게 주면됩니다.


13 Line : animTimer -= frameDelay;


조건문 결과 true 값으로 안으로 들어왔다면, 타이머에서 딜레이 값 만큼 빼줍니다.

안그러면은 타이머가 계속 올라가기만 해서 딜레이 보다 무조건 크다 라고 판단하고 조건문을 계속 들어오니까요.


14 Line : currentFrame++;


업데이트를 하게 되었으니, 현재 프레임을 상승시켜줍니다.


15 Line : if (currentFrame < startFrame || currentFrame > endFrame)


위 뜻은 현재 프레임이 시작프레임 보다 작거나 현재 프레임이 끝 프레임보다 크다면

아래의 내용을 수행해라 라는 뜻 입니다.


17 ~ 23 Line : if(loop == true){ } else { }


만약 반복 애니메이션 이라면~? 아래 내용을 수행 / 아니면 현재 프레임을 끝 프레임으로 설정하고 애니메이션이 끝났다 라고 알림 (animComplete = true;)


25 Line : setRect( ) 


다음 출력 해야 할 애니메이션의 spriteData.rect 를 셋업.



여기까지가 Image Class의 Update 내용이었습니다.


혹시나 보시고 응? 잘 이해안가시거나 오류가 있다면 알려주시면

바로 해결하도록 노력하겠습니다.

posted by REDFORCE 2017. 4. 19. 23:09

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics

 + Image - SpriteData  (현재 글)

 + Layer

 + Input

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


-----------------------------------------------------------------------


Image Class의 header 파일을 살펴봤던 것에 이어서, 


.cpp 내용을 살펴보도록 하겠습니다.


1. 생성자 / 소멸자 


간단하게 생성자부터 확인하도록 하겠습니다.


#include "stdafx.h"
#include "image.h"
//=============================================================================
// default constructor
//=============================================================================
Image::Image()
{
initialized = false; // set true when successfully initialized
spriteData.width = 2;
spriteData.height = 2;
spriteData.x = 0.0;
spriteData.y = 0.0;
spriteData.scale = 1.0;
spriteData.angle = 0.0;
spriteData.rect.left = 0; // used to select one frame from multi-frame image
spriteData.rect.top = 0;
spriteData.rect.right = spriteData.width;
spriteData.rect.bottom = spriteData.height;
spriteData.texture = NULL; // the sprite texture (picture)
spriteData.flipHorizontal = false;
spriteData.flipVertical = false;
cols = 1;
textureManager = NULL;
startFrame = 0;
endFrame = 0;
currentFrame = 0;
frameDelay = 1.0; // default to 1 second per frame of animation
animTimer = 0.0;
visible = true; // the image is visible
loop = true; // loop frames
animComplete = false;
graphics = NULL; // link to graphics system
colorFilter = graphicsNS::WHITE; // WHITE for no change
layer = NULL;
}
view raw image.cpp hosted with ❤ by GitHub


생성자 내엔 역시나 주요 변수들에 대한 초기화가 이루어집니다.


포인터 형태인 변수는 null 값으로 초기화가 됐습니다만,

최근에는 모던 C++을 공부하면서 nullptr을 사용하는 것이 더 낫다 보여집니다.



소멸자 아무 내용도 없어서 여기에 담진 않았습니다.



2. initialize


두 번째 내용은 의미있는 초기화를 담당하고 있는 initialize 함수 입니다.


try - catch 구문으로 감싸져있으며, 어떤 exception이 발생할 시엔 return false 값을 내뱉고 종료합니다.

//=============================================================================
// Initialize the Image.
// Post: returns true if successful, false if failed
// pointer to Graphics
// width of Image in pixels (0 = use full texture width)
// height of Image in pixels (0 = use full texture height)
// number of columns in texture (1 to n) (0 same as 1)
// pointer to TextureManager
//=============================================================================
bool Image::initialize(Graphics *g, int width, int height, int ncols,
TextureManager *textureM)
{
try{
graphics = g; // the graphics object
textureManager = textureM; // pointer to texture object
spriteData.texture = textureManager->getTexture();
if(width == 0)
width = textureManager->getWidth(); // use full width of texture
spriteData.width = width;
if(height == 0)
height = textureManager->getHeight(); // use full height of texture
spriteData.height = height;
cols = ncols;
if (cols == 0)
cols = 1; // if 0 cols use 1
// configure spriteData.rect to draw currentFrame
spriteData.rect.left = (currentFrame % cols) * spriteData.width;
// right edge + 1
spriteData.rect.right = spriteData.rect.left + spriteData.width;
spriteData.rect.top = (currentFrame / cols) * spriteData.height;
// bottom edge + 1
spriteData.rect.bottom = spriteData.rect.top + spriteData.height;
this->layer = LAYERMANAGER->getLayer(enLayerList::LAYER_DEFAULT);
}
catch(...) {return false;}
initialized = true; // successfully initialized
return true;
}
//=============================================================================
// Initialize the Image.
// Post: returns true if successful, false if failed
// pointer to Graphics
// width of Image in pixels (0 = use full texture width)
// height of Image in pixels (0 = use full texture height)
// number of columns in texture (1 to n) (0 same as 1)
// pointer to TextureManager
// pointer to this->layer
//=============================================================================
bool Image::initialize(Graphics * g, int width, int height, int ncols, TextureManager * textureM, Layer * pLayer)
{
try {
graphics = g; // the graphics object
textureManager = textureM; // pointer to texture object
spriteData.texture = textureManager->getTexture();
if (width == 0)
width = textureManager->getWidth(); // use full width of texture
spriteData.width = width;
if (height == 0)
height = textureManager->getHeight(); // use full height of texture
spriteData.height = height;
cols = ncols;
if (cols == 0)
cols = 1; // if 0 cols use 1
// configure spriteData.rect to draw currentFrame
spriteData.rect.left = (currentFrame % cols) * spriteData.width;
// right edge + 1
spriteData.rect.right = spriteData.rect.left + spriteData.width;
spriteData.rect.top = (currentFrame / cols) * spriteData.height;
// bottom edge + 1
spriteData.rect.bottom = spriteData.rect.top + spriteData.height;
this->layer = pLayer;
}
catch (...) { return false; }
initialized = true; // successfully initialized
return true;
}
view raw image.cpp hosted with ❤ by GitHub


try 안에서는 매게변수로 받아온 


 (1) Graphics*

 (2) width / height

 (3) cols

 (4) textureManager*


값을 그대로 대입합니다.


아마 이글에서 textureManager (?) 하고 의문이 들수 있습니다.


지금까지 언급했던 내용중에는 textureManager라는 항목이 없었거든요.

많이 궁금하시겠지만 일단은 그냥 그런가보다 하고 넘어가시기 바랍니다.


여기서 꼭 숙지할 점은 받아온 각 파라미터의 값들이 spriteData 안에 대입이 되어진다는 것을 확인하시기 바랍니다.


그리고 Layer 라고 하는 클래스도 갑자기 등장했습니다만, 

추후 Layer 파트에 대한 메뉴얼이 있으므로 그곳에서 설명드리도록 하겠습니다.



3. Draw Functions


세 번째 Draw 함수들 입니다.


Graphics에서 보신것과 같이 기본적인 Image를 Draw 하는 함수와

Image의 Rect가 어느정도 크기인지를 볼 수 있게 도와주는 drawRect 함수입니다.


(drawNine 함수는 현재 제대로 작동하지 않아서 언급하지 않습니다. 추후 버그수정 후 다시 올리도록 하겠습니다)


//=============================================================================
// Draw the image using color as filter
// The color parameter is optional, WHITE is assigned as default in image.h
// The textureN parameter is optional, 0 is default.
// Pre : spriteBegin() is called
// Post: spriteEnd() is called
//=============================================================================
void Image::draw(COLOR_ARGB color, UINT textureN)
{
/* LayerManager Logic Rendering Function ===== Need Fixing */
//if (this->layer->getLayerState() < LAYERMANAGER->getCurrentLayerState())
// return;
if (this->layer->getSwitch() == FALSE)
return;
if (!visible || graphics == NULL)
return;
// set texture to draw
spriteData.texture = textureManager->getTexture(textureN);
if(color == graphicsNS::FILTER) // if draw with filter
graphics->drawSprite(spriteData, colorFilter); // use colorFilter
else
graphics->drawSprite(spriteData, color); // use color as filter
}
//=============================================================================
// Draw this image using the specified SpriteData.
// The current SpriteData.rect is used to select the texture.
// Pre : spriteBegin() is called
// Post: spriteEnd() is called
//=============================================================================
void Image::draw(SpriteData sd, COLOR_ARGB color, UINT textureN)
{
/* LayerManager Logic Rendering Function ===== Need Fixing */
//if (this->layer->getLayerState() < LAYERMANAGER->getCurrentLayerState())
// return;
if (this->layer->getSwitch() == FALSE)
return;
if (!visible || graphics == NULL)
return;
sd.rect = spriteData.rect; // use this Images rect to select texture
sd.texture = textureManager->getTexture(textureN); // set texture to draw
if(color == graphicsNS::FILTER) // if draw with filter
graphics->drawSprite(sd, colorFilter); // use colorFilter
else
graphics->drawSprite(sd, color); // use color as filter
}
void Image::drawNine(SpriteData sd, COLOR_ARGB color, UINT textureN)
{
if (this->layer->getSwitch() == FALSE)
return;
if (!visible || graphics == NULL)
return;
sd.rect = spriteData.rect; // use this Images rect to select texture
sd.texture = textureManager->getTexture(textureN); // set texture to draw
if (color == graphicsNS::FILTER) // if draw with filter
graphics->drawSprite(sd, colorFilter); // use colorFilter
else
graphics->drawSprite(sd, color); // use color as filter
}
//=============================================================================
// Draw Sprite x,y,width,height : RECT
// For Testing Function
// This is Not!! SpriteData.rect
//=============================================================================
void Image::drawRect(COLOR_ARGB color)
{
// LEFT TOP to RIGHT TOP
graphics->drawLine(spriteData.x, spriteData.y,
spriteData.x + spriteData.width, spriteData.y, 1.0f, color);
// LEFT TOP to LEFT BOTTOM
graphics->drawLine(spriteData.x, spriteData.y,
spriteData.x, spriteData.y + spriteData.height, 1.0f, color);
// RIGHT TOP to RIGHT BOTTOM
graphics->drawLine(spriteData.x + spriteData.width, spriteData.y,
spriteData.x + spriteData.width, spriteData.y + spriteData.height, 1.0f, color);
// LEFT BOTTOM to RIGHT BOTTOM
graphics->drawLine(spriteData.x, spriteData.y + spriteData.height,
spriteData.x + spriteData.width, spriteData.y + spriteData.height, 1.0f, color);
}
view raw image.cpp hosted with ❤ by GitHub


draw 함수들에 대해서는 의미적으로 이미 바로 알 수 있기 때문에


어렵게 설명할 내용은 없습니다.


현재 Image 클래스에서 갖고있는 spriteData 구조체에 들어가있는 Texture와 각종 값들을 토대로 Texture 이미지를 그대로 graphics 클래스에게 부탁하여 drawSprite 함수를 호출합니다.



나머지 함수부터는 다음 글로 이어서 적도록 하겠습니다.

'MyProject > SephyEngine' 카테고리의 다른 글

June_Engine #3_Layer Class  (0) 2017.04.21
June_Engine #3_Image Class .cpp (2)  (0) 2017.04.20
June_Engine #3_Image (Class) & SpriteData (Struct)  (0) 2017.04.19
June_Engine #2_Graphics.cpp (3)  (0) 2017.04.18
June_Engine #2_Graphics.cpp (2)  (0) 2017.04.17
posted by REDFORCE 2017. 4. 19. 04:38

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics

 + Image - SpriteData  (현재 글)

 + Layer

 + Input

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


-----------------------------------------------------------------------


이번 글에서는 Image 클래스와 SpriteData 구조체에 대해서 자세히 살펴보도록 하겠습니다.


Image Class 자체는 Graphics 클래스에 비하면


헤더 내용 자체는 양반입니다만 의미 자체는 역시나 Graphics 클래스 못지않게

인게임에서 렌더링을 편하게 할 수 있게 도와주는 클래스 이므로 매우 중요합니다.



그럼 먼저 성격 급하신 분들을 위해 대체 Image Class가 뭐하는 것인지부터 

간략히 설명하도록 하겠습니다.



앞서 우리는 SpriteData 라는 구조체를 봤습니다.

SpriteData 구조체 만으로도 상당히 편하게 In game에서 사용할 이미지를 대충 뿌릴 수 있습니다만...


Image Class는 SpriteData 구조체보다 한 단계 위의 작업을 하기 위해 존재합니다.

결과만 먼저 한 줄로 이야기 해드리자면, 바로 애니메이션! 을 돌리기 위함 입니다.


1. 먼저 헤더 파일의 일부를 살펴보겠습니다.


#ifndef _IMAGE_H // Prevent multiple definitions if this
#define _IMAGE_H // file is included in more than one place
#define WIN32_LEAN_AND_MEAN
class Image;
#include "constants.h"
#include "textureManager.h"
#include "layer.h"
class Image
{
// Image properties
protected:
Graphics* graphics; // pointer to graphics
TextureManager* textureManager; // pointer to texture manager
// spriteData contains the data required to draw the image by Graphics::drawSprite()
SpriteData spriteData; // SpriteData is defined in "graphics.h"
COLOR_ARGB colorFilter; // applied as a color filter (use WHITE for no change)
int cols; // number of cols (1 to n) in multi-frame sprite
int startFrame; // first frame of current animation
int endFrame; // end frame of current animation
int currentFrame; // current frame of animation
float frameDelay; // how long between frames of animation
float animTimer; // animation timer
HRESULT hr; // standard return type
bool loop; // true to loop frames
bool visible; // true when visible
bool initialized; // true when successfully initialized
bool animComplete; // true when loop is false and endFrame has finished displaying
Layer* layer;
}
view raw image.h hosted with ❤ by GitHub



위와 같이 Protected 접근한정자 영역 안에

이미 SpriteData 구조체가 들어가 있음을 볼 수 있습니다.


즉 Image Class 자체에 텍스쳐 및 이미지 정보를 가지고 있는 SpriteData를 내포하고 있습니다.



위에서 아래로 차례대로 변수들의 목적에 대해서 설명해드리겠습니다.


 (1) *graphics : 이미지가 사용 될 Graphics 클래스 포인터

 (2) *textureManager : SpriteData에 입력 될 Texture 정보

 (3) spriteData : SpriteData 구조체

 (4) colorFilter : 투명화 시킬 컬러 필터 값 ( 아무것도 안주면 WHITE 컬러 = No change)

 (5) cols : 텍스쳐에서 애니메이션화 시킬 프레임의 열 갯수

 (6) startFrame : 시작 프레임

 (7) endFrame : 끝 프레임

 (8) currentFrame : 현재 출력중인 프레임

 (9) frameDelay : 애니메이션 딜레이

 (10) animTimer : 애니메이션 타이머

 (11) hr : 초기화 시 리턴 값

 (12) loop : 반복 애니메이션 여부

 (13) visible : 출력 여부

 (14) initialized : 초기화 성공 여부

 (15) animComplete : 애니메이션 끝났는지 여부

 (16) *layer : Layer 클래스 포인터


이렇게 총 16개의 변수가 있어서 상당히 뭐가 많아 보이긴 합니다만.


그저 Image 클래스를 자주 쓰다보면 저절로 익혀지는 내용들이라 

지금 당장 숨가쁘게 이해하실 필요는 없습니다.



2. 다음, Image Class의 각 함수들 입니다.


코드를 보신다면 아마 헐... 뭐가 이렇게 함수가 많아 라고 하실 분들도 계실텐데


주석의 량 때문에 함수 구간이 길어지다보니 많아보일 뿐

사실 함수 갯수자체로는 각 변수별 필요한 내용들에 대한 Setter/Getter 밖에없습니다.


따라서 함수에 대한 설명은 Setter/Getter 함수들을 제외한


특별한 함수들에 대해서만 설명하겠습니다.


헤더에서 일단 이런게 있구나 살펴볼 함수들은 


 (1) initialize

 (2) draw

 (3) drawLine

 (4) drawRect

 (5) update


이상 5가지 입니다.


(함수에 대한 설명글은 .cpp 에서 잇도록 하겠습니다)


class Image
{
public:
// Constructor
Image();
// Destructor
virtual ~Image();
////////////////////////////////////////
// Get functions //
////////////////////////////////////////
// Return visible parameter.
virtual bool getVisible() {return visible;}
// Return X position.
virtual float getX() {return spriteData.x;}
// Return Y position.
virtual float getY() {return spriteData.y;}
// Return scale factor.
virtual float getScale() {return spriteData.scale;}
// Return width.
virtual int getWidth() {return spriteData.width;}
// Return height.
virtual int getHeight() {return spriteData.height;}
// Return center X.
virtual float getCenterX() {return spriteData.x + spriteData.width/2*getScale();}
// Return center Y.
virtual float getCenterY() {return spriteData.y + spriteData.height/2*getScale();}
// Return rotation angle in degrees.
virtual float getDegrees() {return spriteData.angle*(180.0f/(float)PI);}
// Return rotation angle in radians.
virtual float getRadians() {return spriteData.angle;}
// Return delay between frames of animation.
virtual float getFrameDelay() {return frameDelay;}
// Return number of starting frame.
virtual int getStartFrame() {return startFrame;}
// Return number of ending frame.
virtual int getEndFrame() {return endFrame;}
// Return number of current frame.
virtual int getCurrentFrame() {return currentFrame;}
// Return reference to SpriteData structure.
const virtual SpriteData& getSpriteInfo() {return spriteData;} // for backward compatibility
const virtual SpriteData& getSpriteData() {return spriteData;}
// Return RECT structure of Image.
virtual RECT getSpriteDataRect() {return spriteData.rect;}
// Return state of animation complete.
virtual bool getAnimationComplete() {return animComplete;}
// Return colorFilter.
virtual COLOR_ARGB getColorFilter() {return colorFilter;}
////////////////////////////////////////
// Set functions //
////////////////////////////////////////
// Set X location.
virtual void setX(float newX) {spriteData.x = newX;}
// Set Y location.
virtual void setY(float newY) {spriteData.y = newY;}
// Set scale.
virtual void setScale(float s) {spriteData.scale = s;}
// Set width.
virtual void setWidth(int w) {spriteData.width = w;}
// Set height.
virtual void setHeight(int h) {spriteData.height = h;}
// Set rotation angle in degrees.
// 0 degrees is up. Angles progress clockwise.
virtual void setDegrees(float deg) {spriteData.angle = deg*((float)PI/180.0f);}
// Set rotation angle in radians.
// 0 radians is up. Angles progress clockwise.
virtual void setRadians(float rad) {spriteData.angle = rad;}
// Set visible.
virtual void setVisible(bool v) {visible = v;}
// Set delay between frames of animation.
virtual void setFrameDelay(float d) {frameDelay = d;}
// Set starting and ending frames of animation.
virtual void setFrames(int s, int e){startFrame = s; endFrame = e;}
// Set current frame of animation.
virtual void setCurrentFrame(int c);
// Set spriteData.rect to draw currentFrame
virtual void setRect();
// Set spriteData.rect to r.
virtual void setSpriteDataRect(RECT r) {spriteData.rect = r;}
// Set animation loop. lp = true to loop.
virtual void setLoop(bool lp) {loop = lp;}
// Set animation complete Boolean.
virtual void setAnimationComplete(bool a) {animComplete = a;};
// Set color filter. (use WHITE for no change)
virtual void setColorFilter(COLOR_ARGB color) {colorFilter = color;}
// Set TextureManager
virtual void setTextureManager(TextureManager *textureM)
{ textureManager = textureM; }
// Set Animation Timer
virtual void setAnimTimer(float t) {animTimer = t;};
////////////////////////////////////////
// Other functions //
////////////////////////////////////////
// Initialize Image
// Pre: *g = pointer to Graphics object
// width = width of Image in pixels (0 = use full texture width)
// height = height of Image in pixels (0 = use full texture height)
// ncols = number of columns in texture (1 to n) (0 same as 1)
// *textureM = pointer to TextureManager object
virtual bool Image::initialize(Graphics *g, int width, int height,
int ncols, TextureManager *textureM);
// Initialize Image
// Pre: *g = pointer to Graphics object
// width = width of Image in pixels (0 = use full texture width)
// height = height of Image in pixels (0 = use full texture height)
// ncols = number of columns in texture (1 to n) (0 same as 1)
// *textureM = pointer to TextureManager object
// *pLayer = point to This->layer
virtual bool Image::initialize(Graphics *g, int width, int height,
int ncols, TextureManager *textureM, Layer *pLayer);
// Flip image horizontally (mirror)
virtual void flipHorizontal(bool flip) {spriteData.flipHorizontal = flip;}
// Flip image vertically
virtual void flipVertical(bool flip) {spriteData.flipVertical = flip;}
// Draw Image using color as filter. Default color is WHITE.
// textureN is number of texture in textureManager
virtual void draw(COLOR_ARGB color, UINT textureN);
// Draw Image using color as filter. Default color is WHITE.
virtual void draw(COLOR_ARGB color = graphicsNS::WHITE) { draw(color, 0); }
// Draw Image using default color filter.
// textureN is number of texture in textureManager
virtual void draw(UINT textureN) { draw(graphicsNS::WHITE, textureN); }
// Draw this image using the specified SpriteData.
// The current SpriteData.rect is used to select the texture.
// textureN is number of texture in textureManager
virtual void draw(SpriteData sd, COLOR_ARGB color = graphicsNS::WHITE, UINT textureN=0);
// Draw this image using the sepcified SpriteData.
// The Current SpriteData.rect called 9 times
virtual void drawNine(SpriteData sd, COLOR_ARGB color = graphicsNS::WHITE, UINT textureN = 0);
// For Testing Draw Rect Function
virtual void drawRect(COLOR_ARGB color = graphicsNS::RED );
// Update the animation. frameTime is used to regulate the speed.
virtual void update(float frameTime);
Layer* getLayer() { return layer; }
void setLayer(Layer* layer) { this->layer = layer; }
};
view raw image.h hosted with ❤ by GitHub

'MyProject > SephyEngine' 카테고리의 다른 글

June_Engine #3_Image Class .cpp (2)  (0) 2017.04.20
June_Engine #3_Image Class .cpp (1)  (0) 2017.04.19
June_Engine #2_Graphics.cpp (3)  (0) 2017.04.18
June_Engine #2_Graphics.cpp (2)  (0) 2017.04.17
June_Engine #2_Graphics.cpp (1)  (0) 2017.04.15
posted by REDFORCE 2017. 4. 18. 23:18

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics  (현재 글)

 + Image - SpriteData

 + Layer

 + Input

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


-----------------------------------------------------------------------


이번 글에서는 앞서 다룬 DrawSprite 함수에 이어서 적도록 하겠습니다.



1. changeDisplayMode (화면 전환 함수 [창모드/전체화면모드] )


이어서 다룰 첫 번째 함수는 changeDisplayMode 입니다.


이름만봐도 알 수 있듯이 위 함수는 전체화면이나 창모드 화면으로 변경할 시에 사용 되는 함수입니다.


사용빈도 자체는 높진 않지만 일반적인 게임이라면 필수적으로 지원되야하는


기능이라 할 수 있습니다.


우리는 일상에서 많은 게임들이 Alt+Enter 키를 누르면 전체화면이나 창모드로 바뀌는 그런 경우를 볼 수 있었을 것 입니다.


단축키 자체는 별도로 작성해서 이 함수를 호출하도록 하게끔만 만들면 되므로

키 입력에 대한 내용은 추후 Input Class 에서 다루도록 하고 여기서는


어떻게 화면이 변환되는지에 대해서만 언급하겠습니다.


먼저 코드를 살펴보겠습니다.


//=============================================================================
// Toggle window or fullscreen mode
// Pre: All user created D3DPOOL_DEFAULT surfaces are freed.
// Post: All user surfaces are recreated.
//=============================================================================
void Graphics::changeDisplayMode(graphicsNS::DISPLAY_MODE mode)
{
try{
switch(mode)
{
case graphicsNS::FULLSCREEN:
if(fullscreen) // if already in fullscreen mode
return;
fullscreen = true; break;
case graphicsNS::WINDOW:
if(fullscreen == false) // if already in window mode
return;
fullscreen = false; break;
default: // default to toggle window/fullscreen
fullscreen = !fullscreen;
}
reset();
if(fullscreen) // fullscreen
{
SetWindowLong(hwnd, GWL_STYLE, WS_EX_TOPMOST | WS_VISIBLE | WS_POPUP);
}
else // windowed
{
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, HWND_TOP, 0,0,GAME_WIDTH,GAME_HEIGHT,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// Adjust window size so client area is GAME_WIDTH x GAME_HEIGHT
RECT clientRect;
GetClientRect(hwnd, &clientRect); // get size of client area of window
MoveWindow(hwnd,
0, // Left
0, // Top
GAME_WIDTH+(GAME_WIDTH-clientRect.right), // Right
GAME_HEIGHT+(GAME_HEIGHT-clientRect.bottom), // Bottom
TRUE); // Repaint the window
}
} catch(...)
{
// An error occured, try windowed mode
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, HWND_TOP, 0,0,GAME_WIDTH,GAME_HEIGHT,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// Adjust window size so client area is GAME_WIDTH x GAME_HEIGHT
RECT clientRect;
GetClientRect(hwnd, &clientRect); // get size of client area of window
MoveWindow(hwnd,
0, // Left
0, // Top
GAME_WIDTH+(GAME_WIDTH-clientRect.right), // Right
GAME_HEIGHT+(GAME_HEIGHT-clientRect.bottom), // Bottom
TRUE); // Repaint the window
}
}



함수 내용 전체는 try - catch 문으로 감싸져 있습니다.


이유는 미리 우리가 정의해놓은 어떤 디스플레이 모드 상에서 바뀌는 거라면

상관이 없지만 어떤 에러로 인해 제대로 동작이 안될 경우를 대비하여 


안전하게 화면을 바꾸기 위함입니다.



천천히 try 문 부터 살펴보도록 하겠습니다.



switch 문으로 mode 플래그 값을 통해 구분이 되어있는데


동작 원리는 밖에 선언해두었던 fullscreen (bool) 값을 이용하여 단순히 true / false 를 교체해주는 행위를 합니다.


그리고 switch 문에서 나오면 


reset() 함수를 호출하네요.


reset 함수의 내용은 아래와 같습니다.


//=============================================================================
// Reset the graphics device
//=============================================================================
HRESULT Graphics::reset()
{
safeRelease(line);
safeRelease(pOcclusionQuery); // release query
initD3Dpp(); // init D3D presentation parameters
sprite->OnLostDevice(); // release sprite
result = device3d->Reset(&d3dpp); // attempt to reset graphics device
// Configure for alpha blend of primitives
device3d->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
device3d->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device3d->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// recreate query
device3d->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);
sprite->OnResetDevice(); // recreate sprite
createLine(&line); // recreate a DirectX line
return result;
}
view raw reset.cpp hosted with ❤ by GitHub


그리고 fullscreen 값을 체크하여 true 라면

현재 모니터의 전체 화면으로 메인 윈도우를 늘려주고,


아니라면 현재 게임의 width / height 값으로 창화면을 만들어줍니다.


catch 구문으로 갔을 경우의 하는 역할도 마찬가지 입니다.



2. pixelCollision (픽셀 충돌 함수)


두 번째로 다룰 함수는 픽셀 충돌 함수 입니다.


pixelCollision 함수는 저도 꽤 어려운 구역이라 어떻게 설명을 드려야 할지 저도 잘 모르겠습니다. 그래도 아에 스킵하고 넘어갈순 없으므로 어떤 식으로 픽셀 충돌을 검출하는지 확인해보겠습니다.


다음이 픽셀 충돌 함수의 내용입니다.


//=============================================================================
// Return the number of pixels colliding between the two sprites.
// Pre: The device supports a stencil buffer and pOcclusionQuery points to
// a valid occlusionQuery object.
// Post: Returns the number of pixels of overlap
// This function waits for the graphics card to render the last frame and return
// the collision query pixel count. To avoid slowing down your game, use a
// simple collison test first to eliminate entities that are not colliding.
// Using pixel perfect collision test with multiple entities would require
// creating multiple occlusionQuery objects and clearing the stencil buffer
// between each test.
//=============================================================================
DWORD Graphics::pixelCollision(const SpriteData &sprite1, const SpriteData &sprite2)
{
if(!stencilSupport) // if no stencil buffer support
return 0;
beginScene();
// Set up stencil buffer using current entity
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
device3d->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
device3d->SetRenderState(D3DRS_STENCILREF, 0x1);
device3d->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
device3d->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);
device3d->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
device3d->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
// Write a 1 into the stencil buffer for each non-transparent pixel in ent
spriteBegin();
// Enable stencil buffer (must be after spriteBegin)
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
drawSprite(sprite2); // write 1s to stencil buffer
spriteEnd();
// Change stencil buffer to only allow writes where the stencil value is 1
// (where the ent sprite is colliding with the current sprite)
device3d->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
device3d->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
// Begin occlusion query to count pixels that are drawn
pOcclusionQuery->Issue(D3DISSUE_BEGIN);
spriteBegin();
// Enable stencil buffer (must be after spriteBegin)
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
drawSprite(sprite1); // draw current entity
spriteEnd();
// End occlusion query
pOcclusionQuery->Issue(D3DISSUE_END);
// Wait until the GPU is finished.
while(S_FALSE == pOcclusionQuery->GetData( &numberOfPixelsColliding,
sizeof(DWORD), D3DGETDATA_FLUSH ))
{}
// Turn off stencil
device3d->SetRenderState(D3DRS_STENCILENABLE, false);
endScene();
return numberOfPixelsColliding;
}


픽셀 충돌은 먼저 두 개의 spriteData 구조체 파라미터를 받습니다.

그리고 다소 어려운 내용일 수도 있지만 깊이 버퍼 값을 이용하여 픽셀 충돌을 검사합니다.


코드를 보시면, 처음 stencilSupport 여부를 체크하는데요.

이것은 그래픽카드에서 깊이 버퍼를 이용한 기술적 지원을 해주는지 여부를 확인합니다.

(위 stencilSupport 값은 Initialize( ) 에서 true / false 여부가 판단되어 있습니다)


다음, 깊이 버퍼 기술을 이용할 수 있다면


다양한 RenderState를 설정하는 것을 볼 수 있습니다.

이 값들이 의미하는 바에 대해서는 여기서 일일이 설명드리기에 너무 많으므로


MSDN이나 구글링 하시는 것을 통해 알아가시기를 양해드립니다.



첫 번째, 


중요한 요점은 D3DRS_STENCILE 이라 하는 RenderState 값들을 이용한다는 점을 숙지하는 것 입니다.



두 번째,


설정한 RenderState 값들 중에 몇가지는 spriteBegin() 이 호출된 이후에 설정되어야 하는 값들이 있습니다.



세 번째,


pOcclusionQuery (IDirect3DQuery9*)를 이용하여 두 스프라이트의 픽셀 갯수만큼 while 문을 돌고 픽셀 충돌 여부를 검출합니다.


그리고 반환 된 충돌 된 픽셀 갯수가 numberOfPixelsColliding 에 담겨서 리턴되게 됩니다.



만약 이 함수를 이용하여 픽셀 충돌을 검사한다면 numberOfPixelsColliding 값이 0보다 큰지 아닌지만 검사한다면 픽셀 충돌이 이뤄졌는가 아닌가를 알 수 있습니다.



여기까지가 주요 Graphics Class에 대한 내용들이었습니다.


의외로 세 번에 걸쳐서 글을 쓰게 될줄은 몰랐습니다.



처음에는 한번에 다 적을 수 있겠지~~ 했는데, 

코드를 포함하여 적다보니 량이 적지않구나 하는걸 느낀 후로 조금 나눠서 글을 적게 되었습니다.


만약 여기까지 읽어주셨다면 정말 감사드리고, 궁금하신 점 있으시면 답글이나 이메일

redforce01@naver.com 으로 주시면 제가 아는 한에서 꼭 답변해드리겠습니다.




추신. getDeviceState() 함수가 여기서는 다뤄지지 않은 것 같습니다만,

추후에 reset() 함수를 다시 활용할 때 한번 더 정리 하여 올릴 예정입니다.



아래는 Graphics 클래스의 .h 파일과 .cpp 파일 전체 입니다.


헤더 코드입니다.

#ifndef _GRAPHICS_H // Prevent multiple definitions if this
#define _GRAPHICS_H // file is included in more than one place
#define WIN32_LEAN_AND_MEAN
#ifdef _DEBUG
#define D3D_DEBUG_INFO
#endif
class Graphics;
#include <d3d9.h>
#include <d3dx9.h>
#include <D3dx9core.h>
#include "constants.h"
#include "gameError.h"
// DirectX pointer types
#define LP_TEXTURE LPDIRECT3DTEXTURE9
#define LP_SPRITE LPD3DXSPRITE
#define LP_3DDEVICE LPDIRECT3DDEVICE9
#define LP_3D LPDIRECT3D9
#define VECTOR2 D3DXVECTOR2
#define LP_VERTEXBUFFER LPDIRECT3DVERTEXBUFFER9
#define LP_DXFONT LPD3DXFONT
#define LP_VERTEXBUFFER LPDIRECT3DVERTEXBUFFER9
#define LP_DXLINE LPD3DXLINE
// Color defines
// ARGB numbers range from 0 through 255
// a = Alpha channel (transparency where 255 is opaque)
// r = Red, g = Green, b = Blue
#define COLOR_ARGB DWORD
#define SETCOLOR_ARGB(a,r,g,b) \
((COLOR_ARGB)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
namespace graphicsNS
{
// Some common colors
// ARGB numbers range from 0 through 255
// A = Alpha channel (transparency where 255 is opaque)
// R = Red, G = Green, B = Blue
const COLOR_ARGB ORANGE = D3DCOLOR_ARGB(255,255,165, 0);
const COLOR_ARGB BROWN = D3DCOLOR_ARGB(255,139, 69, 19);
const COLOR_ARGB LTGRAY = D3DCOLOR_ARGB(255,192,192,192);
const COLOR_ARGB GRAY = D3DCOLOR_ARGB(255,128,128,128);
const COLOR_ARGB OLIVE = D3DCOLOR_ARGB(255,128,128, 0);
const COLOR_ARGB PURPLE = D3DCOLOR_ARGB(255,128, 0,128);
const COLOR_ARGB MAROON = D3DCOLOR_ARGB(255,128, 0, 0);
const COLOR_ARGB TEAL = D3DCOLOR_ARGB(255, 0,128,128);
const COLOR_ARGB GREEN = D3DCOLOR_ARGB(255, 0,128, 0);
const COLOR_ARGB NAVY = D3DCOLOR_ARGB(255, 0, 0,128);
const COLOR_ARGB WHITE = D3DCOLOR_ARGB(255,255,255,255);
const COLOR_ARGB YELLOW = D3DCOLOR_ARGB(255,255,255, 0);
const COLOR_ARGB MAGENTA = D3DCOLOR_ARGB(255,255, 0,255);
const COLOR_ARGB RED = D3DCOLOR_ARGB(255,255, 0, 0);
const COLOR_ARGB CYAN = D3DCOLOR_ARGB(255, 0,255,255);
const COLOR_ARGB LIME = D3DCOLOR_ARGB(255, 0,255, 0);
const COLOR_ARGB BLUE = D3DCOLOR_ARGB(255, 0, 0,255);
const COLOR_ARGB BLACK = D3DCOLOR_ARGB(255, 0, 0, 0);
const COLOR_ARGB FILTER = D3DCOLOR_ARGB(255,255,255,255); // use to specify drawing with colorFilter
const COLOR_ARGB ALPHA25 = D3DCOLOR_ARGB( 64,255,255,255); // AND with color to get 25% alpha
const COLOR_ARGB ALPHA50 = D3DCOLOR_ARGB(128,255,255,255); // AND with color to get 50% alpha
const COLOR_ARGB BACK_COLOR = NAVY; // background color of game
const COLOR_ARGB TRANSCOLOR = BLACK; // transparent color
enum DISPLAY_MODE{TOGGLE, FULLSCREEN, WINDOW};
}
struct VertexC // Vertex with Color
{
float x, y, z; // vertex location
float rhw; // reciprocal homogeneous W (set to 1)
unsigned long color; // vertex color
};
// Flexible Vertex Format Codes
// D3DFVF_XYZRHW = The verticies are transformed
// D3DFVF_DIFFUSE = The verticies contain diffuse color data
#define D3DFVF_VERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)
// SpriteData: The properties required by Graphics::drawSprite to draw a sprite
struct SpriteData
{
int width; // width of sprite in pixels
int height; // height of sprite in pixels
float x; // screen location (top left corner of sprite)
float y;
float scale; // <1 smaller, >1 bigger
float angle; // rotation angle in radians
RECT rect; // used to select an image from a larger texture
LP_TEXTURE texture; // pointer to texture
bool flipHorizontal; // true to flip sprite horizontally (mirror)
bool flipVertical; // true to flip sprite vertically
};
class Graphics
{
private:
// DirectX pointers and stuff
LP_3D direct3d;
LP_3DDEVICE device3d;
LP_SPRITE sprite;
LP_DXLINE line; // line pointer
D3DPRESENT_PARAMETERS d3dpp;
D3DDISPLAYMODE pMode;
IDirect3DQuery9* pOcclusionQuery; // for pixel perfect collision detection
DWORD numberOfPixelsColliding; // for pixel perfect collision detection
// other variables
HRESULT result; // standard Windows return codes
HWND hwnd;
bool fullscreen;
bool stencilSupport; // true if device supports stencil buffer
int width;
int height;
COLOR_ARGB backColor; // background color
// (For internal engine use only. No user serviceable parts inside.)
// Initialize D3D presentation parameters
void initD3Dpp();
public:
// Constructor
Graphics();
// Destructor
virtual ~Graphics();
// Releases direct3d and device3d.
void releaseAll();
// Initialize DirectX graphics
// Throws GameError on error
// Pre: hw = handle to window
// width = width in pixels
// height = height in pixels
// fullscreen = true for full screen, false for window
void initialize(HWND hw, int width, int height, bool fullscreen);
// Create a vertex buffer.
// Pre: verts[] contains vertex data.
// size = size of verts[]
// Post: &vertexBuffer points to buffer if successful.
HRESULT createVertexBuffer(VertexC verts[], UINT size, LP_VERTEXBUFFER &vertexBuffer);
// Display a quad (rectangle) with alpha transparency.
// Pre: createVertexBuffer was used to create vertexBuffer containing four
// vertices defining the quad in clockwise order.
// g3ddev->BeginScene was called
bool drawQuad(LP_VERTEXBUFFER vertexBuffer);
// Load the texture into default D3D memory (normal texture use)
// For internal engine use only. Use the TextureManager class to load game textures.
// Pre: filename = name of texture file.
// transcolor = transparent color
// Post: width and height = size of texture
// texture points to texture
HRESULT loadTexture(const char * filename, COLOR_ARGB transcolor, UINT &width, UINT &height, LP_TEXTURE &texture);
// Load the texture into system memory (system memory is lockable)
// Provides direct access to pixel data. Use the TextureManager class to load textures for display.
// Pre: filename = name of texture file.
// transcolor = transparent color
// Post: width and height = size of texture
// texture points to texture
HRESULT loadTextureSystemMem(const char *filename, COLOR_ARGB transcolor, UINT &width, UINT &height, LP_TEXTURE &texture);
// Display the offscreen backbuffer to the screen.
HRESULT showBackbuffer();
// Checks the adapter to see if it is compatible with the BackBuffer height,
// width and refresh rate specified in d3dpp. Fills in the pMode structure with
// the format of the compatible mode, if found.
// Pre: d3dpp is initialized.
// Post: Returns true if compatible mode found and pMode structure is filled.
// Returns false if no compatible mode found.
bool isAdapterCompatible();
// Draw the sprite described in SpriteData structure.
// color is optional, it is applied as a filter, WHITE is default (no change).
// Creates a sprite Begin/End pair.
// Pre: spriteData.rect defines the portion of spriteData.texture to draw
// spriteData.rect.right must be right edge + 1
// spriteData.rect.bottom must be bottom edge + 1
void drawSprite(const SpriteData &spriteData, // sprite to draw
COLOR_ARGB color = graphicsNS::WHITE); // default to white color filter (no change)
// Reset the graphics device.
HRESULT reset();
// Toggle, fullscreen or window display mode
// Pre: All user created D3DPOOL_DEFAULT surfaces are freed.
// Post: All user surfaces are recreated.
void changeDisplayMode(graphicsNS::DISPLAY_MODE mode = graphicsNS::TOGGLE);
// Return length of vector v.
static float Vector2Length(const VECTOR2 *v) {return D3DXVec2Length(v);}
// Return Dot product of vectors v1 and v2.
static float Vector2Dot(const VECTOR2 *v1, const VECTOR2 *v2) {return D3DXVec2Dot(v1, v2);}
// Normalize vector v.
static void Vector2Normalize(VECTOR2 *v) {D3DXVec2Normalize(v, v);}
// Transform vector v with matrix m.
static VECTOR2* Vector2Transform(VECTOR2 *v, D3DXMATRIX *m) {return D3DXVec2TransformCoord(v,v,m);}
// Return the number of pixels colliding between the two sprites.
// Pre: The device supports a stencil buffer and pOcclusionQuery points to
// a valid occlusionQuery object.
// Post: Returns the number of pixels of overlap
// This function waits for the graphics card to render the last frame and return
// the collision query pixel count. To avoid slowing down your game, use a
// simple collison test first to eliminate entities that are not colliding.
DWORD pixelCollision(const SpriteData &sprite1, const SpriteData &sprite2);
// get functions
// Return direct3d.
LP_3D get3D() { return direct3d; }
// Return device3d.
LP_3DDEVICE get3Ddevice() { return device3d; }
// Return sprite
LP_SPRITE getSprite() { return sprite; }
// Return handle to device context (window).
HDC getDC() { return GetDC(hwnd); }
// Test for lost device
HRESULT getDeviceState();
// Return fullscreen
bool getFullscreen() { return fullscreen; }
// Return pOcclusionQuery
IDirect3DQuery9* getPOcclusionQuery() { return pOcclusionQuery; }
// Returns true if the graphics card supports a stencil buffer
bool getStencilSupport() { return stencilSupport; }
// Set color used to clear screen
void setBackColor(COLOR_ARGB c) {backColor = c;}
//=============================================================================
// Create DX line, description from D3dx9core.h
// This object intends to provide an easy way to draw lines using D3D.
//
// Begin -
// Prepares device for drawing lines
// Draw -
// Draws a line strip in screen-space.
// Input is in the form of a array defining points on the line strip. of D3DXVECTOR2
// DrawTransform -
// Draws a line in screen-space with a specified input transformation matrix.
// End -
// Restores device state to how it was when Begin was called.
// SetPattern -
// Applies a stipple pattern to the line. Input is one 32-bit
// DWORD which describes the stipple pattern. 1 is opaque, 0 is
// transparent.
// SetPatternScale -
// Stretches the stipple pattern in the u direction. Input is one
// floating-point value. 0.0f is no scaling, whereas 1.0f doubles
// the length of the stipple pattern.
// SetWidth -
// Specifies the thickness of the line in the v direction. Input is
// one floating-point value.
// SetAntialias -
// Toggles line antialiasing. Input is a BOOL.
// TRUE = Antialiasing on.
// FALSE = Antialiasing off.
// SetGLLines -
// Toggles non-antialiased OpenGL line emulation. Input is a BOOL.
// TRUE = OpenGL line emulation on.
// FALSE = OpenGL line emulation off.
// OpenGL line: Regular line:
// *\ *\
// | \ / \
// | \ *\ \
// *\ \ \ \
// \ \ \ \
// \ * \ *
// \ | \ /
// \| *
// *
// OnLostDevice, OnResetDevice -
// Call OnLostDevice() on this object before calling Reset() on the
// device, so that this object can release any stateblocks and video
// memory resources. After Reset(), the call OnResetDevice().
//=============================================================================
void createLine(LP_DXLINE *line)
{
D3DXCreateLine(device3d, line);
}
//=============================================================================
// Draw DirectX line from X1,Y1 to X2,Y2.
// width defauts to 1.
// color defaults to graphicsNS::WHITE.
//=============================================================================
void drawLine(float x1, float y1, float x2, float y2, float width = 1.0f, COLOR_ARGB color = graphicsNS::WHITE);
//=============================================================================
// Clear backbuffer and BeginScene()
//=============================================================================
HRESULT beginScene()
{
result = E_FAIL;
if(device3d == NULL)
return result;
// Clear back buffer, stencil buffer and depth buffer
device3d->Clear(0, 0,
D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,
backColor, 1.0f, 0);
result = device3d->BeginScene(); // begin scene for drawing
return result;
}
//=============================================================================
// EndScene()
//=============================================================================
HRESULT endScene()
{
result = E_FAIL;
if(device3d)
result = device3d->EndScene();
return result;
}
//=============================================================================
// Sprite Begin
//=============================================================================
void spriteBegin()
{
sprite->Begin(D3DXSPRITE_ALPHABLEND);
}
//=============================================================================
// Sprite End
//=============================================================================
void spriteEnd()
{
sprite->End();
}
};
#endif
view raw Graphics.h hosted with ❤ by GitHub


cpp 코드입니다.


#include "stdafx.h"
#include "graphics.h"
//=============================================================================
// Constructor
//=============================================================================
Graphics::Graphics()
{
direct3d = NULL;
device3d = NULL;
sprite = NULL;
fullscreen = false;
stencilSupport = false;
width = GAME_WIDTH; // width & height are replaced in initialize()
height = GAME_HEIGHT;
backColor = graphicsNS::BACK_COLOR;
pOcclusionQuery = NULL;
numberOfPixelsColliding = 0;
line = NULL;
}
//=============================================================================
// Destructor
//=============================================================================
Graphics::~Graphics()
{
releaseAll();
}
//=============================================================================
// Initialize DirectX graphics
// throws GameError on error
//=============================================================================
void Graphics::initialize(HWND hw, int w, int h, bool full)
{
hwnd = hw;
width = w;
height = h;
fullscreen = full;
//initialize Direct3D
direct3d = Direct3DCreate9(D3D_SDK_VERSION);
if (direct3d == NULL)
throw(GameError(gameErrorNS::FATAL_ERROR, "Error initializing Direct3D"));
initD3Dpp(); // init D3D presentation parameters
if(fullscreen) // if full-screen mode
{
if(isAdapterCompatible()) // is the adapter compatible
// set the refresh rate with a compatible one
d3dpp.FullScreen_RefreshRateInHz = pMode.RefreshRate;
else
throw(GameError(gameErrorNS::FATAL_ERROR,
"The graphics device does not support the specified resolution and/or format."));
}
// determine if graphics card supports harware texturing and lighting and vertex shaders
D3DCAPS9 caps;
DWORD behavior;
result = direct3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
// If device doesn't support HW T&L or doesn't support 1.1 vertex
// shaders in hardware, then switch to software vertex processing.
if( (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0 ||
caps.VertexShaderVersion < D3DVS_VERSION(1,1) )
behavior = D3DCREATE_SOFTWARE_VERTEXPROCESSING; // use software only processing
else
behavior = D3DCREATE_HARDWARE_VERTEXPROCESSING; // use hardware only processing
//create Direct3D device
result = direct3d->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hwnd,
behavior,
&d3dpp,
&device3d);
if (FAILED(result))
throw(GameError(gameErrorNS::FATAL_ERROR, "Error creating Direct3D device"));
result = D3DXCreateSprite(device3d, &sprite);
if (FAILED(result))
throw(GameError(gameErrorNS::FATAL_ERROR, "Error creating Direct3D sprite"));
// Configure for alpha blend of primitives
device3d->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
device3d->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device3d->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// Check if the device supports 8-bit stencil buffering.
if( FAILED( direct3d->CheckDeviceFormat(caps.AdapterOrdinal,
caps.DeviceType,
d3dpp.BackBufferFormat,
D3DUSAGE_DEPTHSTENCIL,
D3DRTYPE_SURFACE,
D3DFMT_D24S8 ) ) )
stencilSupport = false;
else
stencilSupport = true;
// Create query object, used for pixel perfect collision detection
device3d->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);
createLine(&line); // create a DirectX line
}
//=============================================================================
// Initialize D3D presentation parameters
//=============================================================================
void Graphics::initD3Dpp()
{
try{
ZeroMemory(&d3dpp, sizeof(d3dpp)); // fill the structure with 0
// fill in the parameters we need
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
if(fullscreen) // if fullscreen
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; // 24 bit color
else
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; // use desktop setting
d3dpp.BackBufferCount = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hwnd;
d3dpp.Windowed = (!fullscreen);
if(VSYNC) // if vertical sync is enabled
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
else
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; // Depth 24, Stencil 8
} catch(...)
{
throw(GameError(gameErrorNS::FATAL_ERROR,
"Error initializing D3D presentation parameters"));
}
}
//=============================================================================
// Load the texture into default D3D memory (normal texture use)
// For internal engine use only. Use the TextureManager class to load game textures.
// Pre: filename is name of texture file.
// transcolor is transparent color
// Post: width and height = size of texture
// texture points to texture
// Returns HRESULT
//=============================================================================
HRESULT Graphics::loadTexture(const char *filename, COLOR_ARGB transcolor,
UINT &width, UINT &height, LP_TEXTURE &texture)
{
// The struct for reading file info
D3DXIMAGE_INFO info;
result = E_FAIL;
try{
if(filename == NULL)
{
texture = NULL;
return D3DERR_INVALIDCALL;
}
// Get width and height from file
result = D3DXGetImageInfoFromFile(filename, &info);
if (result != D3D_OK)
return result;
width = info.Width;
height = info.Height;
// Create the new texture by loading from file
result = D3DXCreateTextureFromFileEx(
device3d, //3D device
filename, //image filename
info.Width, //texture width
info.Height, //texture height
1, //mip-map levels (1 for no chain)
0, //usage
D3DFMT_UNKNOWN, //surface format (default)
D3DPOOL_DEFAULT, //memory class for the texture
D3DX_DEFAULT, //image filter
D3DX_DEFAULT, //mip filter
transcolor, //color key for transparency
&info, //bitmap file info (from loaded file)
NULL, //color palette
&texture ); //destination texture
} catch(...)
{
throw(GameError(gameErrorNS::FATAL_ERROR, "Error in Graphics::loadTexture"));
}
return result;
}
//=============================================================================
// Load the texture into system memory (system memory is lockable)
// Provides direct access to pixel data. Use the TextureManager class to load
// textures for display.
// Pre: filename is name of texture file.
// transcolor transparent color
// Post: width and height = size of texture
// texture points to texture
// Returns HRESULT and fills TextureData structure.
//=============================================================================
HRESULT Graphics::loadTextureSystemMem(const char *filename, COLOR_ARGB transcolor,
UINT &width, UINT &height, LP_TEXTURE &texture)
{
// The struct for reading bitmap file info
D3DXIMAGE_INFO info;
result = E_FAIL; // Standard Windows return value
try{
if(filename == NULL)
{
texture = NULL;
return D3DERR_INVALIDCALL;
}
// Get width and height from bitmap file
result = D3DXGetImageInfoFromFile(filename, &info);
if (result != D3D_OK)
return result;
width = info.Width;
height = info.Height;
// Create the new texture by loading a bitmap image file
result = D3DXCreateTextureFromFileEx(
device3d, //3D device
filename, //bitmap filename
info.Width, //bitmap image width
info.Height, //bitmap image height
1, //mip-map levels (1 for no chain)
0, //usage
D3DFMT_UNKNOWN, //surface format (default)
D3DPOOL_SYSTEMMEM, //systemmem is lockable
D3DX_DEFAULT, //image filter
D3DX_DEFAULT, //mip filter
transcolor, //color key for transparency
&info, //bitmap file info (from loaded file)
NULL, //color palette
&texture ); //destination texture
} catch(...)
{
throw(GameError(gameErrorNS::FATAL_ERROR, "Error in Graphics::loadTexture"));
}
return result;
}
//=============================================================================
// Create a vertex buffer.
// Pre: verts[] contains vertex data.
// size = size of verts[]
// Post: &vertexBuffer points to buffer if successful.
//=============================================================================
HRESULT Graphics::createVertexBuffer(VertexC verts[], UINT size, LP_VERTEXBUFFER &vertexBuffer)
{
// Standard Windows return value
HRESULT result = E_FAIL;
// Create a vertex buffer
result = device3d->CreateVertexBuffer(size, D3DUSAGE_WRITEONLY, D3DFVF_VERTEX,
D3DPOOL_DEFAULT, &vertexBuffer, NULL);
if(FAILED(result))
return result;
void *ptr;
// must lock buffer before data can be transferred in
result = vertexBuffer->Lock(0, size, (void**)&ptr, 0);
if(FAILED(result))
return result;
memcpy(ptr, verts, size); // copy vertex data into buffer
vertexBuffer->Unlock(); // unlock buffer
return result;
}
//=============================================================================
// Display a quad with alpha transparency using Triangle Fan
// Pre: createVertexBuffer was used to create vertexBuffer containing four
// vertices defining the quad in clockwise order.
// g3ddev->BeginScene was called
// Post: Quad is drawn
//=============================================================================
bool Graphics::drawQuad(LP_VERTEXBUFFER vertexBuffer)
{
HRESULT result = E_FAIL; // standard Windows return value
if(vertexBuffer == NULL)
return false;
device3d->SetRenderState(D3DRS_ALPHABLENDENABLE, true); // enable alpha blend
device3d->SetStreamSource(0, vertexBuffer, 0, sizeof(VertexC));
device3d->SetFVF(D3DFVF_VERTEX);
result = device3d->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
device3d->SetRenderState(D3DRS_ALPHABLENDENABLE, false); // alpha blend off
if(FAILED(result))
return false;
return true;
}
//=============================================================================
// Draw DirectX line from X1,Y1 to X2,Y2.
// width defauts to 1.
// color defaults to graphicsNS::WHITE.
//=============================================================================
void Graphics::drawLine(float x1, float y1, float x2, float y2, float width, COLOR_ARGB color)
{
VECTOR2 endpoints[] = { VECTOR2(x1, y1), VECTOR2(x2, y2) };
if (width < 0.1f)
width = 0.1f;
line->SetWidth(width); // SetWidth must be called before Begin
line->Begin(); // begin drawing lines
line->Draw(endpoints, 2, color); // draw line, 2 vertices
line->End(); // end drawing lines
}
//=============================================================================
// Display the backbuffer
//=============================================================================
HRESULT Graphics::showBackbuffer()
{
// Display backbuffer to screen
result = device3d->Present(NULL, NULL, NULL, NULL);
return result;
}
//=============================================================================
// Checks the adapter to see if it is compatible with the BackBuffer height,
// width and refresh rate specified in d3dpp. Fills in the pMode structure with
// the format of the compatible mode, if found.
// Pre: d3dpp is initialized.
// Post: Returns true if compatible mode found and pMode structure is filled.
// Returns false if no compatible mode found.
//=============================================================================
bool Graphics::isAdapterCompatible()
{
UINT modes = direct3d->GetAdapterModeCount(D3DADAPTER_DEFAULT,
d3dpp.BackBufferFormat);
for(UINT i=0; i<modes; i++)
{
result = direct3d->EnumAdapterModes( D3DADAPTER_DEFAULT,
d3dpp.BackBufferFormat,
i, &pMode);
if( pMode.Height == d3dpp.BackBufferHeight &&
pMode.Width == d3dpp.BackBufferWidth &&
pMode.RefreshRate >= d3dpp.FullScreen_RefreshRateInHz)
return true;
}
return false;
}
//=============================================================================
// Draw the sprite described in SpriteData structure
// Color is optional, it is applied like a filter, WHITE is default (no change)
// Pre : sprite->Begin() is called
// Post: sprite->End() is called
// spriteData.rect defines the portion of spriteData.texture to draw
// spriteData.rect.right must be right edge + 1
// spriteData.rect.bottom must be bottom edge + 1
//=============================================================================
void Graphics::drawSprite(const SpriteData &spriteData, COLOR_ARGB color)
{
if(spriteData.texture == NULL) // if no texture
return;
// Find center of sprite
D3DXVECTOR2 spriteCenter=D3DXVECTOR2((float)(spriteData.width/2*spriteData.scale),
(float)(spriteData.height/2*spriteData.scale));
// Screen position of the sprite
D3DXVECTOR2 translate=D3DXVECTOR2((float)spriteData.x,(float)spriteData.y);
// Scaling X,Y
D3DXVECTOR2 scaling(spriteData.scale,spriteData.scale);
if (spriteData.flipHorizontal) // if flip horizontal
{
scaling.x *= -1; // negative X scale to flip
// Get center of flipped image.
spriteCenter.x -= (float)(spriteData.width*spriteData.scale);
// Flip occurs around left edge, translate right to put
// Flipped image in same location as original.
translate.x += (float)(spriteData.width*spriteData.scale);
}
if (spriteData.flipVertical) // if flip vertical
{
scaling.y *= -1; // negative Y scale to flip
// Get center of flipped image
spriteCenter.y -= (float)(spriteData.height*spriteData.scale);
// Flip occurs around top edge, translate down to put
// Flipped image in same location as original.
translate.y += (float)(spriteData.height*spriteData.scale);
}
// Create a matrix to rotate, scale and position our sprite
D3DXMATRIX matrix;
D3DXMatrixTransformation2D(
&matrix, // the matrix
NULL, // keep origin at top left when scaling
0.0f, // no scaling rotation
&scaling, // scale amount
&spriteCenter, // rotation center
(float)(spriteData.angle), // rotation angle
&translate); // X,Y location
// Tell the sprite about the matrix "Hello Neo"
sprite->SetTransform(&matrix);
// Draw the sprite
sprite->Draw(spriteData.texture, &spriteData.rect,NULL,NULL,color);
}
//=============================================================================
// Toggle window or fullscreen mode
// Pre: All user created D3DPOOL_DEFAULT surfaces are freed.
// Post: All user surfaces are recreated.
//=============================================================================
void Graphics::changeDisplayMode(graphicsNS::DISPLAY_MODE mode)
{
try{
switch(mode)
{
case graphicsNS::FULLSCREEN:
if(fullscreen) // if already in fullscreen mode
return;
fullscreen = true; break;
case graphicsNS::WINDOW:
if(fullscreen == false) // if already in window mode
return;
fullscreen = false; break;
default: // default to toggle window/fullscreen
fullscreen = !fullscreen;
}
reset();
if(fullscreen) // fullscreen
{
SetWindowLong(hwnd, GWL_STYLE, WS_EX_TOPMOST | WS_VISIBLE | WS_POPUP);
}
else // windowed
{
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, HWND_TOP, 0,0,GAME_WIDTH,GAME_HEIGHT,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// Adjust window size so client area is GAME_WIDTH x GAME_HEIGHT
RECT clientRect;
GetClientRect(hwnd, &clientRect); // get size of client area of window
MoveWindow(hwnd,
0, // Left
0, // Top
GAME_WIDTH+(GAME_WIDTH-clientRect.right), // Right
GAME_HEIGHT+(GAME_HEIGHT-clientRect.bottom), // Bottom
TRUE); // Repaint the window
}
} catch(...)
{
// An error occured, try windowed mode
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, HWND_TOP, 0,0,GAME_WIDTH,GAME_HEIGHT,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// Adjust window size so client area is GAME_WIDTH x GAME_HEIGHT
RECT clientRect;
GetClientRect(hwnd, &clientRect); // get size of client area of window
MoveWindow(hwnd,
0, // Left
0, // Top
GAME_WIDTH+(GAME_WIDTH-clientRect.right), // Right
GAME_HEIGHT+(GAME_HEIGHT-clientRect.bottom), // Bottom
TRUE); // Repaint the window
}
}
//=============================================================================
// Return the number of pixels colliding between the two sprites.
// Pre: The device supports a stencil buffer and pOcclusionQuery points to
// a valid occlusionQuery object.
// Post: Returns the number of pixels of overlap
// This function waits for the graphics card to render the last frame and return
// the collision query pixel count. To avoid slowing down your game, use a
// simple collison test first to eliminate entities that are not colliding.
// Using pixel perfect collision test with multiple entities would require
// creating multiple occlusionQuery objects and clearing the stencil buffer
// between each test.
//=============================================================================
DWORD Graphics::pixelCollision(const SpriteData &sprite1, const SpriteData &sprite2)
{
if(!stencilSupport) // if no stencil buffer support
return 0;
beginScene();
// Set up stencil buffer using current entity
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
device3d->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
device3d->SetRenderState(D3DRS_STENCILREF, 0x1);
device3d->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
device3d->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);
device3d->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
device3d->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
// Write a 1 into the stencil buffer for each non-transparent pixel in ent
spriteBegin();
// Enable stencil buffer (must be after spriteBegin)
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
drawSprite(sprite2); // write 1s to stencil buffer
spriteEnd();
// Change stencil buffer to only allow writes where the stencil value is 1
// (where the ent sprite is colliding with the current sprite)
device3d->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
device3d->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
// Begin occlusion query to count pixels that are drawn
pOcclusionQuery->Issue(D3DISSUE_BEGIN);
spriteBegin();
// Enable stencil buffer (must be after spriteBegin)
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
drawSprite(sprite1); // draw current entity
spriteEnd();
// End occlusion query
pOcclusionQuery->Issue(D3DISSUE_END);
// Wait until the GPU is finished.
while(S_FALSE == pOcclusionQuery->GetData( &numberOfPixelsColliding,
sizeof(DWORD), D3DGETDATA_FLUSH ))
{}
// Turn off stencil
device3d->SetRenderState(D3DRS_STENCILENABLE, false);
endScene();
return numberOfPixelsColliding;
}
//=============================================================================
// Test for lost device
//=============================================================================
HRESULT Graphics::getDeviceState()
{
result = E_FAIL; // default to fail, replace on success
if (device3d == NULL)
return result;
result = device3d->TestCooperativeLevel();
return result;
}
//=============================================================================
// Release all
//=============================================================================
void Graphics::releaseAll()
{
safeRelease(pOcclusionQuery);
safeRelease(sprite);
safeRelease(device3d);
safeRelease(direct3d);
safeRelease(line);
}
//=============================================================================
// Reset the graphics device
//=============================================================================
HRESULT Graphics::reset()
{
safeRelease(line);
safeRelease(pOcclusionQuery); // release query
initD3Dpp(); // init D3D presentation parameters
sprite->OnLostDevice(); // release sprite
result = device3d->Reset(&d3dpp); // attempt to reset graphics device
// Configure for alpha blend of primitives
device3d->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
device3d->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device3d->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// recreate query
device3d->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);
sprite->OnResetDevice(); // recreate sprite
createLine(&line); // recreate a DirectX line
return result;
}
view raw graphics.cpp hosted with ❤ by GitHub

'MyProject > SephyEngine' 카테고리의 다른 글

June_Engine #3_Image Class .cpp (1)  (0) 2017.04.19
June_Engine #3_Image (Class) & SpriteData (Struct)  (0) 2017.04.19
June_Engine #2_Graphics.cpp (2)  (0) 2017.04.17
June_Engine #2_Graphics.cpp (1)  (0) 2017.04.15
June_Engine #2_Graphics.h  (0) 2017.04.07
posted by REDFORCE 2017. 4. 17. 21:53

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics  (현재 글)

 + Image - SpriteData

 + Layer

 + Input

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


-----------------------------------------------------------------------


Graphics Class의 핵심 내용에 이어서 나머지 함수들에 대해서 정리하도록 하겠습니다.


아래 코드는 Graphics class에서 제공해주는 VertexBuffer / Draw Quad / Draw Line 함수 입니다.


//=============================================================================
// Create a vertex buffer.
// Pre: verts[] contains vertex data.
// size = size of verts[]
// Post: &vertexBuffer points to buffer if successful.
//=============================================================================
HRESULT Graphics::createVertexBuffer(VertexC verts[], UINT size, LP_VERTEXBUFFER &vertexBuffer)
{
// Standard Windows return value
HRESULT result = E_FAIL;
// Create a vertex buffer
result = device3d->CreateVertexBuffer(size, D3DUSAGE_WRITEONLY, D3DFVF_VERTEX,
D3DPOOL_DEFAULT, &vertexBuffer, NULL);
if(FAILED(result))
return result;
void *ptr;
// must lock buffer before data can be transferred in
result = vertexBuffer->Lock(0, size, (void**)&ptr, 0);
if(FAILED(result))
return result;
memcpy(ptr, verts, size); // copy vertex data into buffer
vertexBuffer->Unlock(); // unlock buffer
return result;
}
//=============================================================================
// Display a quad with alpha transparency using Triangle Fan
// Pre: createVertexBuffer was used to create vertexBuffer containing four
// vertices defining the quad in clockwise order.
// g3ddev->BeginScene was called
// Post: Quad is drawn
//=============================================================================
bool Graphics::drawQuad(LP_VERTEXBUFFER vertexBuffer)
{
HRESULT result = E_FAIL; // standard Windows return value
if(vertexBuffer == NULL)
return false;
device3d->SetRenderState(D3DRS_ALPHABLENDENABLE, true); // enable alpha blend
device3d->SetStreamSource(0, vertexBuffer, 0, sizeof(VertexC));
device3d->SetFVF(D3DFVF_VERTEX);
result = device3d->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
device3d->SetRenderState(D3DRS_ALPHABLENDENABLE, false); // alpha blend off
if(FAILED(result))
return false;
return true;
}
//=============================================================================
// Draw DirectX line from X1,Y1 to X2,Y2.
// width defauts to 1.
// color defaults to graphicsNS::WHITE.
//=============================================================================
void Graphics::drawLine(float x1, float y1, float x2, float y2, float width, COLOR_ARGB color)
{
VECTOR2 endpoints[] = { VECTOR2(x1, y1), VECTOR2(x2, y2) };
if (width < 0.1f)
width = 0.1f;
line->SetWidth(width); // SetWidth must be called before Begin
line->Begin(); // begin drawing lines
line->Draw(endpoints, 2, color); // draw line, 2 vertices
line->End(); // end drawing lines
}
view raw Graphics.cpp hosted with ❤ by GitHub


1. createVertexBuffer


VertexBuffer 라는 말에서 볼 수 있듯이


createVertexBuffer 함수는 directX를 공부하셨다면 Vertex 라는 개념을 이해할 수 있습니다.

이때 사용하는 함수인데, 모든 이미지나 어떤 출력물은 vertex라 하는 좌표점들을 통해 구성이 되어집니다.


특히 3D의 모델은 삼각형으로 이루어진 폴리곤의 집합체라 볼 수 있습니다.


마찬가지로 2D 에서도 Vertex를 이용한 Drawing이 필요할 때가 있습니다.


그 예시들은 다음에 기회가 되면 설명드리기로 하고, 지금은 일단 넘어가겠습니다.



2. DrawLine / DrawQuad


DrawLine과 DrawQuad는 Vertex를 이용하여 어떤 선 / 면을 그리는 함수 입니다.

인게임 자체에서 자주 이용할지 여부는 때에 따라 다르나,


맵툴에서의 타일 또는 간단한 테스팅을 위한 UI용도로는 상당히 편리한 함수입니다.




3. showBackBuffer


세 번째 showBackBuffer 함수는 매우 심플하여 별거 없어보이지만

의미 자체는 정말 중요한 함수입니다.


showBackBuffer의 내용을 보면 간단히 코드 한줄로만 이루어져 있습니다.

(return 문은 계산에서 제외..)


device3d->Present(NULL, NULL, NULL, NULL);


가 그 코드인데요.


present(NULL, NULL, NULL, NULL)로 주는 순간. 


Device에서는 "아~ 내가 출력할 것들다 넣어줬어? 그럼 이제 렌더링해야지~"

하고 받아들이게 됩니다.


결국 showbackbuffer 라는 함수는 매 프레임 렌더할 내용들이 끝나는 시점인 마지막에

호출하게 됩니다.

//=============================================================================
// Display the backbuffer
//=============================================================================
HRESULT Graphics::showBackbuffer()
{
// Display backbuffer to screen
result = device3d->Present(NULL, NULL, NULL, NULL);
return result;
}



4. isAdapterCompatible


isAdapterCompatible 함수는 원래 initialize 를 다룰때 설명해드렸어야 했으나


제가 실수로 이제와서 언급하게 되었네요.


함수의 내용을 보면 UINT 값 modes 를 통해 현재 모니터의 어댑터 모드를 볼 수 있습니다. DVI라던지...HDMI라던지...그리고 받아온 값을 통해 전체화면으로 출력 할 시


adapter 값을 확인하고 화면의 width height 값 만큼 전체 화면으로 출력 해줍니다.



5. drawSprite


5번째로 설명할 drawSprite 함수는 Graphics 클래스에서 가장 많이 사용 되어지며


결과적으로 Graphics 클래스가 궁극적으로 'rendering' 이라는 역할을 수행하는

함수가 바로 drawSprite 입니다.


//=============================================================================
// Draw the sprite described in SpriteData structure
// Color is optional, it is applied like a filter, WHITE is default (no change)
// Pre : sprite->Begin() is called
// Post: sprite->End() is called
// spriteData.rect defines the portion of spriteData.texture to draw
// spriteData.rect.right must be right edge + 1
// spriteData.rect.bottom must be bottom edge + 1
//=============================================================================
void Graphics::drawSprite(const SpriteData &spriteData, COLOR_ARGB color)
{
if(spriteData.texture == NULL) // if no texture
return;
// Find center of sprite
D3DXVECTOR2 spriteCenter=D3DXVECTOR2((float)(spriteData.width/2*spriteData.scale),
(float)(spriteData.height/2*spriteData.scale));
// Screen position of the sprite
D3DXVECTOR2 translate=D3DXVECTOR2((float)spriteData.x,(float)spriteData.y);
// Scaling X,Y
D3DXVECTOR2 scaling(spriteData.scale,spriteData.scale);
if (spriteData.flipHorizontal) // if flip horizontal
{
scaling.x *= -1; // negative X scale to flip
// Get center of flipped image.
spriteCenter.x -= (float)(spriteData.width*spriteData.scale);
// Flip occurs around left edge, translate right to put
// Flipped image in same location as original.
translate.x += (float)(spriteData.width*spriteData.scale);
}
if (spriteData.flipVertical) // if flip vertical
{
scaling.y *= -1; // negative Y scale to flip
// Get center of flipped image
spriteCenter.y -= (float)(spriteData.height*spriteData.scale);
// Flip occurs around top edge, translate down to put
// Flipped image in same location as original.
translate.y += (float)(spriteData.height*spriteData.scale);
}
// Create a matrix to rotate, scale and position our sprite
D3DXMATRIX matrix;
D3DXMatrixTransformation2D(
&matrix, // the matrix
NULL, // keep origin at top left when scaling
0.0f, // no scaling rotation
&scaling, // scale amount
&spriteCenter, // rotation center
(float)(spriteData.angle), // rotation angle
&translate); // X,Y location
// Tell the sprite about the matrix "Hello Neo"
sprite->SetTransform(&matrix);
// Draw the sprite
sprite->Draw(spriteData.texture, &spriteData.rect,NULL,NULL,color);
}
view raw drawSprite.cpp hosted with ❤ by GitHub



drawSprite 함수는 2가지 파라미터 값을 받습니다.


 (1) SprtieData 구조체 타입 값

 (2) 투명 적용시킬 컬러 값


SpriteData 구조체는 앞에서 몇번 언급했듯이 모든 이미지들이 담겨져있는 데이터 구조체 입니다.


SpriteData 구조체를 까먹으신 분들을 위해 다시 한번 SpriteData 구조체안에 어떤 정보들이 담겨있는지 다시 살펴보겠습니다.


// SpriteData: The properties required by Graphics::drawSprite to draw a sprite
struct SpriteData
{
int width; // width of sprite in pixels
int height; // height of sprite in pixels
float x; // screen location (top left corner of sprite)
float y;
float scale; // <1 smaller, >1 bigger
float angle; // rotation angle in radians
RECT rect; // used to select an image from a larger texture
LP_TEXTURE texture; // pointer to texture
bool flipHorizontal; // true to flip sprite horizontally (mirror)
bool flipVertical; // true to flip sprite vertically
};
view raw graphics.h hosted with ❤ by GitHub


구조체 안에 있는 정보들을 보면 


 (1) width / height  (텍스쳐 그림 크기)

 (2) x, y (출력할 x, y 좌표)

 (3) scale (스케일 크기)

 (4) angle (출력 할 그림의 각도)

 (5) rect (텍스쳐 안에서 출력 할 부분)

 (6) 기타..(texture / 반전 여부)


들이 있습니다. 위 항목들을 토대로 drawSprite 함수에서 필요한 내용들이 수행되어집니다.


차례대로 함수안의 내용을 나열해보겠습니다.


 A. spriteCenter : SpriteCenter 값은 출력 할 스프라이트(그림)의 센터를 받아옵니다.

 B. translate : 스크린상의 x, y 좌표로 변환 값

 C. scaling : SpriteData 안에 있는 scale 값을 토대로 이미지에 연산 시킬 스케일 값

 D. 좌우상하 반전 여부를 통해 센터값을 기준으로 상하좌우 반전을 계산합니다.

 E. D3DXMatrixTransformation2D 함수를 통해 연산 값들을 넣어 최종 D3DMATRIX (matrix)에 넣습니다.

 F. 나온 결과 값을 토대로 sprite에 matrix 값을 transform 시킵니다.

 G. 마지막으로 Draw 함수 수행.


위 과정을 간추려 요약하자면  3가지 단계로 이루어지게 됩니다.


 ① SpriteData구조체 값을 통해 출력 할 내용을 D3DMatrix 값으로 변환 

 ② LPD3DXSPRITE 에 SetTransform (위치나 스케일 등 Setup)

 ③ LPD3DXSPRITE Draw


첫 번째 과정이 조금 길게 느껴지실 수 있으나 한번 위와같이 설정해두면 우리는 어떤 그림이든 이제 이 함수 자체만을 통해 바로바로 그림을 출력 할 수 있게 됩니다.


계속해서 적으면 글이 너무 길어지므로, 이번 글에서는 DrawSprite 까지만 다루도록 하겠습니다.

'MyProject > SephyEngine' 카테고리의 다른 글

June_Engine #3_Image (Class) & SpriteData (Struct)  (0) 2017.04.19
June_Engine #2_Graphics.cpp (3)  (0) 2017.04.18
June_Engine #2_Graphics.cpp (1)  (0) 2017.04.15
June_Engine #2_Graphics.h  (0) 2017.04.07
June_Engine #1_MainNode  (0) 2017.04.07
posted by REDFORCE 2017. 4. 15. 01:51

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics  (현재 글)

 + Image - SpriteData

 + Layer

 + Input

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


-----------------------------------------------------------------------


이번 글에서는 Graphics 클래스의 cpp 파일을 확인하도록 하겠습니다.


먼저 헤더 내용을 정리하며 가리켰던 3가지 항목


A. struct VertexC

B. struct SpriteData

C. Class Graphics


를 다시 상기해볼 필요가 있습니다.


A. VertexC에 대해서 간략히 다시 설명하자면


앞으로 사용 할 모든 이미지들은 x,y 좌표가 필요한데, DirectX 상에서는 3D 좌표를 이용하므로

z 축 좌표가 하나 더 필요합니다.


그러나 여기서 다룰 2D 게임을 위한 엔진에서는 z 축은 사실 상 필요가 없습니다.

따라서 z 축 자체는 0 값으로 모두 보존됩니다.



B. SpriteData 구조체는 모든 이미지를 사용함에 있어 그 데이터를 가지고 있습니다.

출력할 스크린 상의 x, y 좌표, 텍스쳐(이미지) 정보, 텍스쳐에서 어떤 부분을 사용할 것인지 RECT 값 등.


Graphics 클래스에서는 이 SpriteData 구조체를 주소 값 형식( * 가 아닌 & 연산자)으로 받아옴으로 원하고자 하는 SpriteData 정보를 이용하여 이미지를 출력합니다.



C. Graphics Class의 .cpp 내용을 지금 부터 확인해보겠습니다.



1. 생성자 ( Constructor )와 소멸자


생성자 구역은 헤더에서 선언한 주요 Graphics 클래스의 멤버들을 초기화합니다.

별 다른 특별한 내용은 없습니다.

#include "stdafx.h"
#include "graphics.h"
//=============================================================================
// Constructor
//=============================================================================
Graphics::Graphics()
{
direct3d = NULL;
device3d = NULL;
sprite = NULL;
fullscreen = false;
stencilSupport = false;
width = GAME_WIDTH; // width & height are replaced in initialize()
height = GAME_HEIGHT;
backColor = graphicsNS::BACK_COLOR;
pOcclusionQuery = NULL;
numberOfPixelsColliding = 0;
line = NULL;
}
//=============================================================================
// Destructor
//=============================================================================
Graphics::~Graphics()
{
releaseAll();
}
view raw Graphics.cpp hosted with ❤ by GitHub



소멸자는 releaseAll 함수를 호출하여 연결되어있던 내용들을 해지합니다.

//=============================================================================
// Release all
//=============================================================================
void Graphics::releaseAll()
{
safeRelease(pOcclusionQuery);
safeRelease(sprite);
safeRelease(device3d);
safeRelease(direct3d);
safeRelease(line);
}



releaseAll( ) 에서 사용한 safeRelease( ) 함수는 템플릿 형태로 미리 만들어 둔 매크로 입니다.

/=============================================================================
// Function templates for safely dealing with pointer referenced items.
// The functions defined by these templates may be called using a normal
// function call syntax. The compiler will create a function that replaces T
// with the type of the calling parameter.
//=============================================================================
// Safely release pointer referenced item
template <typename T>
inline void safeRelease(T& ptr)
{
if (ptr)
{
ptr->Release();
ptr = NULL;
}
}
#define SAFE_RELEASE safeRelease // for backward compatiblility
// Safely delete pointer referenced item
template <typename T>
inline void safeDelete(T& ptr)
{
if (ptr)
{
delete ptr;
ptr = NULL;
}
}
#define SAFE_DELETE safeDelete // for backward compatiblility
// Safely delete pointer referenced array
template <typename T>
inline void safeDeleteArray(T& ptr)
{
if (ptr)
{
delete[] ptr;
ptr = NULL;
}
}
#define SAFE_DELETE_ARRAY safeDeleteArray // for backward compatiblility
// Safely call onLostDevice
template <typename T>
inline void safeOnLostDevice(T& ptr)
{
if (ptr)
ptr->onLostDevice();
}
#define SAFE_ON_LOST_DEVICE safeOnLostDevice // for backward compatiblility
// Safely call onResetDevice
template <typename T>
inline void safeOnResetDevice(T& ptr)
{
if (ptr)
ptr->onResetDevice();
}
#define SAFE_ON_RESET_DEVICE safeOnResetDevice // for backward compatiblility
view raw safeRelease.cpp hosted with ❤ by GitHub




2. Initialize


이어서 Graphics Class의 시작인 initialize( )를 살펴보겠습니다.


initialize 의미 자체대로 모든 초기화에 대한 진행을 이곳에서 합니다.


//=============================================================================
// Initialize DirectX graphics
// throws GameError on error
//=============================================================================
void Graphics::initialize(HWND hw, int w, int h, bool full)
{
hwnd = hw;
width = w;
height = h;
fullscreen = full;
//initialize Direct3D
direct3d = Direct3DCreate9(D3D_SDK_VERSION);
if (direct3d == NULL)
throw(GameError(gameErrorNS::FATAL_ERROR, "Error initializing Direct3D"));
initD3Dpp(); // init D3D presentation parameters
if(fullscreen) // if full-screen mode
{
if(isAdapterCompatible()) // is the adapter compatible
// set the refresh rate with a compatible one
d3dpp.FullScreen_RefreshRateInHz = pMode.RefreshRate;
else
throw(GameError(gameErrorNS::FATAL_ERROR,
"The graphics device does not support the specified resolution and/or format."));
}
// determine if graphics card supports harware texturing and lighting and vertex shaders
D3DCAPS9 caps;
DWORD behavior;
result = direct3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
// If device doesn't support HW T&L or doesn't support 1.1 vertex
// shaders in hardware, then switch to software vertex processing.
if( (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0 ||
caps.VertexShaderVersion < D3DVS_VERSION(1,1) )
behavior = D3DCREATE_SOFTWARE_VERTEXPROCESSING; // use software only processing
else
behavior = D3DCREATE_HARDWARE_VERTEXPROCESSING; // use hardware only processing
//create Direct3D device
result = direct3d->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hwnd,
behavior,
&d3dpp,
&device3d);
if (FAILED(result))
throw(GameError(gameErrorNS::FATAL_ERROR, "Error creating Direct3D device"));
result = D3DXCreateSprite(device3d, &sprite);
if (FAILED(result))
throw(GameError(gameErrorNS::FATAL_ERROR, "Error creating Direct3D sprite"));
// Configure for alpha blend of primitives
device3d->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
device3d->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device3d->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// Check if the device supports 8-bit stencil buffering.
if( FAILED( direct3d->CheckDeviceFormat(caps.AdapterOrdinal,
caps.DeviceType,
d3dpp.BackBufferFormat,
D3DUSAGE_DEPTHSTENCIL,
D3DRTYPE_SURFACE,
D3DFMT_D24S8 ) ) )
stencilSupport = false;
else
stencilSupport = true;
// Create query object, used for pixel perfect collision detection
device3d->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);
createLine(&line); // create a DirectX line
}
/=============================================================================
// Initialize D3D presentation parameters
//=============================================================================
void Graphics::initD3Dpp()
{
try{
ZeroMemory(&d3dpp, sizeof(d3dpp)); // fill the structure with 0
// fill in the parameters we need
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
if(fullscreen) // if fullscreen
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; // 24 bit color
else
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; // use desktop setting
d3dpp.BackBufferCount = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hwnd;
d3dpp.Windowed = (!fullscreen);
if(VSYNC) // if vertical sync is enabled
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
else
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; // Depth 24, Stencil 8
} catch(...)
{
throw(GameError(gameErrorNS::FATAL_ERROR,
"Error initializing D3D presentation parameters"));
}
}
view raw initialize.cpp hosted with ❤ by GitHub


주요 내용을 천천히 살펴보면 먼저 파라미터


 (1) 생성된 윈도우 핸들 값 (HWND) 

 (2) width / height 값 (int)

 (3) fullscreen 여부 (bool)


값들을 받습니다. 

그리고 direct3d 초기화에 들어갑니다.


위에서 보여지는 DirectX 초기화 설정에 대한 내용은 여기서 다루진 않겠습니다.

(추후 DirectX 관련 글을 올리면서 어떤 내용들이었는지 언급하여 다루겠습니다)


어 일단 이거 코드를 활용 해보고 싶은데? 라는 분들은 그대로 가져가셔서 쓰시되 필요한 설정에 대한 부분만 본인에 맞게 바꾸시면 될 것 같습니다.



3. LoadTexture


LoadTexture 함수는 Graphics Class Initialize 중에 생성 한 DirectX Device에

Texture를 올리는 함수입니다.


간단히 비유하여 설명드리자면,


2. Initialize에서 Direct9Device 라는 상자를 만들었고

LoadTexture 함수는 그 상자안에 Texture라는 내용들을 담아주는 거라 보시면 됩니다.



LoadTexture 함수는 파라미터 값으로 다음의 내용들을 받습니다.


 (1) fileName (const char*) - 파일 경로

 (2) transColor ( COLOR_ARGB ) - 걸러 내고싶은 컬러 값

 (3) width / height ( UINT [unsigned int] ) - 불러 올 텍스쳐 크기

 (4) &texture ( LP_TEXTURE ) - 텍스쳐의 주소 값


아래는 LoadTexture 함수 전체입니다.


간단히 설명하자면 


 (1) 파라미터로 받아온 파일 경로를 통해 이미지 정보를 생성

 (2) 텍스쳐 정보 생성 및 device에 입력


순으로 들어갑니다.


//=============================================================================
// Load the texture into default D3D memory (normal texture use)
// For internal engine use only. Use the TextureManager class to load game textures.
// Pre: filename is name of texture file.
// transcolor is transparent color
// Post: width and height = size of texture
// texture points to texture
// Returns HRESULT
//=============================================================================
HRESULT Graphics::loadTexture(const char *filename, COLOR_ARGB transcolor,
UINT &width, UINT &height, LP_TEXTURE &texture)
{
// The struct for reading file info
D3DXIMAGE_INFO info;
result = E_FAIL;
try{
if(filename == NULL)
{
texture = NULL;
return D3DERR_INVALIDCALL;
}
// Get width and height from file
result = D3DXGetImageInfoFromFile(filename, &info);
if (result != D3D_OK)
return result;
width = info.Width;
height = info.Height;
// Create the new texture by loading from file
result = D3DXCreateTextureFromFileEx(
device3d, //3D device
filename, //image filename
info.Width, //texture width
info.Height, //texture height
1, //mip-map levels (1 for no chain)
0, //usage
D3DFMT_UNKNOWN, //surface format (default)
D3DPOOL_DEFAULT, //memory class for the texture
D3DX_DEFAULT, //image filter
D3DX_DEFAULT, //mip filter
transcolor, //color key for transparency
&info, //bitmap file info (from loaded file)
NULL, //color palette
&texture ); //destination texture
} catch(...)
{
throw(GameError(gameErrorNS::FATAL_ERROR, "Error in Graphics::loadTexture"));
}
return result;
}
view raw LoadTexture.cpp hosted with ❤ by GitHub

위 함수가 기본적인 텍스쳐 정보들을 Device에 담아주는 함수입니다만

아래와 같이 만약 시스템 메모리를 사용하여 텍스쳐를 담고 싶을 경우에는 


로드 텍스쳐 함수를 다음과 같이 이용합니다.

//=============================================================================
// Load the texture into system memory (system memory is lockable)
// Provides direct access to pixel data. Use the TextureManager class to load
// textures for display.
// Pre: filename is name of texture file.
// transcolor transparent color
// Post: width and height = size of texture
// texture points to texture
// Returns HRESULT and fills TextureData structure.
//=============================================================================
HRESULT Graphics::loadTextureSystemMem(const char *filename, COLOR_ARGB transcolor,
UINT &width, UINT &height, LP_TEXTURE &texture)
{
// The struct for reading bitmap file info
D3DXIMAGE_INFO info;
result = E_FAIL; // Standard Windows return value
try{
if(filename == NULL)
{
texture = NULL;
return D3DERR_INVALIDCALL;
}
// Get width and height from bitmap file
result = D3DXGetImageInfoFromFile(filename, &info);
if (result != D3D_OK)
return result;
width = info.Width;
height = info.Height;
// Create the new texture by loading a bitmap image file
result = D3DXCreateTextureFromFileEx(
device3d, //3D device
filename, //bitmap filename
info.Width, //bitmap image width
info.Height, //bitmap image height
1, //mip-map levels (1 for no chain)
0, //usage
D3DFMT_UNKNOWN, //surface format (default)
D3DPOOL_SYSTEMMEM, //systemmem is lockable
D3DX_DEFAULT, //image filter
D3DX_DEFAULT, //mip filter
transcolor, //color key for transparency
&info, //bitmap file info (from loaded file)
NULL, //color palette
&texture ); //destination texture
} catch(...)
{
throw(GameError(gameErrorNS::FATAL_ERROR, "Error in Graphics::loadTexture"));
}
return result;
}
view raw LoadTexture.cpp hosted with ❤ by GitHub



Graphics Class의 주요 핵심 내용은 이렇게 위 3가지로 좁혀집니다만.


다음 글 부터 나머지 Graphics class의 내용에 대해서 정리하도록 하겠습니다.

'MyProject > SephyEngine' 카테고리의 다른 글

June_Engine #2_Graphics.cpp (3)  (0) 2017.04.18
June_Engine #2_Graphics.cpp (2)  (0) 2017.04.17
June_Engine #2_Graphics.h  (0) 2017.04.07
June_Engine #1_MainNode  (0) 2017.04.07
June_Engine #1_EngineError.h  (0) 2017.04.07