2017年1月11日 星期三

[C/C++自製2D遊戲]#07 實現第一個Class -- [Sprite]

在本篇中,我們會實現2D遊戲引擎的核心物件---Sprite(圖精靈)物件。

本系列前面幾篇文章中,所使用到的程式碼均為標準C語言,
為了邁向物件導向(OOP)之路,從本篇開始我們會使用C++語言來實現物件的部分。
讀者若有一些C++實作OOP的經驗是最好的。

==================
STEP #1:

打開前節專案,新增Sprite.h與Sprite.cpp檔案。


打開Sprite.h,輸入以下宣告:

#ifndef SPRITE_H
#define SPRITE_H
using std::string;

class Sprite{
// public attributes
public:
 int x;
 int y;
 int w;
 int h;
 string filename;

// public methods
 Sprite(string _filename, int _x, int _y, int _w, int _h);
 void Draw();
};
#endif

打開Sprite.cpp,輸入以下程式:

#include <iostream>
#include <stdio.h>
#include "graphics.h"
#include "Sprite.h"

Sprite::Sprite(string _filename, int _x, int _y, int _w, int _h){
 x = _x;
 y = _y;
 w = _w;
 h = _h;
 filename = _filename;
}

void Sprite::Draw(){
 readimagefile(const_cast<char*>(filename.c_str()), x, y, x + w, y + h);
}

程式解說:

  1. 在.h檔中我們宣告了一個class名為Sprite。Sprite利用x,y參數來儲存他的位置;用w,h參數儲存他的寬高;filename字串儲存Sprite圖檔的位置。
  2. 在Sprite的建構函數中,傳入x,y,w,h及filename值用以初始化。
  3. Draw()方法用來繪製此Sprite。
  4. 由於readimagefile()所接受的檔名為(char *)形式,若直接用.c_str()方法得到的將是(const char *),編譯會失誤。因此我們只好使用禁忌魔法 const_cast<char *>()函數,硬是將const char*轉為char *。
字串的實現在C語言下,向來是個大問題。在正規的C語言教學中(不使用C++),字串的操作極有可能超出原本配置的記憶體大小。這個問題一旦發生,整個程式可能會當掉。
故在此處我們利用C++的字串類型sting來操作字串。
使用C++ string物件必須使用std命名空間的子類string:using std:string。
將string輸出至主控台則需匯入<iostream>,如此一來才能使用cout<<。

以C++的風格使用string是必修的基礎課程!即使你已經會C,C++的字串對你可能也是一個陌生的題目,但絕對會比直接用C以byte array來實現字串要方便。

==================
STEP #2:

打開main.cpp,修改如下

#include <stdio.h>
#include <conio.h>
#include "graphics.h"
#include <stdlib.h>
#include "Sprite.h"

int main(){
 
 Sprite sp("games.gif",0,0,64,64);
 initwindow(640,480,"This is my game");

 setactivepage(0);
 setvisualpage(1);
 setcolor(2);
 settextstyle(SANS_SERIF_FONT, HORIZ_DIR, 0);
 outtextxy(200,300,const_cast<char *>(sp.filename.c_str()));
 readimagefile("games.gif", 220, 120, 380, 280);
 swapbuffers();
 getch();
 cleardevice();
 swapbuffers();
 getch();

 //Observe the result after adding paging.
 for(int i; i<1000;i++){
  cleardevice();
  sp.x = sp.x + 1;
  sp.y = sp.y + 2;
  sp.Draw();
  swapbuffers();
  delay(16);
 }
}


  1. 以Sprite物件產生了一個實件sp並做初始化。
  2. 迴圈內直接去調整sp.x與sp.y值,這樣就形同移動了sp。
  3. 呼叫sp.Draw()將sp繪製於page上
按F5運行,可以看到搖桿向右下角移動過去。









2017年1月10日 星期二

[C/C++自製2D遊戲]#06 使用Double page避開閃爍

在傳統以graphics.h實現遊戲時,常常會看到double buffer這個詞彙,到底是什麼意思呢?
稍後我們就會具體使用程式碼來觀察現象,並使用double buffer來解決掉。

請先將前節<bgi>目錄夾複製一份,改名為<doubleBuffer>

==========
STEP #1:

修改前節程式碼,我們使用一個1000次的for迴圈,讓畫面中的搖桿每16ms向右移動1 pixel。

#include <stdio.h>
#include <conio.h>
#include "graphics.h"
#include <stdlib.h>

int main(){
 
 initwindow(640,480,"This is my game");

 setcolor(2);
 settextstyle(SANS_SERIF_FONT, HORIZ_DIR, 0);
 outtextxy(200,300,"hello world");
 readimagefile("games.gif", 220, 120, 380, 280);
 getch();
 cleardevice();
 getch();

 for(int i; i<1000;i++){
  cleardevice();
  readimagefile("games.gif", 220+i, 120, 380+i, 280);
  delay(16);
 }

}


  1. #19清屏
  2. #20行繪製"games.gif"
按下F5運行,連按兩次Enter後可看到畫面中的搖桿向右移動,但屏幕不停地閃爍。



閃爍的原因是因為:#19行執行完畢後,畫面確實地變成了全黑,而後再繪製上了搖桿。這個繪圖過程雖然快速,但還是會出現黑屏殘像,因此產生了閃爍。
解決的方式是使用setactivepage()與setvisualpage()兩則指令,做出切換page的動作。

==================
STEP #2
修改程式如下:

#include <stdio.h>
#include <conio.h>
#include "graphics.h"
#include <stdlib.h>

int main(){
 
 int curPage =0;

 initwindow(640,480,"This is my game");

 setactivepage(curPage);
 setvisualpage(curPage);
 setcolor(2);
 settextstyle(SANS_SERIF_FONT, HORIZ_DIR, 0);
 outtextxy(200,300,"hello world");
 readimagefile("games.gif", 220, 120, 380, 280);
 getch();
 cleardevice();
 getch();

 for(int i; i<1000;i++){
  curPage = (curPage + 1) % 2;
  setactivepage(curPage);
  cleardevice();
  readimagefile("games.gif", 220+i, 120, 380+i, 280);
  setvisualpage(curPage);
  delay(16);
 }
}


  1. #8宣告了一個整數變數curPage(current page),用以紀錄目前操作中的頁面號碼。這個page可視為傳統double buffer技巧中的buffer概念。
  2. #12,#13在初始化的過程中,先使用預設頁面0來做首次繪製。
  3. #23使用餘數除法(mod,後稱餘除法)。這條指令可讓0變1,1變0,做出切換的效果。
  4. #24切換到非顯示中的頁面,隨後接著清屏並繪製games.gif
  5. #27將非顯示頁切換為顯示頁
由於在清屏與繪製的過程中,我們是針對非顯示中的頁面,因此繪製的過程也就是圖像蝶家的過程玩家是看不到的。等待繪製完畢(疊加完畢)後再一次切換為顯示屏讓玩家看清楚。如此一來就不會讓玩家看到繪製的過程,也就不會閃爍了。

按F5運行遊戲,確認此次搖桿向右移動時不會有閃爍現象。



==============
STEP #3:

剛剛使用curPage去做顯示切換實在太麻煩了(還要用餘除法,實在很累捏~~),
所以接下來我們改用超好用的swapbuffer()指令來改寫上述程式。
由於在以前的年代graphics.h只支援雙buffer(也夠了),所以整個顯示的過程是由page0與page1分工合作所完成了。
我們設計的工作流程是這樣的:
  • 在任何時刻下,其中一個page作為繪製屏,另一page做為顯示屏。繪製顯示屏必定不為同一屏。
  • 當程式在繪製屏上繪製完畢時,直接下一則swapbuffers()指令將角色轉換。如此一來繪製結果立刻可輸出。
  • 由於新繪製圖已顯示,所以前次的顯示屏在swapbuffers()後角色轉為繪製屏,其內容已不需要,固可大膽重繪之。
改寫程式如下:

#include <stdio.h>
#include <conio.h>
#include "graphics.h"
#include <stdlib.h>

int main(){
 
 initwindow(640,480,"This is my game");

 setactivepage(0);
 setvisualpage(1);
 setcolor(2);
 settextstyle(SANS_SERIF_FONT, HORIZ_DIR, 0);
 outtextxy(200,300,"hello world");
 readimagefile("games.gif", 220, 120, 380, 280);
 swapbuffers();
 getch();
 cleardevice();
 swapbuffers();
 getch();

 for(int i; i<1000;i++){
  cleardevice();
  readimagefile("games.gif", 220+i, 120, 380+i, 280);
  swapbuffers();
  delay(16);
 }
}

完成後按F5運行,確認功能與上個STEP完全相同。

* winBGIm的說明文檔在此

2017年1月6日 星期五

[C/C++自製2D遊戲]#05 安裝graphics.h

在傳統的C語言教學中,有關圖形的部份都是使用<graphics.h>函式庫來進行的。
問題是:這個庫是非公規的庫,在20多年前DOS年代,他是由Turbo C及Borland C所自行提供的。
進入到Windows系統後,Visual Studio內是沒有自帶<graphics.h>的,
所以在這一課的開始,我們要安裝Colorado大學所開發的 <WinBGIm Graphics Library with Visual Studio 2005/2008>函式庫。
這套函式庫與Borland的BGI系列繪圖函式庫相容,因此我們可以使用他來體會20年前<先賢先烈>開發遊戲時的辛苦(大誤...^^)

請連至以下網址,下載BGI2008.zip

https://www.cs.colorado.edu/~main/bgi/visual/


解包後先放到桌面上即可。

==================
STEP #1:

開啟bgi資料夾,將bgi.sln拖曳到VC++ Express中,即可享受<裸奔>工程(大陸工程師界的詞彙,意指可讓小白們快速直接切入工程的檔案):


在<Header Files>上點右鍵,選擇<加入>-><現有項目>,匯入<graphics.h>檔案。
好!現在在案子內的C/C++程式碼就可以使用Borland相容的<graphics.h>囉~
請隨意將一個gif圖標複製到程式根目錄,取名為games.gif。稍後我們會利用程式碼在視窗中顯示他。


* 這套函式庫的說明書在此:
http://www.cs.colorado.edu/~main/bgi/doc/

======================
STEP #2:

在專案的Soucre Files夾中新增main.cpp檔案,撰寫程式如下:

#include <stdio.h>
#include <conio.h>
#include "graphics.h"

int main(){
 
 initwindow(640,480,"This is my game");
 setcolor(2);
 settextstyle(SANS_SERIF_FONT, HORIZ_DIR, 0);
 outtextxy(200,300,"hello world");
 readimagefile("games.gif", 220, 120, 380, 280);
 getch();
 cleardevice();
 getch();

}

程式說明:

  1. #7用一條指令就開出了一個<繪圖視窗>,大小為640x480,標題"This is my game"。
  2. #8設置文字顏色為系統2號色,完整的色號在此:
    http://www.cs.colorado.edu/~main/bgi/doc/setcolor.html
  3. #9行設置文字字型,參數詳見說明書
  4. #10行輸出文字,#11行輸出圖片。參數詳見說明書。
  5. #13行使用cleardevice清除整個螢幕。
按下F5運行,good,我們可以顯示圖片與文字了。
任意點擊一次鍵盤可以清除所有圖文,二次點擊鍵盤則可關閉程式。