270 likes | 447 Views
Chapter 6-4. Parallax Scrolling. Demo 05 실행. Code file Bmp.h Bmp.cpp, Timer.h, Timer.cpp Defines.h, Bsprite.h, Bsprite.cpp, objects.h, objects.cpp, Sbmp.h, Sbmp.cpp Ddsetup.cpp, CSprite.h, Csprite.cpp Main.cpp, Objects.h, Objects.cpp( 수정) Objman.h(cpp), Random.h(cpp), View.h(cpp)( 추가)
E N D
Chapter 6-4. Parallax Scrolling • Demo 05 실행 • Code file • Bmp.h Bmp.cpp, Timer.h, Timer.cpp • Defines.h, Bsprite.h, Bsprite.cpp, • objects.h, objects.cpp, Sbmp.h, Sbmp.cpp • Ddsetup.cpp, CSprite.h, Csprite.cpp • Main.cpp, Objects.h, Objects.cpp(수정) • Objman.h(cpp), Random.h(cpp), View.h(cpp)(추가) • Media file • Sprite.bmp,Bckgnd.bmp, Farm.bmp • Lib file • Ddraw.lib, Winmm.lib
Here’s what you’ll learn • How to manage the player’s viewpoint within the virtual universe with a viewpoint manager • What parallax scrolling is and how to implement it • How to get subpixel scrolling with manager arithmetic • How to manage objects with an object manager • What pseudorandom numbers are, why they are used, and how to generate them
Parallax Scrolling • The effect of having things cross your field of vision at a speed that is inversely proportional to their distance is called parallax. We will cre ate the illusion of depth in our flat two-dimensional virtual universe by having the back ground scroll at a lower speed than objects in the foreground in a process known as parallax scrolling.
The ViewPoint Manager • Ned’s Turkey Farm은 1,280 픽셀너비의 2차원 가상유니버스에서 실행 • Viewpoint의 위치를 기록 및 유지, 화면에 그려질 위치에 관한 정보를 개체에게 제공 • 게임내의 각 개체는 Cobject의 m_nX, M_nY에 가상유니버스내에서의 자신의 위치를 기록한다. • 플레이어의 Viewpoint는 비행기가 화면에 중간에 놓일 수 있도록 비행기에 고정 • 가상 유니버스내의 고정개체(배경, 농장)를 오른쪽으로 이동시킴으로써 비행기가 왼쪽으로 이동하는 parallax Scrolling구현
Viewpoint Manager Overview • Declared in View.h(cpp) #define WORLD_WIDTH 1280 //width of the world class CViewPoint{ private: int m_nX; //x coordinate of viewpoint int m_nBgOffset; //offset of parallax scrolled background int m_nLastTimeBgDrawn; //last time background was drawn public: CViewPoint(); //constructor void set_position(int x); //set current viewpoint int screen(int x); //screen coords relative to viewpt void normalize(int &x); //normalize location x void draw_background(LPDIRECTDRAWSURFACE lpSource, LPDIRECTDRAWSURFACE lpDestination,int speed); };
The Object Manager • 모든 게임개체의 목록을 관리 및 처리하는 작업 • Declared in Objman.h(cpp) class CObjectManager{ private: CObject **m_pObjectList; //list of objects in game int m_nCount; //how many objects in list int m_nMaxCount; //maximum number of objects int m_nCurrentObject; //index of the current object public: CObjectManager(int max); //constructor ~CObjectManager(); //destructor int create(ObjectType object,int x,int y, int xspeed,int yspeed); //create new object //animate all objects void animate(LPDIRECTDRAWSURFACE surface); //the following functions operate on the current object void accelerate(int xdelta,int ydelta=0); //change speed void set_current(int index); //set current object int speed(); //return magnitude of speed };
Generating Pseudorandom Number • 까마귀의 날개짓의 비동기화 목적 • 랜덤프레임에서 까마귀 개체의 날개짓 시작 • 프레임간격을 220~280ms 내의 Random number로 설정 • Declared in Random.h(cpp) #define STALE_RAND 1000 //stale after this many calls class CRandom{ private: int m_nCount; //count of number of times used public: CRandom(); //constructor int number(int i,int j); //return random number in i..j void sowseed(); //seed the random number generator };
Changes to the Object Class enum ObjectType{CROW_OBJECT=0,PLANE_OBJECT,FARM_OBJECT, FIELD_OBJECT,NUM_SPRITES}; class CObject{ //class for a moving object friend class CObjectManager; private: int m_nX,m_nY; //current location int m_nXspeed,m_nYspeed; //current speed int m_nLastXMoveTime; //last time moved horizontally int m_nLastYMoveTime; //last time moved vertically CBaseSprite *m_pSprite; //pointer to sprite int m_nMinXSpeed,m_nMaxXSpeed; //min, max horizontal speeds int m_nMinYSpeed,m_nMaxYSpeed; //min,max vertical speeds int m_nCurrentFrame; //frame to be displayed int m_nFrameCount; //number of frames in animation int m_nLastFrameTime; //last time the frame was changed int m_nFrameInterval; //interval between frames BOOL m_bForwardAnimation; //is animation going forwards? public: • Declaration in Object.h
class CObject{ //class for a moving object private: public: CObject(); //constructor void draw(LPDIRECTDRAWSURFACE surface); //draw void create(ObjectType object,int x,int y, int xspeed,int yspeed); //create object void accelerate(int xdelta,int ydelta=0); //change speed void move(); //make a move depending on time and speed };
Changes to Object Class extern CClippedSprite *g_pSprite[]; //sprites extern CTimer Timer; //game timer Extern Crandom Random; // random number generator Extern CViewPoint Viewpoint; // player viewpoint CObject::CObject(){ //constructor m_nX=m_nY=m_nXspeed=m_nYspeed=0; m_pSprite=NULL; m_nLastXMoveTime=m_nLastYMoveTime=0; m_nCurrentFrame=m_nFrameCount=m_nLastFrameTime=0; m_bForwardAnimation=TRUE; m_nFrameInterval=30; } • CObject 생성자에서 초당 프레임 수 설정
Changes to Draw Function void CObject::draw(LPDIRECTDRAWSURFACE surface){ //draw //draw the current frame m_pSprite->draw(m_nCurrentFrame,Viewpoint.screen(m_nX) ,m_nY,surface); //figure out which frame is next int t=m_nFrameInterval/(1+abs(m_nXspeed)); //frame interval if(m_nFrameCount>1&&Timer.elapsed(m_nLastFrameTime,t)) if(m_bForwardAnimation){ //forward animation if(++m_nCurrentFrame>=m_nFrameCount){ //wrap m_nCurrentFrame=m_nFrameCount-2; m_bForwardAnimation=FALSE; } } else{ //backward animation if(--m_nCurrentFrame<0){ //wrap m_nCurrentFrame=1; m_bForwardAnimation=TRUE; } } } • M_sprite를 가상유니버스의 절대좌표 대신 뷰포인트에 대한 화면상의 위치에 그릴수 있도록 코드추가
Create Function void CObject::create(ObjectType object,int x,int y, int xspeed,int yspeed){ m_nLastXMoveTime=m_nLastYMoveTime=Timer.time(); //time m_nX=x; m_nY=y; //location m_nXspeed=xspeed; m_nYspeed=yspeed; //speed m_pSprite=g_pSprite[object]; //sprite m_nFrameCount=m_pSprite->frame_count(); //frame count //customize properties of each object type switch(object){ case PLANE_OBJECT: m_nMinXSpeed=-3; m_nMaxXSpeed=-1; m_nMinYSpeed=-4; m_nMaxYSpeed=4; m_nFrameIntervel = 250; break; case CROW_OBJECT: m_nMinXSpeed=-2; m_nMaxXSpeed=-1; m_nMinYSpeed=-1; m_nMaxYSpeed=1; m_nCurrentFrame=Random.number(0,m_nFrameCount-1); m_nFrameInterval=250+Random.number(-30,30); break; }
Move Function void CObject::move(){ //move object const int XSCALE=16; //to scale back horizontal motion const int YSCALE=32; //to scale back vertical motion const int YMARGIN=20; //vertical margin; int xdelta,ydelta; //change in position int time=Timer.time(); //current time //horizontal motion int tfactor=time-m_nLastXMoveTime; //time since last move xdelta=(m_nXspeed*tfactor)/XSCALE; //x distance moved m_nX+=xdelta; //x motion Viewpoint.normalize(m_nX); //normalize to world width if(xdelta||m_nXspeed==0) //record time of move m_nLastXMoveTime=time; //vertical motion tfactor=time-m_nLastYMoveTime; //time since last move ydelta=(m_nYspeed*tfactor)/YSCALE; //y distance moved m_nY+=ydelta; //y motion if(m_nY<YMARGIN)m_nY=YMARGIN; if(m_nY>=SCREEN_HEIGHT)m_nY=SCREEN_HEIGHT-1; if(ydelta||m_nYspeed==0) //record time of move m_nLastYMoveTime=time; } • 수평. 수직 이동을 조정
Changes to Main.cpp • declaration of the maximum number of object, foreground sprite,and object manager #define MAX_OBJECTS 32 //max number of objects in game CBmpFileReader background; //background image CBmpSpriteFileReader g_cSpriteImages; //sprite images CBmpSpriteFileReader g_cFrgndImages; //foreground images CObjectManager ObjectManager(MAX_OBJECTS); //object manager CClippedSprite *g_pSprite[NUM_SPRITES]; //sprites CViewPoint Viewpoint; //player viewpoint CRandom Random; //random number generator
Loading Sprites • which now loads six frames of plane animation from locations (1,1), (123,1) , (245,1), (367,1) ,(489,1), and (17,74) of the image stored in the bmpsprite file reader : g_cSpriteImages : BOOL LoadPlaneSprite(){ BOOL result=TRUE; result=result&&g_pSprite[PLANE_OBJECT]-> load(&g_cSpriteImages,0,1,1); result=result&&g_pSprite[PLANE_OBJECT]-> load(&g_cSpriteImages,1,123,1); result=result&&g_pSprite[PLANE_OBJECT]-> load(&g_cSpriteImages,2,245,1); result=result&&g_pSprite[PLANE_OBJECT]-> load(&g_cSpriteImages,3,367,1); result=result&&g_pSprite[PLANE_OBJECT]-> load(&g_cSpriteImages,4,489,1); result=result&&g_pSprite[PLANE_OBJECT]-> load(&g_cSpriteImages,5,17,74); return result; } //LoadPlaneSprite
LoadImages Functions BOOL LoadImages(){ //load graphics from files to surfaces //get the background image if(!background.load("bckgnd.bmp"))return FALSE; //read from file background.draw(lpBackground); //draw to background surface //set palettes in all surfaces if(!background.setpalette(lpPrimaryPalette))return FALSE; if(!background.setpalette(lpSecondaryPalette))return FALSE; //load the sprites... if(!g_cSpriteImages.load("sprites.bmp"))return FALSE; //...the plane g_pSprite[PLANE_OBJECT]=new CClippedSprite(6,121,72); if(!LoadPlaneSprite())return FALSE; //load plane images //...the crow g_pSprite[CROW_OBJECT]=new CClippedSprite(4,58,37); if(!LoadCrowSprite())return FALSE; //load crow images //...the foreground sprites if(!g_cFrgndImages.load("farm.bmp"))return FALSE; g_pSprite[FARM_OBJECT]=new CClippedSprite(1,640,162); g_pSprite[FIELD_OBJECT]=new CClippedSprite(1,640,162); if(!LoadFrgndSprites())return FALSE; //load foreground return TRUE; } //LoadImages
CreateObjects function • func tion CreateObjects now requests the object manager to create the objects for us. First, the farm and field objects are placed side by side so as to fill up the vir tual uni verse. The farm object is placed at (0,SCREEN_HEIGHT-1 ), and the field object is placed at (SCREEN_WIDTH,SCREEN_HEIGHT-1). void CreateObjects(){ ObjectManager.create(FARM_OBJECT,0,SCREEN_HEIGHT-1,0,0); ObjectManager.create(FIELD_OBJECT,SCREEN_WIDTH, SCREEN_HEIGHT-1,0,0); ObjectManager.create(CROW_OBJECT,400,100,-2,0); ObjectManager.create(CROW_OBJECT,420,80,-2,0); ObjectManager.create(CROW_OBJECT,430,120,-2,0); ObjectManager.set_current( ObjectManager.create(PLANE_OBJECT,320,271,-1,0)); ObjectManager.create(CROW_OBJECT,320,100,-1,0); ObjectManager.create(CROW_OBJECT,405,90,-1,0); ObjectManager.create(CROW_OBJECT,255,125,-1,0); } //CreateObjects
WinMain function int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow){ … Initialize code //init graphics for(int i=0; i<NUM_SPRITES; i++) //null out sprites g_pSprite[i]=NULL; BOOL OK=InitDirectDraw(hwnd);//initialize DirectDraw if(OK)OK=LoadImages(); //load images from disk if(!OK){ //bail out if initialization failed DestroyWindow(hwnd); return FALSE; } //start game timer Timer.start(); //create objects CreateObjects(); //message loop while(TRUE) if(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)){ if(!GetMessage(&msg,NULL,0,0))return msg.wParam; TranslateMessage(&msg); DispatchMessage(&msg); } else if(ActiveApp)ProcessFrame(); else WaitMessage(); } //WinMain
Process Frame and CompseFrame BOOL ComposeFrame(){ //compose a frame of animation Viewpoint.draw_background(lpBackground,lpSecondary, ObjectManager.speed()); //draw scrolling background ObjectManager.animate(lpSecondary); //draw objects return TRUE; } //ComposeFrame BOOL ProcessFrame(){ //process a frame of animation ComposeFrame(); //compose a frame in secondary surface return PageFlip(); //flip video memory surfaces } //ProcessFrame
Keyboard Handler • Handling response to the arrow key which is used to accelerate the plane either up,down, left or right BOOL keyboard_handler(WPARAM keystroke){ //keyboard handler BOOL result=FALSE; //return TRUE if game is to end switch(keystroke){ case VK_ESCAPE: result=TRUE; break; //exit game case VK_UP: ObjectManager.accelerate(0,-1); break; case VK_DOWN: ObjectManager.accelerate(0,1); break; case VK_LEFT: ObjectManager.accelerate(-1,0); break; case VK_RIGHT: ObjectManager.accelerate(1,0); break; default: break; } return result; } //keyboard_handler
Chanage to Keyboard Handler #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) #define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1) BOOL keyboard_handler(){ //keyboard handler BOOL result=FALSE; //return TRUE if game is to end if(KEYDOWN(VK_ESCAPE)){ result = TRUE; } else if(KEYDOWN(VK_UP)){ ObjectManager.accelerate(0,-1); } else if(KEYDOWN(VK_DOWN)){ ObjectManager.accelerate(0,1); } else if(KEYDOWN(VK_LEFT)){ ObjectManager.accelerate(-1,0); } else if(KEYDOWN(VK_RIGHT)){ ObjectManager.accelerate(1,0); } return result; } //keyboard_handler
기본 스프라이트 파일 클래스 class CBaseSprite{ //simplest sprite protected: LPDIRECTDRAWSURFACE *m_lpDDImage; //sprite image int m_nFrameCount; //how many frames used in sprite int m_nWidth,m_nHeight; //dimensions of sprite images void CreateSurfaces(); //create surfaces public: CBaseSprite(int frames,int w,int h); //constructor ~CBaseSprite(); //destructor BOOL load(CBmpSpriteFileReader *image,int frame, int x,int y); //load sprite image virtual void draw(int frame,int x,int y, LPDIRECTDRAWSURFACE destination); //draw sprite void Release(); //release direct draw surfaces BOOL Restore(); //restore surfaces int frame_count(); //return number of frames int height(); //return height int width(); //return width }; • CBmpFileReader 클래스의 인스턴스 선언 • 화면 해상도 설정
CBaseSprite 인스턴스의 생성 및 제거 • 스프라이트의 프레임 수 , 너비, 높이 기록 • 프레임 포인터 배열을 위한 공간 할당 및 초기화 • 스프라이트 각 프레임마다 표면 생성 및 초기화 CBaseSprite::CBaseSprite(int frames,int width,int height){ //settings m_nWidth=width; m_nHeight=height; //width and height m_nFrameCount=frames; //assign number of frames //create space for sprite surface pointers m_lpDDImage=new LPDIRECTDRAWSURFACE[frames]; for(int i=0; i<frames; i++)m_lpDDImage[i]=NULL; //create surfaces for sprite images CreateSurfaces(); } CBaseSprite::~CBaseSprite(){ //destructor //deallocate memory allocated in constructor delete[]m_lpDDImage; }
Sprite 프레임 로드 및 그리기 BOOL CBaseSprite::load(CBmpSpriteFileReader *buffer, int frame,int x,int y){ //grab sprite image from (x,y) in *buffer //and store in m_lpDDImage[frame] return buffer->draw(m_lpDDImage[frame], m_nWidth,m_nHeight,x,y); } void CBaseSprite::draw(int frame,int x,int y, LPDIRECTDRAWSURFACE dest){ dest->BltFast(x-m_nWidth/2,y-m_nHeight,m_lpDDImage[frame], NULL,DDBLTFAST_SRCCOLORKEY|DDBLTFAST_WAIT); }