<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>猴子靈藥 [Monkey Potion] &#187; 學習筆記</title>
	<atom:link href="http://blog.monkeypotion.net/category/gameprog/note/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.monkeypotion.net</link>
	<description>遊戲開發‧遊戲程式‧遊戲設計</description>
	<lastBuildDate>Mon, 06 Sep 2010 01:52:21 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>快快樂樂學遊戲Threading程式設計</title>
		<link>http://blog.monkeypotion.net/gameprog/note/learning-threading-in-game-programming</link>
		<comments>http://blog.monkeypotion.net/gameprog/note/learning-threading-in-game-programming#comments</comments>
		<pubDate>Thu, 26 Mar 2009 16:19:01 +0000</pubDate>
		<dc:creator>半路</dc:creator>
				<category><![CDATA[學習筆記]]></category>

		<guid isPermaLink="false">http://blog.monkeypotion.net/?p=149</guid>
		<description><![CDATA[這是我在近期內對於 Threading 主題的學習整理文。如果你對於多執行緒程式設計沒有半點概念的話，建議可以先從我之前寫的「多核多緒多樂趣」開始閱讀，然後再視個人需求取用以下各項資源。 所謂的「多執行緒」程式設計，或者可簡稱為「多緒」程式設計，在英文中有許多相關的專業技術名詞，例如 Threading、Multithread、Concurrency、Parallel、Multicore 與 Multiprocessor 等等，在搜尋資料時可以嘗試不同的關鍵字，往往可以找到不少意料之外的好東西。而其中最常見的總括性簡稱，應該就是 Threading 了。 基礎定義 既然要學習 Threading 程式設計的知識，首先要瞭解的當然是 Threading 的基礎概念： Thread：看看 Wikipedia 裡的定義，至少把最前頭那段 Thread 的基本定義，以及 Thread 與 Process 的不同之處搞懂。簡單來說，執行緒是用來執行電腦程式的執行環境；而多執行緒，就是能夠使程式系統同時執行多個不同程式碼區段的一種技術。 Thread Safety：雖然使用 Threading 技術能夠提升程式系統的執行效能，但伴隨而來的則是麻煩又難解的 Thread Safety 議題。大致上，我們可以使用 Re-entrancy、Mutual exclusion、Thread-local storage 以及 Atomic operations 這四種方法來達成安全使用多執行緒技術的目標。 教學文章 C++ Multithreading Tutorial @ paulbridger.net：質量非常不錯的基礎教學文章，從 Race Condition、Mutex、Deadlock 到 Condition Variables 等等重要的基本概念，都有清楚易懂的文章教學與程式碼範例。 Multithreading Tutorial @ Homepage [...]]]></description>
			<content:encoded><![CDATA[<p>這是我在近期內對於 Threading 主題的學習整理文。如果你對於多執行緒程式設計沒有半點概念的話，建議可以先從我之前寫的<a href="http://blog.monkeypotion.net/gameprog/note/multicore-multithread-and-multifun"><strong>「多核多緒多樂趣」</strong></a>開始閱讀，然後再視個人需求取用以下各項資源。</p>
<p>所謂的「多執行緒」程式設計，或者可簡稱為「多緒」程式設計，在英文中有許多相關的專業技術名詞，例如 Threading、Multithread、Concurrency、Parallel、Multicore 與 Multiprocessor 等等，在搜尋資料時可以嘗試不同的關鍵字，往往可以找到不少意料之外的好東西。而其中最常見的總括性簡稱，應該就是 Threading 了。</p>
<h4><u><strong>基礎定義</strong></u></h4>
<p>既然要學習 Threading 程式設計的知識，首先要瞭解的當然是 Threading 的基礎概念：</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Thread_(computer_science)">Thread</a>：看看 Wikipedia 裡的定義，至少把最前頭那段 Thread 的基本定義，以及 Thread 與 Process 的不同之處搞懂。簡單來說，執行緒是用來執行電腦程式的執行環境；而多執行緒，就是能夠使程式系統同時執行多個不同程式碼區段的一種技術。</li>
<li><a href="http://en.wikipedia.org/wiki/Thread-safe">Thread Safety</a>：雖然使用 Threading 技術能夠提升程式系統的執行效能，但伴隨而來的則是麻煩又難解的 Thread Safety 議題。大致上，我們可以使用 Re-entrancy、Mutual exclusion、Thread-local storage 以及 Atomic operations 這四種方法來達成安全使用多執行緒技術的目標。</li>
</ul>
<p><span id="more-149"></span></p>
<h4><u><strong>教學文章</strong></u></h4>
<ul>
<li><a href="http://paulbridger.net/multithreading_tutorial">C++ Multithreading Tutorial</a> @ paulbridger.net：質量非常不錯的基礎教學文章，從 Race Condition、Mutex、Deadlock 到 Condition Variables 等等重要的基本概念，都有清楚易懂的文章教學與程式碼範例。</li>
<li><a href="http://www.mario-konrad.ch/index.php?page=30100">Multithreading Tutorial</a> @ Homepage of Mario Konrad：網站裡分成基礎、中間與進階三種等級的教學文章，可以看看 Socket 網路程式與 Producer-Consumer 的程式碼，應該會更加瞭解多執行緒技術的可運用之處。</li>
</ul>
<h4><u><strong>跨平台函式庫</strong></u></h4>
<ul>
<li><a href="http://www.boost.org/doc/libs/1_38_0/doc/html/thread.html">Boost.Thread</a>（連結為 1.38 版）：簡單易用且功能完備的跨平台函式庫。如果沒有使用過 Boost C++ Libraries，可以參照<a href="http://blog.monkeypotion.net/gameprog/note/first-touch-of-boost-cpp-libraries">這篇文章</a>的步驟來建置 Boost.Thread 函式庫。</li>
<li><a href="http://threadpool.sourceforge.net/">threadpool</a>：以 Boost.Thread 為基礎，實做出一個非常好用的 <a href="http://en.wikipedia.org/wiki/Thread_pool_pattern">Thread Pool</a> 設計模式。</li>
</ul>
<h4><u><strong>名家專區</strong></u></h4>
<ul>
<li><a href="http://software.intel.com/en-us/multi-core/">Intel: Parallel Programming and Multi-Core</a>：因為 Intel 的本業是靠 CPU 吃飯的，所以自然相當注重多執行緒程式設計的議題，在這裡可以挖到不少質量兼具的優秀文章。</li>
<li><a href="http://msdn.microsoft.com/zh-tw/concurrency/default(en-us).aspx">MSDN: Parallel Computing</a>：MSDN 中的平行計算處理專區，充滿文章、影片以及部落格等相關資源。</li>
<li><a href="http://www.devx.com/SpecialReports/Door/40893">DevX.com: Move to the Future with Multicore Code</a>：有幾篇不錯的概念性文章，另外還有介紹如何使用 .NET 平台的 Task Parallel Library。</li>
<li><a href="http://herbsutter.wordpress.com/">Sutter&#8217;s Mill</a>：Herb Sutter 大師的個人部落格。目前在 DDJ 雜誌上，有他每個月固定連載的 Effective Concurrency 專欄文章，適合進階高手服用。</li>
</ul>
<h4><u><strong>投影片</strong></u></h4>
<p>微軟於 <a href="http://www.xnagamefest.com/presentations07.htm">2007</a> 年與 <a href="http://www.xnagamefest.com/presentations08.htm">2008</a> 時所舉辦的 Gamefest 開發者會議，其中有許多關於 Threading 的演講主題，內容非常豐富多樣且紮實，而且大部分課程都有投影片與錄音檔可供下載：</p>
<ul>
<li><a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=D6940B55-B805-46B5-B683-B8A2FE9B3D00&#038;displaylang=en">Multi-Threaded Rendering for Games</a></li>
<li><a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=72EFB35C-0C1B-47A2-AAB8-68CA841364F3&#038;displaylang=en">A Detailed Overview of Xbox 360 Direct3D Synchronization and Multithreading</a></li>
<li><a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=E410716F-12BF-4E8F-AC41-97B4440C3B90&#038;displaylang=en">Introduction to the Direct3D 11 Graphics Pipeline</a></li>
<li><a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=EE4C7B70-1606-45E8-8027-50AB9A2F646F&#038;displaylang=en">Practical Parallel Rendering with DirectX 9 and 10, Windows PC Command Buffer Recording</a></li>
<li><a href="http://download.microsoft.com/download/c/f/8/cf8d0552-5e8c-4501-a52e-0986a9295821/Multicore%20Programming%20Two%20Years%20Later.zip">Multicore Programming, Two Years Later</a></li>
<li><a href="http://download.microsoft.com/download/8/0/4/804e5b2b-1308-4584-9644-7ccb9b52bacd/Magic%20and%20Technology%20-%20Migrating%20from%20One%20to%20Many%20Cores.zip">Magic and Technology: Migrating from One to Many Cores in Shadowrun</a></li>
<p>另外這篇是微軟於 GDC 2008 中開設的講座：</p>
<li><a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=A36FE736-5FE7-4E08-84CF-ACCF801538EB&#038;displaylang=en">GDC 2008: Getting More From Multicore</a></li>
</ul>
<h4><u><strong>推薦文章</strong></u></h4>
<ul>
<li><a href="http://software.intel.com/en-us/articles/threading-basics-for-games/">Threading Basics for Games</a>：概略簡介了遊戲所使用的基礎多緒程式架構。</li>
<li><a href="http://www.intel.com/cd/ids/developer/asmo-na/eng/dc/games/331359.htm">Threading the OGRE3D Render System</a>：這篇文章闡述了三種能夠使 OGRE 繪圖引擎充分利用多緒能力的技術，並完整實作出其中一種方法，附源碼可供下載。</li>
<li><a href="http://software.intel.com/en-us/articles/multithreading-the-rendering-pipeline-for-3d-model-animation/">Multithreading the Rendering Pipeline for 3D Model Animation</a>：相對於 GPU 的強大運算能力，這篇文章試圖藉由多核心 CPU 的力量來達成 Skinning Animation 的功能。雖然目前這個作法的實用性不高，但是文章中的設計架構仍然非常值得參考學習，也有完整源碼可下載。</li>
<li><a href="http://www.gamasutra.com/view/feature/3941/sponsored_feature_designing_the_.php">Designing the Framework of a Parallel Game Engine</a>：相當夠份量而且完整的好文章！著重於設計概念與流程架構，沒有提供任何程式碼範例。</li>
</ul>
<h4><u><strong>書籍文章</strong></u></h4>
<p>關於書籍中的文章，我推薦《Game Programming Gems 7》中的「Design and Implementation of a Multi-Platform Threading Engine」與「Multithread Job and Dependency System」這兩篇文章。前者實作出了一個可跨平台的 Threading 引擎架構，後者則著重於更加複雜的任務相依系統上，兩篇文章各有擅場，而且都有完整的程式源碼可供讀者仔細研究！</p>
<p>如果有其他關於遊戲 Threading 程式設計的資源，隨時歡迎各位的回應與補充喔～ ：D</p>
<img src="http://blog.monkeypotion.net/?ak_action=api_record_view&id=149&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.monkeypotion.net/gameprog/note/learning-threading-in-game-programming/feed</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>登泰山而小天下：Boost C++ Libraries 初體驗</title>
		<link>http://blog.monkeypotion.net/gameprog/note/first-touch-of-boost-cpp-libraries</link>
		<comments>http://blog.monkeypotion.net/gameprog/note/first-touch-of-boost-cpp-libraries#comments</comments>
		<pubDate>Thu, 09 Oct 2008 16:08:11 +0000</pubDate>
		<dc:creator>半路</dc:creator>
				<category><![CDATA[學習筆記]]></category>

		<guid isPermaLink="false">http://blog.monkeypotion.net/?p=605</guid>
		<description><![CDATA[說來有些慚愧，一直以 C++ 語言做為謀生工具的我，在使用了多年的 C++ 程式語言之後，最近終於與聞名遐邇的開源專案 Boost C++ Libraries（簡稱為 Boost）進行了第一次的親密接觸。在本文裡，將參照 Boost 官網的「Getting Started on Windows」文章，於 Windows 作業系統以及 Visual Studio 2005 程式編譯器的環境下，詳細介紹安裝及建置 Boost 的步驟，並且在最後使用 Boost.Thread 函式庫做為測試範例。 從前就常聽到其他人談論著 Boost 的優點，但是只要知道其中由上千個標頭檔以及數十個函式庫所組成，而且還需要以繁複的步驟手動建立函式庫的 .lib 檔案，很容易就會使初學者望而卻步。對於用慣了程式整合開發環境以及現成函式庫的我來說，Boost 簡直就像是一座美麗動人但又戒備森嚴的堡壘般令人不敢輕易接近。多年以後的現在，總算是鼓起了勇氣重新面對 Boost。花了些時間閱讀文件，然後按照指示一步步完成安裝建置之後，發現原來這些步驟比起從前已經簡化許多，函式庫的使用也不如想像中的困難！ One of the reasons for boost&#8217;s success has been the cross-pollination of ideas between diverse library projects. Boost 是由一群功能獨立的函式庫所組合而成的集合體，其中涵蓋了許多熱門而經常使用的函式庫，以及比較冷門的特殊功能作用函式庫。所有的函式庫都包含在名為 boost 的 namespace 中，不僅能夠使各函式庫維持一致的使用風格，同時也得以避免命名衝突的問題。如官網中的 FAQ [...]]]></description>
			<content:encoded><![CDATA[<p>說來有些慚愧，一直以 C++ 語言做為謀生工具的我，在使用了多年的 C++ 程式語言之後，最近終於與聞名遐邇的開源專案 <a href="http://www.boost.org/"><strong>Boost C++ Libraries</strong></a>（簡稱為 <strong>Boost</strong>）進行了第一次的親密接觸。在本文裡，將參照 Boost 官網的<a href="http://www.boost.org/doc/libs/1_36_0/more/getting_started/windows.html">「Getting Started on Windows」</a>文章，於 Windows 作業系統以及 Visual Studio 2005 程式編譯器的環境下，詳細介紹安裝及建置 Boost 的步驟，並且在最後使用 Boost.Thread 函式庫做為測試範例。</p>
<p>從前就常聽到其他人談論著 Boost 的優點，但是只要知道其中由上千個標頭檔以及數十個函式庫所組成，而且還需要以繁複的步驟手動建立函式庫的 .lib 檔案，很容易就會使初學者望而卻步。對於用慣了程式整合開發環境以及現成函式庫的我來說，Boost 簡直就像是一座美麗動人但又戒備森嚴的堡壘般令人不敢輕易接近。多年以後的現在，總算是鼓起了勇氣重新面對 Boost。花了些時間閱讀文件，然後按照指示一步步完成安裝建置之後，發現原來這些步驟比起從前已經簡化許多，函式庫的使用也不如想像中的困難！</p>
<blockquote><p>
One of the reasons for boost&#8217;s success has been the cross-pollination of ideas between diverse library projects.
</p></blockquote>
<p><strong>Boost 是由一群功能獨立的函式庫所組合而成的集合體</strong>，其中涵蓋了許多熱門而經常使用的函式庫，以及比較冷門的特殊功能作用函式庫。所有的函式庫都包含在名為 boost 的 namespace 中，不僅能夠使各函式庫維持一致的使用風格，同時也得以避免命名衝突的問題。如官網中的 <a href="http://www.boost.org/users/faq.html">FAQ</a> 所述，<strong>Boost 的成功正是由於不同功能函式庫之間的「交叉授粉」(cross-pollination) 作用所致</strong>；雖然其中多數函式庫都能夠分開獨立使用，但正是由於函式庫之間相互支援的功能以及介面，才使得 Boost 的開發者社群與使用者社群能夠蓬勃發展且欣欣向榮。</p>
<p><span id="more-605"></span></p>
<p>而經過千錘百鍊的 Boost，也獲得了 C++ Standards Committee 的認可，其中有部分的函式庫例如 Array、Bind、Tuple 與 Unordered 等等皆已納入 <a href="http://en.wikipedia.org/wiki/Technical_Report_1">C++ Technical Report 1</a>（簡稱為 TR1）的範疇之中，很快地將會在眾所矚目的 C++ 語言新版本中，正式將這些函式庫納入標準。</p>
<p>事不宜遲，讓我們開始進入 Boost 的奇妙領域吧！</p>
<p>首先，於官網下載 Boost C++ Libraries 檔案包，目前最新的版本是於 2008 年 8 月 12 日發佈的 1.36 版。官網提供了多種不同壓縮格式的檔案包，建議下載以 <a href="http://www.7-zip.org/">7-Zip</a> 包裝的 <a href="http://downloads.sourceforge.net/boost/boost_1_36_0.7z?modtime=1218749377&#038;big_mirror=0">boost_1_36_0.7z</a> 版本，檔案大小僅有 ZIP 版本的 50% 而已。下載完成後，直接執行檔案解壓縮，就會生成一個新的目錄 <strong>boost_1_36_0</strong>。</p>
<p>「奇怪，怎麼看來看去都只有 .hpp 的標頭檔？那些 .cpp 跑到哪裡去了？」Boost 與其他開源專案最關鍵的不同之處，就是在於<strong>多數 Boost 中的函式庫，都是以「僅有標頭檔」(header-only) 的形式存在</strong>；也就是說，在使用多數的 Boost 函式庫時，只需要 #include 相對應的 .hpp 檔案，不需要額外連結 .lib 檔案就能夠快快樂樂地撰寫程式。Boost 之所以能夠達到這種令人讚嘆的神妙境界，主要歸功於 <a href="http://en.wikipedia.org/wiki/Template_metaprogramming">Template Metaprogramming</a> 技術的強大威力，因此使 Boost 能夠在編譯時期動態生成所需的程式碼。</p>
<p>雖然多數 Boost 函式庫可以直接使用標頭檔，但是某些 Boost 函式庫例如 FileSystem、IOStream 與 Thread 等等，就必須要建置並且連結 .lib 檔案才能夠使用。而在下載回來的資料包之中，只有包含原始碼檔案，所以我們需要自己進行 .lib 檔案的建置工作。為了建置所需的函式庫，必須使用 bjam 這個工具。再次於官網下載 <a href="http://downloads.sourceforge.net/boost/boost-jam-3.1.16-1-ntx86.zip?modtime=1196632824&#038;big_mirror=0">boost-jam-3.1.16-1-ntx86.zip</a>，解壓縮後將其中的 bjam.exe 移至 boost_1_36_0 目錄下，接著開啟「Visual Studio 2005 命令提示字元」，更換目錄至 boost_1_36_0 後，執行以下指令然後耐心等待（依電腦配備等級不同，約需要數十分鐘的建置時間）：</p>
<pre name="code" class="lua">
bjam --build-dir=".\build" --toolset=msvc stage
</pre>
<p>其中 &#8211;build-dir 參數指定的是建置函式庫的輸出目錄，在此設定為 .\build，就是在 boost_1_36_0 目錄下產生一個新的 build 目錄；而 &#8211;toolset 則是指定用來建置函式庫的工具，設定為 msvc 也就是使用 Microsoft Visual Studio 的編譯器執行建置；最後的 stage 參數則是一個特殊的建置目標，在函式庫建置完畢之後，會將所有的 .lib 與 .dll 檔案複製一份到 boost_1_36_0\stage\lib\ 目錄當中。</p>
<p>在上述的預設指令中，只會建置出 Release 組態的函式庫，如果想要建置出包含 Debug 組態在內的所有函式庫組態，可以在指令中加上 &#8211;build-type=complete 參數；而如果不想花費許多時間建立所有的函式庫，只想要建立其中某個函式庫，就可以使用 &#8211;with 參數後面接上函式庫的名稱。以單獨建置 Boost.Thread 為例：</p>
<pre name="code" class="lua">
bjam --with-thread --build-dir=".\build" --build-type=complete --toolset=msvc stage
</pre>
<p>如果不確定有哪些函式庫可以使用 bjam 建置，可以使用 &#8211;show-libraries 參數列出所有可建置的函式庫：</p>
<pre name="code" class="lua">
bjam --show-libraries
</pre>
<p>接下來，就創建一個專案正式和 Boost 說聲 Hello World 吧！</p>
<p>由於範例中所使用的 Boost.Thread 函式庫，相依於 Boost.DateTime 處理與時間相關的功能，所以首先要按照之前的步驟將 Boost.DateTime 建置起來：</p>
<pre name="code" class="lua">
bjam --with-date_time --build-dir=".\build" --build-type=complete --toolset=msvc stage
</pre>
<p>然後在 Visual Studio 2005 中，創建一個「Win32 主控台應用程式」的空專案，新增一個 .cpp 檔案並且填入以下程式碼：</p>
<pre name="code" class="cpp">
#include < boost/thread/thread.hpp >
#include < iostream >

void helloworld()
{
    std::cout << "Hello World!" << std::endl;
}

void main()
{
    boost::thread thrd(&#038;helloworld);
    thrd.join();
}
</pre>
<p>將專案屬性中的「其他 Include 目錄」設定為 boost_1_36_0 所在的目錄，「其他程式庫目錄」則設定為 boost_1_36_0\stage\lib；在設定時不需要明確指定要連結的 .lib 檔案名稱，Boost 會自動連結 (Automatic linking) 到正確的函式庫版本。以上步驟完成後，按下建置方案然後執行，第一個 Boost.Thread 的 Hello World 程式就誕生囉！</p>
<p>另外，如果將 Boost.Thread 搭配 Boost.Bind 函式庫一起服用，就能夠更便利地傳入 Thread Function 所需的參數值。因為 Boost.Bind 不需連結 .lib 檔案，所以只要 #include 指定 boost/bind.hpp 檔案就可以立即使用：</p>
<pre name="code" class="cpp">
#include < boost/thread/thread.hpp >
#include < boost/bind.hpp >
#include < iostream >

void helloworld(const char* who)
{
    std::cout << who << ": "Hello World!"" << std::endl;
}

void main()
{
    boost::thread thrd(boost::bind(&#038;helloworld, "MonkeyPotion"));
    thrd.join();
}
</pre>
<p>開始使用 Boost，就是這麼輕鬆簡單！</p>
<p>為了取得更多關於 Boost 的深入知識，除了官方文件以外，我也嘗試著在網路上搜尋相關的教學文章與使用心得。結果一如預期，網路上關於 Boost 的中文資訊仍然相當稀少，而其中繁體中文的資訊更是少之又少。<a href="http://www.stlchina.org/twiki/bin/view.pl/Main/BoostChina">「Boost 中文站」</a>是我目前找到內容最為豐富的 Boost 主題網站，其中的<a href="http://www.stlchina.org/twiki/bin/view.pl/Main/BoostThread">「C++ Boost Thread 编程指南」</a>寫得相當出色，是一篇值得仔細閱讀的好文章。</p>
<p>目前為止，我僅僅接觸了這座龐大城堡中的一項 Boost.Thread 函式庫，就已經深深感受到 Boost C++ Libraries 的驚人威力。<strong>登上巨人的肩膀，才會知道世界有多遼闊，而自己又有多渺小。</strong>從現在開始，我會抱持著滿滿的學習熱誠與好奇心，努力探索 Boost 領域的知識，請幫我推薦些實用有趣的 Boost 函式庫吧！</p>
<img src="http://blog.monkeypotion.net/?ak_action=api_record_view&id=605&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.monkeypotion.net/gameprog/note/first-touch-of-boost-cpp-libraries/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>初探Nintendo DS程式開發與設計（四）：做個拼貼藝術大師</title>
		<link>http://blog.monkeypotion.net/gameprog/note/nds-dev-tiled-background</link>
		<comments>http://blog.monkeypotion.net/gameprog/note/nds-dev-tiled-background#comments</comments>
		<pubDate>Fri, 04 Jul 2008 16:07:17 +0000</pubDate>
		<dc:creator>半路</dc:creator>
				<category><![CDATA[學習筆記]]></category>

		<guid isPermaLink="false">http://blog.monkeypotion.net/?p=151</guid>
		<description><![CDATA[由之前一系列的三篇文章中，已經一步步地瞭解了如何在 NDS 平台上使用 Framebuffer 背景模式、Bitmap 背景模式，以及 Sprite 物件功能，接著本篇將再次深入與遊戲製作息息相關的背景顯示功能，介紹在真實的遊戲程式應用中，更加實用的圖塊背景 (Tiled Background) 模式。 前情提要： 初探Nintendo DS程式開發與設計（一） 初探Nintendo DS程式開發與設計（二） 初探Nintendo DS程式開發與設計（三）：二維世界小精靈 由於硬體架構上的先天限制，在 NDS 平台中，沒有如同個人電腦平台上的全彩 (32 bits) 或高彩 (24 bits) 顯示模式可以使用；在這個小小的掌上世界中，所有的繪圖物件只能夠使用 16 bits、8 bits 或 4 bits 的色彩顯示模式。而如前篇文章的內容所提，Sprite 物件能夠使用 16 色 (4 bits) 與 256 色 (8 bits) 兩種模式，對於背景圖件來說亦然如此。在 Sprite 的功能中，可以選擇使用 OBJCOLOR_16 或 OBJCOLOR_256 這兩項色彩模式，而在背景圖件中，也有相對應於 16 色與 256 色模式的 [...]]]></description>
			<content:encoded><![CDATA[<p>由之前一系列的三篇文章中，已經一步步地瞭解了如何在 NDS 平台上使用 Framebuffer 背景模式、Bitmap 背景模式，以及 Sprite 物件功能，接著本篇將再次深入與遊戲製作息息相關的背景顯示功能，介紹在真實的遊戲程式應用中，更加實用的<strong>圖塊背景 (Tiled Background) 模式</strong>。</p>
<blockquote><p>
<strong>前情提要</strong>：</p>
<ul>
<li><a href="http://blog.monkeypotion.net/gameprog/note/nds-dev-part-one">初探Nintendo DS程式開發與設計（一）</a></li>
<li><a href="http://blog.monkeypotion.net/gameprog/note/nds-dev-part-two">初探Nintendo DS程式開發與設計（二）</a></li>
<li><a href="http://blog.monkeypotion.net/gameprog/note/nds-dev-sprite">初探Nintendo DS程式開發與設計（三）：二維世界小精靈</a></li>
</ul>
</blockquote>
<p>由於硬體架構上的先天限制，在 NDS 平台中，沒有如同個人電腦平台上的全彩 (32 bits) 或高彩 (24 bits) 顯示模式可以使用；在這個小小的掌上世界中，所有的繪圖物件只能夠使用 16 bits、8 bits 或 4 bits 的色彩顯示模式。而如前篇文章的內容所提，Sprite 物件能夠使用 16 色 (4 bits) 與 256 色 (8 bits) 兩種模式，對於背景圖件來說亦然如此。在 Sprite 的功能中，可以選擇使用 OBJCOLOR_16 或 OBJCOLOR_256 這兩項色彩模式，而在背景圖件中，也有相對應於 16 色與 256 色模式的 BG_COLOR_16 與 BG_COLOR_256 定義。</p>
<p>瞭解了色彩模式的種類之後，就可以開始進入<strong>圖塊 (Tile)</strong> 的世界了。很久很久以前，在那個電腦記憶體仍然十分珍貴而稀少的年代裡，多數的家用遊樂器主機，都只配備著非常有限的記憶體容量。而對於掌上型主機來說，記憶體的限制則更為顯著。因此，我們無法使用多張高解析度的點陣圖 (Bitmap) 做為遊戲中的背景圖片，所以在 NDS 上存在著一種特別的繪圖顯示模式，能夠以一組相同大小的圖塊，拼貼組合成為完整的遊戲背景圖片。因此，遊戲開發者就可以利用類似磁磚拼貼的方式，由有限數量的小型圖塊，組合出變化多端的大型圖片。</p>
<p><span id="more-151"></span></p>
<blockquote><p>
在 NDS 平台上，Tile 是指一個 8&#215;8 大小，共計由 64 個像素點所組成的圖塊。
</p></blockquote>
<p><img src="http://blog.monkeypotion.net/wp-content/uploads/2008/07/tileset.jpg" alt="tileset" title="tileset" width="205" height="205" class="alignright size-full wp-image-164" />以一張 32&#215;32 大小的點陣圖為例，當程式設計者將原先存放於卡匣中的點陣圖片載入至 NDS 的記憶體中時，在硬體系統的內部會將圖片以 8&#215;8 的大小，依照橫列的順序依序讀取進來。因此，如圖所示，一張 32&#215;32 大小的點陣圖片，就會被分為由 1 至 16 共計 16 個圖塊。</p>
<p>圖塊背景模式的顯示原理，其實非常類似於圖片的<strong>調色盤 (Palette) 顯示模式</strong>原理。首先將相等大小的圖塊載入繪圖記憶體中，然後在背景的顯示區域中，再指定一個索引值 (Index) 至指定的圖塊編號；如此一來，NDS 的繪圖引擎，就能夠依據索引值找出相對應的圖塊資料以做為繪圖顯示之用。而這些索引值資料，則經常被稱為 Map 或者 Tilemap。</p>
<p>請仔細地端詳這張精美的 <a href="http://www.dev-scene.com/images/5/57/Nds_2D_background_memory.png"><strong>2D Background Memory</strong></a> 圖片，其中的 <strong>Graphics Base（也可稱為 Char Base）就是存放圖塊資料的地方，而 Map Base 則是存放背景圖件索引值的地方</strong>。Map Base 的每一塊 Base 大小為 0x800h (2 KByte)；而 Graphics Base 的每一塊 Base 則佔據著 0x4000h (16 KByte) 的記憶體空間。但是請注意，在圖片上顯示出的 Graphics Base 與 Map Base，只是邏輯觀念上的分野，實際上兩者皆位於相同的記憶體位址 0&#215;06000000 之中，只是依據程式設計者所做的設定，而使 NDS 系統對記憶體的內容物做出適當的解譯。</p>
<p>為了使 CPU 存取記憶體資料的速度加快，在大多數的電腦硬體架構中，都存在著 <a href="http://en.wikipedia.org/wiki/Data_structure_alignment">Memory Alignment</a> 的規則與限制，當然 NDS 平台也不例外。因此在圖塊背景模式中，程式設計者<strong>不能夠隨性地將資料填入任意的記憶體位址中</strong>，而是<strong>必須將圖塊資料與索引資料對齊 (align) 硬體架構中的邏輯邊界</strong>。但是這些在 Base 與 Base 之間的分界，完全只是邏輯性的分野，並沒有任何內建的機制能夠確保這些記憶體位址上的資料不會相互重疊，因此程式設計者必須<strong>負責</strong>將資料寫入適當的記憶體位址，並且確保它們<strong>不會彼此覆寫</strong>。</p>
<p>至此，我們知道為了顯示圖塊背景，需要設定兩種資料：</p>
<ul>
<li><strong>圖塊資料 (Graphics)</strong>：由點陣圖片分解而成 8&#215;8 大小的像素點資料。</li>
<li><strong>索引資料 (Map)</strong>：用來索引至圖塊資料的數值。</li>
</ul>
<p>而為了能夠迅速存取 Map Base 與 Graphics Base 的記憶體位址，所以在 libnds 中已經為 Main Engine 預先定義了方便使用的 #define 值：</p>
<pre name="code" class="cpp">
#define BG_MAP_RAM(base)        (((base)*0x800) + 0x06000000)
#define BG_TILE_RAM(base)        (((base)*0x4000) + 0x06000000)
</pre>
<p>舉例來說，如果要將索引資料寫入第 0 個 Base，並且將圖塊資料寫入第 1 個 Base 中，可以使用以下的方法：</p>
<pre name="code" class="cpp">
// Get Map Base 0
u16* piMapMemory = (u16*)BG_MAP_RAM(0);
// Copy data to Map Base 0
dmaCopy(mapData, piMapMemory, mapDataLen);

// Get Graphics Base 1
u16* piTileMemory = (u16*)BG_TILE_RAM(1);
// Copy data to Graphics Base 1
dmaCopy(backgroundTiles, piTileMemory, backgroundTilesLen);
</pre>
<p>接著，再於背景圖件的設定中，指定所使用的 Map Base 與 Tile Base 編號，就能夠順利地顯示背景圖片了：</p>
<pre name="code" class="cpp">
// 背景圖層0：使用 32x32 個 Map，256 色模式，以及 Map Base 0 與 Tile Base 1 的資料
BG0_CR = BG_32x32 | BG_COLOR_256 | BG_MAP_BASE(0) | BG_TILE_BASE(1);
</pre>
<p>在 NDS 的程式設計領域中，有一個非常重要的觀念，就是不論是 Bitmap 資料、Tile 資料、Map 資料或者 3D 資料，<strong>同樣都存在於相同的記憶體區段之中</strong>，NDS 本身並不知道如何詮釋這些記憶體中存在的資料，因此程式設計者需要藉由 videoSetMode() 函式以及其他相關的設定，才能夠使 NDS 對記憶體中的資料做出正確的解譯。</p>
<p>還記得在前篇文章中，有提到 Sprite 系統最多可擁有 1024 個不同的 Tile 數嗎？因為圖塊的 Map 有 8 bits 與 16 bits 兩種形式，在 8 bits 模式下，最多只能索引至 2 的 8 次方，也就是 256 個不同的 Tile；而在 16 bits 模式下，只有<strong>其中的 10 個 bits</strong> 會拿來做為索引值的用途，最多只能<strong>索引至 1024 個不同的 Tile</strong>，所以這也就成為了 Sprite 所能使用的圖塊上限值。另外，16 bits 的 Map 還能夠提供調色盤以及圖塊鏡射 (Flip) 的功能。</p>
<p>在本文的範例程式中，將會載入 4 張 32&#215;32 大小的圖片，用來組合出數種不同的背景圖片樣式。由前述的圖塊定義可知，32&#215;32 大小的圖片會被分解成為 16 個圖塊，其中第一張圖片的圖塊索引編號為 0、1、2 … 至 15 ，第二張圖片的編號則從 16 至 31，其他則以此類推：</p>
<ul>
<li>圖片編號 0：由圖塊 0、1、2 …… 至 15。</li>
<li>圖片編號 1：由圖塊 16、17、18 …… 至 31。</li>
<li>圖片編號 2：由圖塊 32、33、34 …… 至 47。</li>
<li>圖片編號 3：由圖塊 48、49、50 …… 至 63。</li>
</ul>
<p>接著，可以利用字元陣列的方式，定義出背景圖片的樣式：</p>
<pre name="code" class="cpp">
static char g_acMap1[] =
{
	2,2,2,0,1,1,0,0,
	2,2,2,0,1,1,0,0,
	2,2,2,0,0,0,0,0,
	0,0,0,0,0,3,3,3,
	0,1,1,0,0,3,3,3,
	0,1,1,0,0,3,3,3,
};
</pre>
<p>要注意的是，這個陣列中的編號並不是<strong>圖塊編號</strong>，而是用來表示螢幕上所需顯示的<strong>圖片編號</strong>；在遊戲的背景中，填入 0 的位置顯示第一張圖片，1 顯示第二張圖片，2 顯示第三張圖片，3 則顯示第四張圖片。再來就是利用前述的 libnds 定義，將點陣圖片的資料複製至 BG_TILE_RAM(1) 的位址上，並且將 g_acMap1 的資料與 BG_MAP_RAM(0) 傳入 BuildBG() 函式中進行處理：</p>
<pre name="code" class="cpp">
// Load graphics data
dmaCopy(backgroundTiles, (u16*)BG_TILE_RAM(1), backgroundTilesLen);
dmaCopy(backgroundPal, BG_PALETTE, backgroundPalLen);

// Build up map data
BuildBG((u16*)BG_MAP_RAM(0), g_acMap1);
</pre>
<p>NDS 的螢幕尺寸為 256&#215;192 個像素點，以 32&#215;32 大小的圖片來說，在單一螢幕上總共能夠容納 8&#215;6 張圖片，也就是每一列有 8 張圖片，每一行則有 6 張圖片。所以，需要在 BuildBG() 函式中，對每一列與每一行的圖片進行設定：</p>
<pre name="code" class="cpp">
void BuildBG(u16* piMemory, char* pcMap)
{
	for (int y = 0; y < TILESET_HEIGHT; y++) {  // TILESET_HEIGHT = 6
		for (int x = 0; x < TILESET_WIDTH; x++) {  // TILESET_WIDTH = 8
			char cTileIndex = *pcMap++;
			BuildTileset(piMemory, x, y, cTileIndex);
		}
	}
}
</pre>
<p>而在 BuildTileset() 函式中，則需要在先前傳入的 BG_MAP_RAM(0) 位址上，設定圖塊的索引資料：</p>
<pre name="code" class="cpp">
void BuildTileset(u16* piMemory, int iX, int iY, char cTileIndex)
{
	char cValue = cTileIndex * TILES_PER_TILESET;
	int iTileX = iX * TILES_NUM_WIDTH;
	int iTileY = iY * TILES_NUM_HEIGHT;

	piMemory[iTileX + iTileY * BG_WIDTH] = cValue;
	piMemory[iTileX + iTileY * BG_WIDTH + 1] = cValue + 1;
	piMemory[iTileX + iTileY * BG_WIDTH + 2] = cValue + 2;
	piMemory[iTileX + iTileY * BG_WIDTH + 3] = cValue + 3;

	piMemory[iTileX + (iTileY + 1) * BG_WIDTH] = cValue + 4;
	piMemory[iTileX + (iTileY + 1) * BG_WIDTH + 1] = cValue + 5;
	piMemory[iTileX + (iTileY + 1) * BG_WIDTH + 2] = cValue + 6;
	piMemory[iTileX + (iTileY + 1) * BG_WIDTH + 3] = cValue + 7;

	piMemory[iTileX + (iTileY + 2) * BG_WIDTH] = cValue + 8;
	piMemory[iTileX + (iTileY + 2) * BG_WIDTH + 1] = cValue + 9;
	piMemory[iTileX + (iTileY + 2) * BG_WIDTH + 2] = cValue + 10;
	piMemory[iTileX + (iTileY + 2) * BG_WIDTH + 3] = cValue + 11;

	piMemory[iTileX + (iTileY + 3) * BG_WIDTH] = cValue + 12;
	piMemory[iTileX + (iTileY + 3) * BG_WIDTH + 1] = cValue + 13;
	piMemory[iTileX + (iTileY + 3) * BG_WIDTH + 2] = cValue + 14;
	piMemory[iTileX + (iTileY + 3) * BG_WIDTH + 3] = cValue + 15;
}
</pre>
<p><img src="http://blog.monkeypotion.net/wp-content/uploads/2008/07/nds-dev-tiled-background.jpg" alt="nds-dev-tiled-background" title="nds-dev-tiled-background" width="185" height="307" class="alignleft size-full wp-image-165" />在上述的程式碼中，先將原來傳入 BuildBG() 函式的圖片編號，轉換成相對應的圖塊編號 cValue，接著再直接存取 piMemory，也就是 BG_MAP_RAM(0) 位址，以每一列 4 個圖塊為一組，一次完成 16 個圖塊的索引值設定。</p>
<p>大功告成！只需要按照上述的步驟一步步執行，就能夠完成 NDS 的程式設計領域中，非常實用也非常具有彈性的圖塊背景模式功能。</p>
<p>接續之前三篇文章所介紹的 Framebuffer、Bitmap 與 Sprite，一路走來已經認識了 NDS 平台的輸入功能、聲音功能、Sprite 圖件，以及圖塊背景顯示模式。光是利用這些知識，已經非常足夠寫出幾個簡單的小遊戲了。有興趣的讀者，不妨試著利用這些概念<strong>開始動手寫個簡單的小型遊戲吧！</strong>如果能夠利用所學的知識，製造出完整的遊戲成品，不止可以獲得滿滿的成就感，同時也是一種非常棒的正向學習回饋噢！ ：D</p>
<p>程式碼範例下載：<a href="http://blog.monkeypotion.net/download-manager.php?id=9"><strong>NDSDev_TiledBackground.zip</strong></a> <small>（下載次數： 320 ）</small><br />
操作方式：程式中共有 3 種不同樣式的 Tilemap，按下「A」鍵或「B」鍵即可切換。</p>
<p>參考資料：<a href="http://www.dev-scene.com/NDS/Tutorials_Day_4">NDS/Tutorials Day 4: 2D Tile Graphics</a></p>
<img src="http://blog.monkeypotion.net/?ak_action=api_record_view&id=151&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.monkeypotion.net/gameprog/note/nds-dev-tiled-background/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>初探Nintendo DS程式開發與設計（三）：二維世界小精靈</title>
		<link>http://blog.monkeypotion.net/gameprog/note/nds-dev-sprite</link>
		<comments>http://blog.monkeypotion.net/gameprog/note/nds-dev-sprite#comments</comments>
		<pubDate>Sat, 14 Jun 2008 18:02:21 +0000</pubDate>
		<dc:creator>半路</dc:creator>
				<category><![CDATA[學習筆記]]></category>

		<guid isPermaLink="false">http://blog.monkeypotion.net/?p=140</guid>
		<description><![CDATA[延續前篇文章中所介紹的 Bitmap 背景圖件顯示功能後，本篇開始進入 2D 圖件 (Sprite) 的二維世界。所謂的 Sprite，原意是指「調皮搗蛋的小精靈」，但是在電腦繪圖與遊戲程式的領域中，Sprite 一詞則被用來當作二維繪圖物件的代名詞： Sprite is the term used to describe a 2D graphics with a number of related functions. 從前，在那個只有 X 軸與 Y 軸的平面世界中，Sprite 物件就是一切事物的根基，能夠用來表示角色人物、怪物敵人以及武器裝備等許多不同的遊戲物件。Sprite 可以只是一張單純的靜態圖片，也可以由數張圖片組合成為動畫的形式。簡單來講，Sprite 不過是一張可以移動與旋轉的 2D 貼圖罷了。 以 DirectX 為例，在 D3D 中已經包含了 Sprite 的物件功能，能夠使用 LPD3DXSPRITE 與 D3DXSprite() 輕易地創建出 Sprite 物件。即使目前許多平台上的遊戲，都已經趨向全 3D 型態的呈現方式，但是如果能夠應用得宜的話，傳統的 Sprite 物件繪圖模式仍然有很多的可能性存在。例如著名的線上遊戲《仙境傳說》，就是以 2D Sprite 人物，搭配 [...]]]></description>
			<content:encoded><![CDATA[<p>延續<a href="http://blog.monkeypotion.net/gameprog/note/nds-dev-part-two"><strong>前篇文章</strong></a>中所介紹的 Bitmap 背景圖件顯示功能後，本篇開始進入 <strong>2D 圖件 (Sprite)</strong> 的二維世界。所謂的 Sprite，原意是指「調皮搗蛋的小精靈」，但是在電腦繪圖與遊戲程式的領域中，Sprite 一詞則被用來當作二維繪圖物件的代名詞：</p>
<blockquote><p>
Sprite is the term used to describe a 2D graphics with a number of related functions.
</p></blockquote>
<p><img src="http://blog.monkeypotion.net/wp-content/uploads/2008/06/nds-dev-sprite.jpg" alt="nds-dev-sprite" title="nds-dev-sprite" width="211" height="350" class="alignleft size-full wp-image-156" />從前，在那個只有 X 軸與 Y 軸的平面世界中，<strong>Sprite 物件就是一切事物的根基</strong>，能夠用來表示角色人物、怪物敵人以及武器裝備等許多不同的遊戲物件。Sprite 可以只是一張單純的靜態圖片，也可以由數張圖片組合成為動畫的形式。簡單來講，Sprite 不過是一張可以移動與旋轉的 2D 貼圖罷了。</p>
<p>以 DirectX 為例，在 D3D 中已經包含了 Sprite 的物件功能，能夠使用 LPD3DXSPRITE 與 D3DXSprite() 輕易地創建出 Sprite 物件。即使目前許多平台上的遊戲，都已經趨向全 3D 型態的呈現方式，但是如果能夠應用得宜的話，傳統的 Sprite 物件繪圖模式仍然有很多的可能性存在。例如著名的線上遊戲<a href="http://ro.gameflier.com/">《仙境傳說》</a>，就是以 2D Sprite 人物，搭配 3D 型態場景的絕佳實例。</p>
<p>如果之前有接觸過 GBA 程式開發與設計知識的讀者，應該能夠很容易發現 NDS 平台與 GBA 平台的硬體架構與核心功能，其實擁有許多的相同之處。任天堂公司<strong>保留了原本設計良好的 GBA 架構</strong>，然後更進一步延伸，加入了 3D 繪圖、觸控操作、雙處理器引擎等等進階的硬體架構與功能。但是在 2D 繪圖的處理層面，仍然沿襲 GBA 平台的各種設定以及使用方法。而 Sprite 的許多定義與操作行為，也都是由 GBA 時代流傳下來的概念，因此在 NDS 平台上，Sprite 同樣佔了舉足輕重的關鍵地位。</p>
<p><span id="more-140"></span></p>
<p>正如 <a href="http://www.dev-scene.com/images/5/54/Nds_2D_memory.png"><strong>2D Memory Layout</strong></a> 的圖片所示，在 NDS 的記憶體配置架構中，有一個部分是專門為 Sprite 物件功能所設置的區域：<strong>Main Engine</strong> 所能夠使用的 Sprite Attributes Memory 位於 <strong>0&#215;07000000</strong> 的記憶體位址上；而 <strong>Sub Engine</strong> 所能夠使用的 Sprite Attributes Memory 則位於 <strong>0&#215;07000400</strong> 位址。在這一塊用途限定的記憶體位址中，能夠使程式設計者設定各種與 Sprite 物件相關的屬性，包括 Sprite 的位置、大小、形狀、色彩模式、顯示優先權、特殊效果等等。</p>
<p>而在 Sprite 的功能限制上，Main Engine 以及 Sub Engine 個別能夠：</p>
<ul>
<li>擁有 128 個 Sprite 物件。</li>
<li>擁有 32 個仿射轉置 (Affine Transformation) 矩陣。</li>
<li>可使用 16 色以及 256 色兩種色彩模式。</li>
<li>可使用 1024 個圖塊 (Tile)。</li>
</ul>
<p>所謂的<strong>仿射轉置矩陣</strong>，是用來轉換座標空間用的一種數學方法，只要按照矩陣公式調整其中的數值，就能夠使二維空間中的 Sprite 物件呈現出<strong>旋轉 (Rotation)</strong> 與<strong>縮放 (Scale)</strong> 的效果。但是在 128 個 Sprite 物件中，並不是全部都能擁有獨佔的仿射轉置矩陣；最多能夠產生出 32 個不同的仿射轉置矩陣，然後由所有的 Sprite 物件共享。在色彩模式的部分，Sprite 物件能夠使用 16 色 (4-bits) 與 256 色 (8-bits) 兩種模式；這兩種色彩模式都會使用到<strong>調色盤 (Palette)</strong> 的顏色配置方法，以達到節省記憶體空間的目的。而關於圖塊的定義與功用，將保留到下一篇文章中詳述。</p>
<p>以 Main Engine 的操作為例，如果要使用 Sprite 的功能，首先要啟動 NDS 的 Sprite 繪圖模式，並且設定繪圖記憶體的映射位置：</p>
<pre name="code" class="cpp">
// 啟動 Sprite 繪圖模式，並且使用 1D Tile 模式
videoSetMode(MODE_5_2D | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D);	

// 將 Bank E 設定給 Sprite 功能使用
vramSetBankE(VRAM_E_MAIN_SPRITE);
</pre>
<p>在 libnds 的程式碼中，用來操作 Sprite 物件行為與屬性的主要結構為 tOAM；所謂的 OAM，也就是 <strong>Object Attribute Memory</strong> 的縮寫名稱。所以我們可以在程式中，配置一個由 libnds 所定義的 tOAM 物件，以便於後續操作使用：</p>
<pre name="code" class="cpp">
// 主要的 OAM 結構
tOAM* pkOAM = new tOAM();

// 總共有 128 個 SpriteEntry 物件可使用
SpriteEntry* pkSprite0 = pkOAM->spriteBuffer[0];
SpriteEntry* pkSprite1 = pkOAM->spriteBuffer[1];
// ...
SpriteEntry* pkSprite127 = pkOAM->spriteBuffer[127];

// 總共有 32 個 SpriteRotation 物件可使用
SpriteRotation* pkSpriteRot0 = pkOAM->matrixBuffer[0];
SpriteRotation* pkSpriteRot1 = pkOAM->matrixBuffer[1];
// ...
SpriteRotation* pkSpriteRot31 = pkOAM->matrixBuffer[31];
</pre>
<p>而在開始使用 Sprite 物件之前，最好先將 tOAM 結構的內容重新設定為初始化的狀態，包含 spriteBuffer 與 matrixBuffer 在內：</p>
<pre name="code" class="cpp">
// 重設 Sprite Buffer 的內容
for (int i = 0; i < SPRITE_COUNT; i++)
{
    pkOAM->spriteBuffer[i].attribute[0] = ATTR0_DISABLED;
    pkOAM->spriteBuffer[i].attribute[1] = 0;
    pkOAM->spriteBuffer[i].attribute[2] = 0;
}

// 重設 Matrix Buffer 的內容
for (int i = 0; i < MATRIX_COUNT; i++)
{
    pkOAM->matrixBuffer[i].hdx = 1 << 8;
    pkOAM->matrixBuffer[i].hdy = 0;
    pkOAM->matrixBuffer[i].vdx = 0;
    pkOAM->matrixBuffer[i].vdy = 1 << 8;
}
</pre>
<p>其中的 SPRITE_COUNT 為 libnds 內定預設值 128，而 MATRIX_COUNT 則為 32。在 spriteBuffer 中總共存在三大分類的屬性，所以必須將這些屬性值全部關閉重設；另外，matrixBuffer 則是如前篇文章所提的作法，將其重設為單位矩陣。在更改了 Sprite 的屬性值之後，接著需要使用 dmaCopy() 函式將 tOAM 中的 spriteBuffer 資料複製至指定的記憶體位址中：</p>
<pre name="code" class="cpp">
DC_FlushAll();
dmaCopy(pkOAM->spriteBuffer, OAM, SPRITE_COUNT * sizeof(SpriteEntry));
</pre>
<p>這裡的 OAM，也就是由 libnds 所定義的 Sprite Attributes Memory 位址 0x07000000。以上這些資訊，全部都定義在 libnds 目錄下的 include/nds/arm9/sprite.h 檔案中，只要有安裝 devkitPro 就能夠隨時查閱檢視。</p>
<p>接下來，為了讓 Sprite 物件能夠自由地在二維的平面世界中轉來轉去，在此要先定義一項全新的幾何學規則：</p>
<blockquote><p>
<strong>一個圓的角度為 512 度。</strong>
</p></blockquote>
<p>請暫時捨棄我們所熟知的「一個圓為 360 度」的基礎數學知識。為什麼要使用這樣奇怪的自訂規則系統呢？因為 512 是 2 的冪次方，會比較便於在電腦系統中使用移位計算。一個圓是 512 度，半個圓則是 256 度，因此角度與徑度相互轉換的公式如下所示：</p>
<pre name="code" class="cpp">
#define PI (3.1415926)

// 徑度轉換成角度
inline int Rad2Deg(float fRadius)
{
	return (int)(fRadius * (256 / PI));
}

// 角度轉換成徑度
inline float Deg2Rad(int fDegree)
{
	return (fDegree * (PI / 256));
}
</pre>
<p>定義了全新的 512 度系統以及徑度角度轉換的函式之後，就可以開始盡情地轉動 Sprite 物件了：</p>
<pre name="code" class="cpp">
// 取得仿射矩陣
SpriteRotation* pkSpriteRot0 = pkOAM->matrixBuffer[0];

// 取得 sin 值與 cos 值
s16 s = SIN[fAngle &#038; SPRITE_ANGLE_MASK] >> 4;
s16 c = COS[fAngle &#038; SPRITE_ANGLE_MASK] >> 4;

// 設定矩陣數值
pkSpriteRot0->hdx = c;
pkSpriteRot0->hdy = s;
pkSpriteRot0->vdx = -s;
pkSpriteRot0->vdy = c;
</pre>
<p>上述程式碼主要是利用在 include/nds/arm9/trig_lut.h 中預先定義好的 SIN 與 COS <strong>查值表 (Lookup Table)</strong> ，取出對應於 512 角度系統的 sin 函數值與 cos 函數值，然後填入仿射轉置矩陣之中，即可使 Sprite 達到旋轉的效果。這個部分的程式碼，運用了數學中的<strong>線性代數</strong>理論，如果有興趣深入瞭解仿射轉置矩陣的使用原理，可以參考<a href="http://www.coranac.com/tonc/text/affine.htm">「The Affine Transformation Matrix」</a>這篇文章的內容。</p>
<p>搞定了複雜的旋轉程序之後，接著要移動 Sprite 物件就容易許多了。在 libnds 預先定義好的 SpriteEntry 結構中，已經提供了 posX 與 posY 成員變數供程式設計者直接使用：</p>
<pre name="code" class="cpp">
// 將 pkSpriteEntry 移動到 (100, 50) 的位置
pkSpriteEntry->posX = 100;
pkSpriteEntry->posY = 50;
</pre>
<p>而在範例程式中，為了使飛機物件能夠朝著機頭面向的角度移動，必須利用三角函數的公式，分別計算出 X 軸向與 Y 軸向的位移量：</p>
<blockquote><p>
X 軸位移量 = 動量 * sin(角度)<br />
Y 軸位移量 = 動量 * cos(角度)
</p></blockquote>
<p>首先利用 Deg2Rad() 函式，將 512 角度系統的數值轉換成為徑度，然後才能夠將徑度值傳入 sin() 與 cos() 函式中得出相對應的位移量：</p>
<pre name="code" class="cpp">
// 將角度轉換為徑度
float fRadius = Deg2Rad(fAngle);

// 將 Sprite 的位置分別加上 X 軸與 Y 軸的位移分量
static const int THRUST_FACTOR = 2;
pkSpriteEntry->posX += (int)(THRUST_FACTOR * sin(fRadius));
pkSpriteEntry->posY += (int)(-THRUST_FACTOR * cos(fRadius));
</pre>
<p>至此，就完成了 Sprite 物件的初始化、更新、旋轉與位移程序！</p>
<p>如前篇文章所提到的內容，NDS 架構中的 Sprite 物件功能，也是藉由<strong>記憶體位址的 bits 操作</strong>以控制各種相關的屬性值。而在 libnds 的include/nds/arm9/sprite.h 程式碼之中，可以看到作者巧妙地利用了<strong>結構的 union 語法</strong>，簡化了繁複的記憶體位元操作，使程式設計者能夠在同一塊記憶體區域中，以結構成員變數的方式更輕易地存取各種屬性值。</p>
<p>最後，我將這些與 Sprite 相關的功能，包裝成為一個 SpriteManager 類別；不過程式碼沒有經過太多測試，敬請小心服用：</p>
<pre name="code" class="cpp">
class SpriteManager
{
public:
    SpriteManager();
    ~SpriteManager();

    void Update();
    SpriteInfo* CreateSprite(SpriteStructure&#038; rkSpriteStructure);
    void TranslateSprite(SpriteInfo* pkInfo, u16 iPosX, u16 iPosY);
    void TranslateOffsetSprite(SpriteInfo* pkInfo, u16 iPosX, u16 iPosY);
    void RotateSprite(SpriteInfo* pkInfo, u16 iAngle);
    tOAM* GetOAM() const { return m_pkOAM; }

private:
    void InitializeOAM();

private:
    tOAM* m_pkOAM;
    SpriteInfo m_kSpriteInfo[SPRITE_COUNT];
    int m_iSpriteIndex;
    int m_iTileIndex;
};
</pre>
<p>程式碼範例下載：<a href="http://blog.monkeypotion.net/download-manager.php?id=8"><strong>NDSDev_Sprite.zip</strong></a> <small>(下載次數： 392 )</small><br />
操作方式：按住方向鍵的「上」可移動飛機，「左」與「右」可旋轉飛機的方向。</p>
<p>參考資料：<a href="http://patater.com/manual">Introduction to Nintendo DS Programming</a>：<a href="http://patater.com/files/projects/manual/manual.html#id2740389">Chapter 6</a></p>
<img src="http://blog.monkeypotion.net/?ak_action=api_record_view&id=140&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.monkeypotion.net/gameprog/note/nds-dev-sprite/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>向食人魔怪說嗨：與OGRE的第一類接觸</title>
		<link>http://blog.monkeypotion.net/gameprog/note/ogre-dev-intro</link>
		<comments>http://blog.monkeypotion.net/gameprog/note/ogre-dev-intro#comments</comments>
		<pubDate>Sun, 25 May 2008 16:07:06 +0000</pubDate>
		<dc:creator>半路</dc:creator>
				<category><![CDATA[學習筆記]]></category>

		<guid isPermaLink="false">http://blog.monkeypotion.net/?p=94</guid>
		<description><![CDATA[最近花費了大把的時間，努力地學習如何與 OGRE 這隻聲名遠播的巨獸成為好麻吉。至目前為止，總算是能夠稱得上稍微和它混熟了一點。關於 OGRE，這裡提出幾點我的學習筆記、目前觀察到的個性特質，以及與它相處的心得感想。 首先，對於稍具經驗的程式設計者來說，見到 OGRE 的第一眼印象，應該莫過於各種設計模式 (Design Pattern) 的廣泛使用了。在 OGRE 的架構設計與系統實作層面中，使用了 Abstract Factory、Factory Method、Singleton、Iterator、Observer 以及 Manager 等等許多著名的設計模式。說實在的，我還是頭一次看到這麼多不同種類的設計模式，能夠同時應用在一個遊戲繪圖引擎之中，正好能夠與以前所學的理論知識相互印證，學習起來非常爽快也很感動，真是令我大開眼界、大呼過癮！ 而其中，最顯而易見的設計模式就是 Singleton 與 Manager 的結合使用。OGRE 利用這兩項模式的組合，製造出了一位一位各有擅場的「專業經理人」，使各個子系統所負的權責，都能夠劃分的非常清楚仔細。另外，利用 Observer 的概念，讓使用者能夠繼承 FrameListener 類別，自行定義進行繪圖程序前後的相關處理程序，也是一個非常優秀的設計模式應用實例。 回想自己當初剛進入業界時，在沒有任何人使用這些設計模式的情況下，額外看了許多書籍網站的資料，最後終於利用 Singleton 與 Manager 模式實作出幾項遊戲專案的子系統組件。結果深入認識了 OGRE 以後，才發現原來這些觀念已經如此廣泛地運用在遊戲引擎的設計架構之中。正所謂「他山之石，可以攻錯」，對於程式設計者來說，吸收新知以及學習別人的長處，的確是十分關鍵而不可或缺的能力啊。 如果以程式語言的角度觀察，OGRE 可以說是一個百分百純天然的 C++ 繪圖引擎。與其他多數的遊戲引擎與繪圖引擎不同，OGRE 身上沒有舊時代遺留下來的沈重包袱，或者為了從 C 語言轉換至 C++ 的「怪味道」等等過期的設計與實作方法。舉例來說，OGRE 對於 namepace 語法的全面使用，就是一個非常正確而且便利的作法： namespace Ogre { class SceneManager { // INSERT [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://blog.monkeypotion.net/wp-content/uploads/2008/05/ogre.jpg" alt="OGRE" title="OGRE" width="250" height="100" class="alignleft size-full wp-image-143" />最近花費了大把的時間，努力地學習如何與 <a href="http://www.ogre3d.org/"><strong>OGRE</strong></a> 這隻聲名遠播的巨獸成為好麻吉。至目前為止，總算是能夠稱得上稍微和它混熟了一點。關於 OGRE，這裡提出幾點我的學習筆記、目前觀察到的個性特質，以及與它相處的心得感想。</p>
<p>首先，對於稍具經驗的程式設計者來說，見到 OGRE 的第一眼印象，應該莫過於各種<a href="http://blog.monkeypotion.net/category/gameprog/pattern"><strong>設計模式</strong></a> (Design Pattern) 的廣泛使用了。在 OGRE 的架構設計與系統實作層面中，使用了 <a href="http://blog.monkeypotion.net/gameprog/pattern/abstract-factory"><strong>Abstract Factory</strong></a>、<a href="http://blog.monkeypotion.net/gameprog/pattern/factory-method"><strong>Factory Method</strong></a>、<strong>Singleton</strong>、<strong>Iterator</strong>、<strong>Observer</strong> 以及 <strong>Manager</strong> 等等許多著名的設計模式。說實在的，我還是頭一次看到這麼多不同種類的設計模式，能夠同時應用在一個遊戲繪圖引擎之中，正好能夠與以前所學的理論知識相互印證，學習起來非常爽快也很感動，真是令我大開眼界、大呼過癮！</p>
<p>而其中，最顯而易見的設計模式就是 <strong>Singleton</strong> 與 <strong>Manager</strong> 的結合使用。OGRE 利用這兩項模式的組合，製造出了一位一位各有擅場的「專業經理人」，使各個子系統所負的權責，都能夠劃分的非常清楚仔細。另外，利用 <strong>Observer</strong> 的概念，讓使用者能夠繼承 FrameListener 類別，自行定義進行繪圖程序前後的相關處理程序，也是一個非常優秀的設計模式應用實例。</p>
<p>回想自己當初剛進入業界時，在沒有任何人使用這些設計模式的情況下，額外看了許多書籍網站的資料，最後終於利用 Singleton 與 Manager 模式實作出幾項遊戲專案的子系統組件。結果深入認識了 OGRE 以後，才發現原來這些觀念已經如此廣泛地運用在遊戲引擎的設計架構之中。正所謂「他山之石，可以攻錯」，對於程式設計者來說，吸收新知以及學習別人的長處，的確是十分關鍵而不可或缺的能力啊。</p>
<p><span id="more-94"></span></p>
<p>如果以程式語言的角度觀察，OGRE 可以說是一個<strong>百分百純天然的 C++ 繪圖引擎</strong>。與其他多數的遊戲引擎與繪圖引擎不同，OGRE 身上沒有舊時代遺留下來的沈重包袱，或者為了從 C 語言轉換至 C++ 的「怪味道」等等過期的設計與實作方法。舉例來說，OGRE 對於 namepace 語法的全面使用，就是一個非常正確而且便利的作法：</p>
<pre name="code" class="cpp">
namespace Ogre
{
    class SceneManager
    {
        // INSERT useful code here.
    };
}
</pre>
<p>從 C 語言時代逐漸演化琢磨而來的引擎前輩們，由於懼怕發生<strong>命名衝突</strong> (Naming Confliction) 的問題，所以一般常見的作法，就是在原始碼的類別與結構名稱裡，加上一個自訂的前綴詞 (Prefix)，形成 OgreSceneManager、OgreSceneNode、OgreRenderSystem 以及 OgreXXX 等等的類別名稱。只要如 OGRE 般善用 C++ 語言中的 namespace 語法，就能夠擺脫這些奇怪又累贅的前綴詞，更加能夠良好的切割類別名稱的定義空間。</p>
<p>而在 OGRE 複雜龐大的內心世界中，除了獨立運作的 Root 類別以外，其他的類別們主要可以分成<strong>場景管理</strong>、<strong>資源管理</strong>以及<strong>繪圖系統</strong>三大類：</p>
<ul>
<li><strong>Scene Management</strong>：用來管理遊戲中的所有物件的外顯行為，相關的類別有 SceneManager、SceneNode、MovableObject 等等。</li>
<li><strong>Resource Management</strong>：負責創建、分配與管理舉凡 Font、Mesh、Texture、Material 等等遊戲中與實體檔案相關的資訊，相關的類別有 ResourceManager、ArchiveManager、MaterialManager、TextureManager 等等。</li>
<li><strong>Rendering</strong>：掌管繪圖程序的系統，相關的類別有 Renderable、RenderWindow、RenderSystem、RenderQueue 等等。</li>
</ul>
<p>然而，如果 OGRE 中僅有這三項系統，其實難以滿足不同專案的不同需求，達到引擎本身應有的廣泛復用性與延展性。在不需要修改原始碼的前提之下，如何讓使用者達到最大的自由度，甚至能夠自由擴充原有的引擎功能？<strong>插件 (Plugin) 系統，就是 OGRE 給程式設計者的答案。</strong>有了場景、資源與繪圖系統三大基礎套件，再加上非常具有彈性的插件架構，就形成了 OGRE 之中最關鍵的黃金四角。而 OGRE 的心臟命脈，就在於萬能的 <strong>SceneManager</strong> 類別中！SceneManager 像是一座巨大的物件工廠，掌握了 OGRE 中多數物件與資源的生殺大權，能夠生產 SceneNode、Material、Light 以及 Animation 等等許多的類別物件並且加以管理。</p>
<p>如果有使用過其他商業遊戲引擎，或 Scene Graph 架構引擎的程式設計者，在初次與 OGRE 相識時，最難以適應的特點應該就是 <strong>Node 與 Object 分離而治的架構</strong>了。將 Scene Node 與 Scene Object 分離，是 OGRE 與其他繪圖引擎最大的不同點。剛開始使用的時候，需要花費不少時間重新調適使用習慣；而在習慣之後，就能夠發現這樣的架構不但擴充彈性較佳，在系統效能上也比傳統的 Scene Graph 架構有更為傑出的表現。</p>
<p>到此為止，聽起來 <strong>OGRE 似乎是個有百利而無一害的親切巨獸？</strong>然而，如同硬幣一樣，所有事物都有正反兩面的角度。</p>
<p>對於反面的第一眼觀察，我比較難以理解的是，為什麼 OGRE 明明是以 C++ 語言撰寫的引擎，卻要使用 Java 式的命名規則，使所有的成員函式名稱，都以首字字母小寫的形式存在，例如 getAnimation() 與 setAnimation() 而非 C++ 中常見的 GetAnimation() 與 SetAnimation() 形式。對於使用者來說，在配合自己所撰寫的程式碼時，需要特別注意命名慣例與撰寫風格上的統一。</p>
<p>另外，OGRE 對於<strong>例外處理</strong> (Exception Handling) 機制的大量使用，令我產生不少使用上的疑慮。舉個例子來說，即使是在很尋常的 getAnimation() 函式中，只要使用者傳入了目前不存在其中的字串名稱，OGRE 就會立即拋出例外狀況：</p>
<pre name="code" class="cpp">
String kAnimationName = "char_idle"; // 假設 char_idle 不存在於 mSceneMgr 中
Animation* pkAnim = mSceneMgr->getAnimation(kAnimationName); // 這裡會拋出例外狀況！
if (pkAnim == NULL) {
    // Create animation and/or report error
}
</pre>
<p>所以為了正確地取得 Animation 物件，必須要使用這樣的作法：</p>
<pre name="code" class="cpp">
Animation* pkAnim = NULL;
String kAnimationName = "char_idle";
// 先檢查存在性（不會拋出例外狀況）
if (mSceneMgr->hasAnimation(kAnimationName)) {
    // 確認存在就可以取值
    pkAnim = mSceneMgr->getAnimation(kAnimationName);
}
</pre>
<p>原來可以直接檢查 getAnimation() 回傳值是否為 NULL 值的方式，在 OGRE 中需要多出一個檢查的步驟，對於許多程式設計者來說，可能一時也難以習慣這樣的作法。而在 OGRE 中處處可見、隨手可拋出的例外狀況的情境之下，很容易出現以下這樣非常糟糕的程式碼撰寫方式：</p>
<pre name="code" class="cpp">
try {
    // Do something might throw exceptions!
    Animation* pkAnim = mSceneMgr->getAnimation(kAnimationName);
}
catch (...) {
    // 忽略，不做任何處理！
}
</pre>
<p>為了能夠順利創建出 Animation 物件，不產生例外狀況，程式設計者甚至會使用以下的作法：</p>
<pre name="code" class="cpp">
Animation* pkAnim = NULL;
float fLength = 1.0f;

try {
    // 如果 kAnimationName 不存在，就會拋出例外而進入 catch 區塊
    pkAnim = mSceneMgr->getAnimation(kAnimationName);
}
catch (...) {
    // 在 catch 區塊中創建所需的 Animation 物件
    pkAnim = mSceneMgr->createAnimation(kAnimationName, fLength);
}
</pre>
<p>相信比較有經驗的程式設計者應該都知道，<strong>C++ Exception Handling 機制應當要使用在極少發生的「例外」狀況中</strong>，而不是用來取代所有可能合理發生的空值狀態。這樣的使用方法，我個人覺得很不恰當，除了對於程式執行效能上的衝擊以外，在程式設計者疏忽大意的情況下，更容易造成錯誤的程式碼撰寫習慣。如果，世界上只存在著兩種類型的程式設計者，其一是「贊同使用例外狀況處理」，其二是「反對使用例外狀況處理」的話，我應該是屬於<strong>「反對在 C++ 中使用例外狀況處理」</strong>的那一類。</p>
<p>而至目前為止，在與 OGRE 的相處過程中，我所遭遇到最重大的問題，就是<strong>資源物件名稱的唯一性</strong>。在 OGRE 的資源管理系統中，幾乎所有的資源物件都是以 string 型別的值做為索引鍵值，存放在 std::map 結構中。這種設計方法的優點，在於程式設計者能夠很有效率地檢查資源的存在性並且進行存取動作。問題是，資源管理系統完全<strong>不允許重複索引鍵值的物件產生</strong>。當程式設計者手動建立物件時，能夠輕易地避免鍵值重複的問題；然而，當資源物件是由實體檔案讀取進來的時候，只要載入同一個檔案兩次以上，OGRE 就會毫不留情地發出哀嚎聲，強制中斷程式系統的運作。</p>
<p>追根究底，原因在於目前的 OGRE 並不存在完善的<strong>資源物件 Clone 機制</strong>，這個缺陷對於欲建立大型遊戲專案的程式設計者來說，應該是一個十分頭痛難纏的問題；目前只能夠由引擎的使用者，在建立資源物件之前，自行傳入獨一無二並且保證絕不重複的 Unique String 值，以確保程式系統執行的正確性。最近在 OGRE 官網的論壇中，看到有人提出了這項資源索引鍵值衝突的問題，OGRE 作者的解決方案是利用一個 Callback 函式，讓使用者可以自訂遇到鍵值名稱衝突時的處理程序，相關的程式碼應該會於下個 OGRE 主要的版本更新中釋出。</p>
<p>最後，在文件方面，OGRE 官方的正式教學文件太過於貧瘠，SDK 中附上的 Manual 裡竟然只有寥寥可數的幾頁內容。反而是在官網 <a href="http://www.ogre3d.org/wiki/index.php/Main_Page"><strong>Wiki</strong></a> 上的教學文章內容比較豐富，也有許多 Step by Step 的主題教學與實際的程式碼範例，使初學者能夠學習到一個比較系統化的知識。值得安慰的是，<strong>OGRE 原始碼的註解內容撰寫得非常不錯！</strong>只要參考 API Reference 文件的說明，在單一類別的使用上就不會遭遇到太大的問題，同時也能夠自行下載原始碼回來，仔細地研究探索其中的實作細節。</p>
<p>至於在實體書籍方面，目前僅有 <a href="http://tlsj.tenlong.com.tw/WebModule/BookSearch/bookSearchViewAction.do?isbn=1590597109&#038;sid=33657"><strong>《Pro OGRE 3D Programming》</strong></a> 唯一一本。這本書在 OGRE 的基本概念與設計理念闡述，都有相當詳盡的介紹，可惜在進階的主題上著墨不深，但仍然是一本認識 OGRE 必讀的入門書籍。另外在中國地區，有許多遊戲都是使用 OGRE 開發完成的，因此對於 OGRE 的研究不少，在網路上也能夠找到非常多關於 OGRE 的簡體中文資訊。對於有志使用 OGRE 開發遊戲的程式設計者來說，多多瀏覽官方的 <a href="http://www.ogre3d.org/phpBB2/"><strong>Forums</strong></a> 也是必不可少的例行工作。</p>
<p>總而言之，比起我之前曾經使用過的商業引擎，OGRE 可以說是一個<strong>設計架構很完善的新時代繪圖引擎</strong>，但是在許多使用層面上的細節，仍然有待更進一步的更新與加強。即使某些部分的功能還不夠完善，但就算是對於自行撰寫繪圖引擎的程式設計者來說，OGRE 也絕對是一個非常出色的參考典範。期待新的 OGRE 1.6 版本釋出以後，能有更具突破性的長足進步！</p>
<p>以上，是我目前對於 OGRE 的一點使用經驗與心得感想。如有其他想法，也歡迎提出討論～</p>
<img src="http://blog.monkeypotion.net/?ak_action=api_record_view&id=94&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.monkeypotion.net/gameprog/note/ogre-dev-intro/feed</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>初探Nintendo DS程式開發與設計（二）</title>
		<link>http://blog.monkeypotion.net/gameprog/note/nds-dev-part-two</link>
		<comments>http://blog.monkeypotion.net/gameprog/note/nds-dev-part-two#comments</comments>
		<pubDate>Wed, 07 May 2008 16:21:59 +0000</pubDate>
		<dc:creator>半路</dc:creator>
				<category><![CDATA[學習筆記]]></category>

		<guid isPermaLink="false">http://blog.monkeypotion.net/?p=67</guid>
		<description><![CDATA[自從年代久遠的「初探Nintendo DS程式開發與設計（一）」問世以後，經過了漫長的兩個月等待，新的續篇終於不負眾望地（？）火熱出爐囉！這篇文章將進入 Nintendo DS 的內心世界，深入探索它在樸實外表下所隱藏的強大能力。然後，同樣地以一個簡單的範例介紹基礎的背景圖件顯示功能。 首先要瞭解的是 DS 的硬體架構。 DS 是一個擁有雙中央處理器 (CPU) 的系統，兩個處理器分別是 ARM 9 以及 ARM 7；ARM 9 是一個 32-bits 架構 66 MHhz 速度的處理器，而 ARM 7 是一個 32-bits 架構 33 MHhz 速度的處理器。DS 擁有 4 MB 的主記憶體容量，總計 656 KB 的顯示記憶體容量，以及數個大小不等的快取記憶體；詳細的內容配置，請參照這張 DS 的記憶體架構圖。除了核心架構以外，當然還有音效硬體、麥克風、觸控螢幕、主機按鍵、GBA 卡匣槽，以及 Wi-Fi 網路的支援能力。 不同於 PC 平台上動輒成千上百的記憶體容量，整個 DS 的主記憶體只有 4 MB 的大小，而相當重要的顯示記憶體更是只有 656 KB 的容量；因此，在 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://blog.monkeypotion.net/wp-content/uploads/2008/05/nds-dev-bitmap.jpg" alt="nds-dev-bitmap" title="nds-dev-bitmap" width="224" height="372" class="alignleft size-full wp-image-135" />自從年代久遠的<a href="http://blog.monkeypotion.net/gameprog/note/nds-dev-part-one"><strong>「初探Nintendo DS程式開發與設計（一）」</strong></a>問世以後，經過了漫長的兩個月等待，新的續篇終於不負眾望地（？）火熱出爐囉！這篇文章將進入 Nintendo DS 的內心世界，深入探索它在樸實外表下所隱藏的強大能力。然後，同樣地以一個簡單的範例介紹基礎的背景圖件顯示功能。</p>
<p>首先要瞭解的是 DS 的硬體架構。</p>
<p>DS 是一個擁有雙中央處理器 (CPU) 的系統，兩個處理器分別是 <strong>ARM 9</strong> 以及 <strong>ARM 7</strong>；ARM 9 是一個 32-bits 架構 66 MHhz 速度的處理器，而 ARM 7 是一個 32-bits 架構 33 MHhz 速度的處理器。DS 擁有 4 MB 的主記憶體容量，總計 656 KB 的顯示記憶體容量，以及數個大小不等的快取記憶體；詳細的內容配置，請參照這張 <a href="http://www.dev-scene.com/images/3/3d/Dov_DS_MemoryMap.png"><strong>DS 的記憶體架構圖</strong></a>。除了核心架構以外，當然還有音效硬體、麥克風、觸控螢幕、主機按鍵、GBA 卡匣槽，以及 Wi-Fi 網路的支援能力。</p>
<p>不同於 PC 平台上動輒成千上百的記憶體容量，整個 DS 的主記憶體只有 4 MB 的大小，而相當重要的<strong>顯示記憶體更是只有 656 KB 的容量</strong>；因此，在 DS 上對於任何資源的配置行為，都需要精打細算地評估考量。ARM 9 與 ARM 7 都能夠自由存取主記憶體，而主記憶體通常會用來存放 ARM 9 所使用的遊戲執行檔，以及與遊戲相關的實體檔案。至於 ARM 7 的執行檔，雖然也能夠放在主記憶體中，但是為了效能上的考量，devkitPro 預設的行為會將執行檔放在快取記憶體 IWRAM 之中。另外，DS 的觸控螢幕輸入功能，是交由 ARM 7 所處理的；在 libnds 的預設執行檔中，ARM 7 會在每次主迴圈更新時，讀取觸控螢幕的相關資料，並且將這些資料存入 ARM 9 也能夠讀取的地方。</p>
<p><span id="more-67"></span></p>
<p>正如 DS 擁有上下雙螢幕的顯示介面，所以 DS 中也存在著 <strong>2 個繪圖核心引擎</strong>，能夠分別操作上螢幕與下螢幕的繪圖工作。這兩個繪圖核心引擎，一般分別稱為 <strong>Main Engine</strong> 以及 <strong>Sub Engine</strong>。在預設的情況下，Main Engine 是用來操作上螢幕繪圖，而 Sub Engine 則是用來操作下螢幕的繪圖，不過也可以在程式中使用 lcdSwap() 函式，交換上下螢幕所使用的繪圖引擎。而 Main Engine 與 Sub Engine 的不同點包括：</p>
<blockquote>
<ul>
<li>Main Engine 擁有 8 種<strong>繪圖模式 (Graphics Modes)</strong>，而 Sub Engine 擁有 6 種繪圖模式；Main Engine 額外的 2 種繪圖模式，能夠用來繪製大型的 Bitmap。</li>
<li>Main Engine 可以使用其中一個背景圖層做為 3D 的繪圖模式。</li>
<li>Main Engine 可以直接使用記憶體中指定的數值繪製畫面；也就是前篇文章中所使用的 Frame Buffer 模式。</li>
</ul>
</blockquote>
<p>在 DS 的小小世界裡，它總共只認得四種格式的繪圖資料：<strong>Bitmap</strong>、<strong>Tiled Background</strong>、<strong>Sprite</strong> 以及 <strong>3D</strong>。Main Engine 以及 Sub Engine 都各能夠使用至多 4 個<strong>背景圖件 (Background)</strong> 與 128 個 <strong>2D 圖件 (Sprite)</strong>。而背景圖件有兩種顯示模式，其一是使用單張的 Bitmap 圖檔做為背景顯示；另外一種方法，則是使用拼貼 (Tiled) 的方式，將數個小區塊的圖片組合成為一張完整的背景圖片。在本文的範例程式中，將實作最簡單的 Bitmap 顯示模式。</p>
<p>為了使 Main Engine 與 Sub Engine 順利進行繪圖工作，在程式初始化時，必須先分別指定這兩個引擎所使用的<strong>背景類型 (Background Type)</strong>，共有下列 6 種模式：</p>
<blockquote>
<ul>
<li><strong>Framebuffer</strong>：直接使用記憶體中的數值。</li>
<li><strong>Text</strong>：使用拼貼的方式繪製背景圖件。</li>
<li><strong>Rotation</strong>：與 Text 相同，另外可對背景圖件進行旋轉與縮放操作。</li>
<li><strong>Extended Rotation</strong>：如同 Framebuffer 模式，可直接使用記憶體中的數值，另外可對背景圖件進行旋轉與縮放操作。</li>
<li><strong>3D</strong>：使用類似 OpenGL 的 API 繪製 3D 圖形。</li>
<li><strong>Large Bitmap</strong>：能夠繪製 512 x 1024 或 1024 x 512 大小，8-bits 模式的巨大 Bitmap 圖片。</li>
</ul>
</blockquote>
<p>關於繪圖模式與背景類型的詳細列表，請參照 <a href="http://www.dev-scene.com/NDS/DOCgraphicmodes#Graphic_Modes">Graphics Modes</a> 以及  <a href="http://www.dev-scene.com/NDS/DOCgraphicmodes#Background_Types">Background Types</a> 中的表格說明。</p>
<p>舉個例子說明，當在程式中使用 videoSetMode(MODE_5_2D) 時，就是指定了 Main Engine 將使用第 5 種繪圖模式；而在這個繪圖模式之下的 4 層背景，BG0（第 0 層背景）能夠使用 Text 或 3D 背景，BG1 使用 Text 背景，而 BG2 與 BG3 都只能使用 Extended Rotation 背景類型。在一般的使用狀況中，未必會需要同時開啟 4 層背景圖件，因此在程式中，需要程式設計者自行指定要啟動的圖層，例如 videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE) 就是指定 Main Engine 使用第 5 種繪圖模式，並且開啟 BG3 背景圖層。</p>
<p><strong>Video RAM</strong>（顯示記憶體，常縮寫為 <strong>VRAM</strong>），也就是用來處理圖像顯示用的記憶體，其中可以存放 3D 物件的貼圖、2D 的 Sprite 物件，以及各種與繪圖程序相關的資料。DS 中<strong>共有 A、B、C、D 至 I，總共 9 條 VRAM Bank</strong>，程式設計者必須將它們映射 (Mapping) 至適當的主記憶體位址中，使繪圖引擎能夠取得所需的圖像資料。</p>
<p>這 9 條 VRAM Bank 各有不同的映射用途，例如在程式中使用 videoSetBankA(VRAM_A_MAIN_BG_0x6000000) 的意義，就是把 Bank A 映射至 0&#215;6000000 的記憶體位址上，同時指定這個位址中的內容，是要拿來做為顯示 Main Engine 的背景圖件之用；接著，只要再把圖片資料放進這個位址中，就能夠順利地顯示圖片內容了。<strong>搞懂記憶體映射的問題，會是一開始進入 NDS 程式設計時遇到的最大難題。</strong>詳細的映射範圍與用途，請參照 <a href="http://www.dev-scene.com/NDS/NDS_Tutorials_VramTable"><strong>VRAM</strong></a> 中的表格內容。</p>
<p>Console 程式設計與 PC 程式設計最大的不同點，在於 Console 需要面對的是更低階以及更底層的<strong>記憶體操作程序</strong>。而在進入 0 與 1 的微觀操作世界之前，還有一項需要特別提出來解釋的部分：DS 的硬體架構中，<strong>沒有浮點數運算器 (Floating Point Unit，簡稱 FPU) 的存在</strong>。因此，如果在程式碼中直接使用 float 資料型別的操作，會大幅地影響程式系統的效能。</p>
<p>為了達到浮點數運算的功能，在 DS 程式設計中有一個特殊的 Fixed Point Number 系統，能夠使用 16-bits 或 32-bits 的整數型別來表示浮點數的資料型別。例如在一個 32-bits 的 Integer 中，可以將 32 個 bits 視為 1、15、16 三個部分；最前頭的 1 bit 用來表示正數或負數，接著的 15 個 bit 表示數字的整數部分，而最末端的 16 bits 則當作數字的小數點部分。另外在轉換矩陣 (Transformation Matrix) 的數值設定中，會需要使用 16-bits，視為 0、8、8 三個部分的 Integer；例如 myVariable = 1 << 8，就是將 myVariable 變數的值設為 1 的動作。</p>
<p>與 DS 相關的硬體架構以及準備知識已經介紹得差不多了，接下來終於能夠正式進入程式設計的部分。這次，我們將拋棄親愛的 Visual Studio、甩掉親切的 PAlib，<strong>像個男子漢堂堂正正地面對 devkitPro 與 libnds！</strong>PAlib 本身相當地易學易用，但是也因為它的包裝與易用性，會使程式設計者忽略了許多基礎的知識以及重要的細節。為了能夠更深入地學習 NDS 的程式設計，務必從最基礎的 libnds 開始著手，才能夠得到更深入的收穫。</p>
<p>在四月下旬時，已經釋出了最新的 <a href="http://sourceforge.net/project/showfiles.php?group_id=114505&#038;package_id=160396"><strong>devkitPro Updater 1.4.6</strong></a>，如果之前有按照前篇文章的流程安裝過 PAlib，建議先執行 Uninstall 程序移除原有的 devkitPro 以後（PAlib 也會一併被刪除），再使用最新的 Updater 下載安裝新版 devkitPro。而建置 NDS 程式用的 IDE，可以選擇使用 devkitPro 內附的 <a href="http://www.pnotepad.org/"><strong>Programmer&#8217;s Notepad</strong></a>。Programmer&#8217;s Notepad 所使用的專案附檔名為 <strong>*.pnproj</strong>，直接點擊就能夠開啟專案，然後按下 Alt+1 就能夠開始建置專案，而 Alt+2 則是清除專案輸出檔。</p>
<p>OK！總算能夠開始撰寫程式了！在 NDS 的程式設計領域中，沒有便利的 API 可以呼叫，沒有妥善包裝的物件可以使用，很多時候程式設計者都需要<strong>對硬體的記憶體位址，直接進行 bit 層級的操作</strong>。例如：</p>
<pre name="code" class="cpp">
// 這是什麼鬼？
((uint16*) 0x6800000)[1] = 31;
((uint16*) 0x6800000)[2] = ((31 << 5) | (31 << 10));
((uint16*) 0x6800000)[3] = ((1) | (1<< 5) | (1<< 10));
</pre>
<p>安西教練說過：<strong>「能掌握記憶體位址的人，就能掌握 DS 程式！」</strong>但是對於程式設計者來說，很難有多餘的腦容量能夠去記憶這些莫名的記憶體位址與奇怪的數字組合。所以為了便利地操作這些記憶體位址，libnds 已經預先定義了非常多的 Preprocessor Macro 與 Global Variables。在 devkitPro 的安裝目錄下，打開 <strong>\include\nds\memory.h</strong> 以及 <strong>\include\nds\arm9\video.h</strong> 檔案，就可以看到許多與記憶體位址相關的定義。</p>
<p>因此重新改寫上述的程式碼，可以轉換成：</p>
<pre name="code" class="cpp">
// 這樣就清楚多了！
VRAM_A[1] = RGB15(31, 0, 0);
VRAM_A[2] = RGB15(0, 31, 31);
VRAM_A[3] = RGB15(1, 1, 1);
</pre>
<p>為了使 NDS 能夠讀取圖片資料，在啟動專案編譯程序時，devkitPro 會依照 <strong>Makefile</strong> 檔案中的指令，將專案目錄中的圖片格式檔案，編譯轉換成特殊的格式，然後產生出一個與檔名相對應的<strong>表頭檔 (Header File)</strong>。例如一張名稱為 image.png 的圖片會產生出 image.h 表頭檔，而表頭檔的內容如下所示：</p>
<pre name="code" class="cpp">
#ifndef __IMAGE__
#define __IMAGE__

#define imageBitmapLen 49152
extern const unsigned int imageBitmap[12288];

#define imagePalLen 512
extern const unsigned short imagePal[256];

#endif // __IMAGE__
</pre>
<p>檔案中定義了 imageBitmap 以及 imagePal 兩個變數，由 imageBitmap 可以存取到 image.png 的圖片資料，而 imagePal 則是 image.png 所使用的調色盤 (Palette) 資料。然後程式設計者就可以利用這兩個變數，將資料複製至合適的記憶體位址以顯示圖片。</p>
<p>為了正確地顯示出 Bitmap 格式的背景圖件，需要按照以下幾個步驟設定：</p>
<ol>
<li>設定繪圖模式，並且啟動背景圖層。</li>
<li>將 VRAM 映射至適當的記憶體位址中。</li>
<li>設定背景圖層所使用的圖片格式。</li>
<li>設定背景圖層的轉換矩陣數值。</li>
<li>將圖片的資料複製至 VRAM 映射的記憶體位址中。</li>
</ol>
<p>依序完成以上五項步驟之後，即可於 NDS 中順利地顯示出圖片：</p>
<pre name="code" class="cpp">
#include < nds.h >
#include "image01.h"

void main()
{
    irqInit();
    irqSet(IRQ_VBLANK, 0);

    // 1. 設定繪圖模式，並且啟動背景圖層。
    videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE);

    // 2. 將 VRAM 映射至適當的記憶體位址中。
    vramSetBankA(VRAM_A_MAIN_BG_0x06000000);

    // 3. 設定背景圖層所使用的圖片格式。
    BG3_CR = BG_BMP8_256x256;

    // 4. 設定背景圖層的轉換矩陣數值。
    // 此處是將 2x2 的轉換矩陣設定為單位矩陣
    BG3_XDX = 1 << 8;
    BG3_XDY = 0;
    BG3_YDX = 0;
    BG3_YDY = 1 << 8;

    // 5. 將圖片的資料複製至 VRAM 映射的記憶體位址中。
    dmaCopy(image01Bitmap, BG_GFX, image01BitmapLen);
    dmaCopy(image01Pal, BG_PALETTE, image01PalLen);

    // 完成圖片載入及顯示程序！

    while (1)
    {
        swiWaitForVBlank();
    }
}
</pre>
<p>範例程式中，我在上下螢幕各顯示了一張圖片，因此除了 Main Engine 以外，需要仿照上述的步驟設定 Sub Engine 的相關參數。在程式中，只要輕輕地點擊觸控螢幕，上下螢幕的圖片就會互換位置，並且發出一個簡短的音效聲音。如果讀者們恰巧擁有 NDS 能夠使用的<strong>謎之備份卡</strong>，只要把檔案中內附的 <strong>NDSDev_Bitmap.nds</strong> 拷貝進去，就可以在真正的 NDS 主機上看到程式的執行成果囉。</p>
<p>更多更深入的 NDS 程式設計內容，咱們下次見！（謎之聲：下次是何時啊？= =+）</p>
<p>程式碼範例下載：<a href="http://blog.monkeypotion.net/download-manager.php?id=7"><strong>NDSDev_Bitmap.zip</strong></a> <small>(下載次數： 467 )</small></p>
<p>參考資料：</p>
<ul>
<li><a href="http://www.dev-scene.com/NDS/Tutorials_Day_2">NDS 硬體架構簡介</a></li>
<li><a href="http://www.dev-scene.com/NDS/NDS_Tutorials_VramTable">NDS VRAM 架構</a></li>
<li><a href="http://www.dev-scene.com/NDS/DOCfixed_point">NDS 的 Fixed Point Number 系統</a></li>
<li><a href="http://www.dev-scene.com/NDS/DOCgraphicmodes">NDS 的繪圖模式與背景模式</a></li>
<li><a href="http://www.tvgame360.com.tw/viewthread.php?tid=18396&#038;extra=page%3D1%26amp%3Bfilter%3Ddigest">TVGAME360 NDS 開發系列引索</a></li>
</ul>
<p><br/></p>
<img src="http://blog.monkeypotion.net/?ak_action=api_record_view&id=67&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.monkeypotion.net/gameprog/note/nds-dev-part-two/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>多核多緒多樂趣</title>
		<link>http://blog.monkeypotion.net/gameprog/note/multicore-multithread-and-multifun</link>
		<comments>http://blog.monkeypotion.net/gameprog/note/multicore-multithread-and-multifun#comments</comments>
		<pubDate>Fri, 11 Apr 2008 16:40:03 +0000</pubDate>
		<dc:creator>半路</dc:creator>
				<category><![CDATA[學習筆記]]></category>

		<guid isPermaLink="false">http://blog.monkeypotion.net/?p=107</guid>
		<description><![CDATA[「多核心處理器有前途嗎？」幾年前，或許只是單純的疑問句，而到了今時今日，無庸置疑地已經成為了肯定句。 有沒有注意到，曾經每年都在急速成長的 CPU 處理速度，已經停留在 2 至 3G MHz 很久的一段時間了呢？在個人電腦的硬體配備中，已經由追求 CPU 的速度提升，轉向追求核心數量的增加。主流的 CPU 規格也逐漸由雙核心進步到四核心、六核心，甚至是未來的八核心中央處理器。「多核心」聽起來很酷，而這個所謂的多核心處理器的意義何在？ 在「Programming for Parallelism」中，共有 8 段配上中文字幕的影片，由 Intel 公司的 James Reinders 由淺至深地簡介了多核心硬體發展的必要性，以及相關程式應用技術的入門概念。其中在第一段的影片內容中，提到關於提升處理器時鐘頻率的作法，有幾項很關鍵的問題點： 繼續往上提升處理器的時鐘頻率有什麼問題？功率是我們最常聽見的說法。提高處理器的時鐘頻率，對於執行速度的提升會越來越不明顯；也許性能提升了 13%，但是功率的卻多耗損了 73%。所以如果我們繼續提升處理器的時鐘頻率，一來電費不划算，二來使用 CPU 煎牛排再也不是夢想。 另一個問題在於記憶體。過去幾年間，CPU 很快樂的以倍速飛快地成長著，然而在現實世界的硬體市場中，記憶體的時鐘頻率已經遠遠地跟不上處理器時鐘頻率的提升。就算處理器的動作再快，也得從記憶體拿取資料，並且將計算完畢的資料恭恭敬敬地交回給記憶體才行，所以在記憶體速度跟不上的情況下，處理器運算速度再快也只能空轉等待。 還有一個問題點在於可平行處理的指令集。能夠進行平行處理的指令集，已經被開發利用得差不多了，接下來即使不斷提升處理器的速度，也不能保證得到更好更快的執行效能，而只是徒增更多 CPU 閒置發呆的時間。 深刻體會上述三項提升處理器時鐘頻率的發展限制之後，總算有聰明絕頂的人想出更好的解決方案：「我們何不在一個處理器之中，塞入更多的內核單位，功率大致與原先相同，卻能大幅提昇處理器的性能至 73% 左右？」因此我們有了雙核心、四核心，以及六核心處理器。處理器核心的數目，未來將會不斷往上成長。 既然瞭解了多核心硬體的發展，已經成為勢在必行的現在進行式以及未來的趨勢，那麼身為遊戲開發者或遊戲程式設計者，也該回頭問問自己：「這和我有什麼關係？」關於這個問題，請聽聽以下兩位開發者的發言： TV Games 開發者 A 君：在 Cosole 程式的開發設計之中，為了彌補非常有限的主記憶體與繪圖記憶體空間，程式設計者必須將處理器的效能發揮至極限。目前 XBox360 中已經使用了三核心的 PowerPC 處理器系統，可預見的是，未來的電視遊樂器 Console，將會朝向更多核心的平行硬體架構方向發展。 PC Games 開發者 B 子：以目前的情況來說，要將程式特別為多核心處理器進行最佳化，是非常困難的事，畢竟 PC [...]]]></description>
			<content:encoded><![CDATA[<p>「多核心處理器有前途嗎？」幾年前，或許只是單純的疑問句，而到了今時今日，無庸置疑地已經成為了肯定句。</p>
<p>有沒有注意到，曾經每年都在急速成長的 CPU 處理速度，已經停留在 2 至 3G MHz 很久的一段時間了呢？在個人電腦的硬體配備中，已經由追求 CPU 的速度提升，轉向追求核心數量的增加。主流的 CPU 規格也逐漸由雙核心進步到四核心、六核心，甚至是未來的八核心中央處理器。「多核心」聽起來很酷，而這個所謂的<a href="http://zh.wikipedia.org/w/index.php?title=%E5%A4%9A%E6%A0%B8%E5%BF%83&#038;variant=zh-tw"><strong>多核心處理器</strong></a>的意義何在？</p>
<p>在<a href="http://www.zdnet.com.tw/white_board/intel/video-1.htm"><strong>「Programming for Parallelism」</strong></a>中，共有 8 段配上中文字幕的影片，由 Intel 公司的 James Reinders 由淺至深地簡介了多核心硬體發展的必要性，以及相關程式應用技術的入門概念。其中在第一段的影片內容中，提到關於提升處理器時鐘頻率的作法，有幾項很關鍵的問題點：</p>
<ul>
<li>繼續往上提升處理器的時鐘頻率有什麼問題？<strong>功率</strong>是我們最常聽見的說法。提高處理器的時鐘頻率，對於執行速度的提升會越來越不明顯；也許性能提升了 13%，但是功率的卻多耗損了 73%。所以如果我們繼續提升處理器的時鐘頻率，一來電費不划算，二來使用 CPU 煎牛排再也不是夢想。</li>
<li>另一個問題在於<strong>記憶體</strong>。過去幾年間，CPU 很快樂的以倍速飛快地成長著，然而在現實世界的硬體市場中，記憶體的時鐘頻率已經遠遠地跟不上處理器時鐘頻率的提升。就算處理器的動作再快，也得從記憶體拿取資料，並且將計算完畢的資料恭恭敬敬地交回給記憶體才行，所以在記憶體速度跟不上的情況下，處理器運算速度再快也只能空轉等待。</li>
<li>還有一個問題點在於<strong>可平行處理的指令集</strong>。能夠進行平行處理的指令集，已經被開發利用得差不多了，接下來即使不斷提升處理器的速度，也不能保證得到更好更快的執行效能，而只是徒增更多 CPU 閒置發呆的時間。</li>
</ul>
<p><span id="more-107"></span></p>
<p>深刻體會上述三項提升處理器時鐘頻率的發展限制之後，總算有聰明絕頂的人想出更好的解決方案：「我們何不在一個處理器之中，塞入更多的內核單位，功率大致與原先相同，卻能大幅提昇處理器的性能至 73% 左右？」因此我們有了雙核心、四核心，以及六核心處理器。處理器核心的數目，未來將會不斷往上成長。</p>
<p>既然瞭解了多核心硬體的發展，已經成為勢在必行的現在進行式以及未來的趨勢，那麼身為遊戲開發者或遊戲程式設計者，也該回頭問問自己：「這和我有什麼關係？」關於這個問題，請聽聽以下兩位開發者的發言：</p>
<blockquote>
<ul>
<li><strong>TV Games 開發者 A 君</strong>：在 Cosole 程式的開發設計之中，為了彌補非常有限的主記憶體與繪圖記憶體空間，程式設計者必須將處理器的效能發揮至極限。目前 XBox360 中已經使用了三核心的 PowerPC 處理器系統，可預見的是，未來的電視遊樂器 Console，將會朝向更多核心的平行硬體架構方向發展。</li>
<li><strong>PC Games 開發者 B 子</strong>：以目前的情況來說，要將程式特別為多核心處理器進行最佳化，是非常困難的事，畢竟 PC 平台不像 Console 平台一樣，屬於單一制式化的硬體規格。但是在未來的二至三年內，多核心處理器的電腦系統，也將會在 PC 平台中佔有相當大的比例。</li>
</ul>
</blockquote>
<p>對於程式設計者，甚至是專案的執行製作者來說，認識多核心的運用潛力，並且學習如何使用相關的技術，已經是刻不容緩的存亡關鍵。而為了正確應用多核心的程式開發技術，有三項至關緊要的關鍵概念必須謹記於心：</p>
<ul>
<li><strong>可擴展性 (Scalability)</strong>：擴展的目的並不是為了使用 8 個核心以獲取 8 倍的效能，我們需要的是<strong>維持程式系統的可擴展性</strong>，不將程式的多緒架構寫死，限定只能在雙核或是四核的硬體系統中執行，而是要能夠使程式系統不論在多少核心的情況下都能夠正常運作，並且同時獲得執行效能上的提升。</li>
<li><strong>正確性 (Correctness)</strong>：防止競賽條件 (Race Condition) 與鎖死 (Deadlock) 狀況的產生，並且確認你的多緒程式系統，<strong>能夠在單緒的環境下得到正確的執行結果</strong>，對於專案的後續測試、除錯與維護程序，將會有非常大的助益與影響。</li>
<li><strong>可維護性 (Maintainability)</strong>：為了因應不斷快速成長與變化的多緒程式設計技術發展，程式設計者要避免直接使用低階或原始的程式模組如 pthread 或 Windows 執行緒，而應該利用各項<strong>高階的抽象化工具與函式庫</strong>，例如：OpenMP、MPI、執行緒結構模組 (Threading Building Blocks)，才能夠達到良好的可維護性以及跨平台能力。</li>
</ul>
<p>關於 <a href="http://www.openmp.org/"><strong>OpenMP</strong></a> 的使用，來看一個最簡單的迴圈範例：</p>
<pre name="code" class="cpp">
#include < stdio.h >
#include < omp.h >

void main()
{
	#pragma omp parallel for
	for (int i = 0; i < 16; i++) {
		printf("thread [%d]: print number %d\n", omp_get_thread_num(), i);
	}
}
</pre>
<p>上述的程式碼，編譯執行後的結果如下所示：</p>
<blockquote><p>
thread [0]: print number 0<br />
thread [0]: print number 1<br />
thread [0]: print number 2<br />
...<br />
thread [0]: print number 15
</p></blockquote>
<p>接著，請在 Visual Studio 的程式專案環境中，開啟專案屬性設定，在 <strong>[ C/C++ ]</strong> 的 <strong>[ 語言 ]</strong> 頁面中，將 <strong>[ OpenMP 支援 ]</strong> 項目改成 <strong>[ 是 ]</strong> 的選項，然後在雙核心的電腦上執行程式，將得到非常不一樣的執行結果：</p>
<blockquote><p>
thread [0]: print number 0<br />
thread [1]: print number 8<br />
thread [0]: print number 1<br />
thread [0]: print number 2<br />
thread [1]: print number 9<br />
thread [0]: print number 3<br />
thread [0]: print number 4<br />
thread [1]: print number 10<br />
thread [0]: print number 5<br />
thread [0]: print number 6<br />
thread [1]: print number 11<br />
thread [0]: print number 7<br />
thread [1]: print number 12<br />
thread [1]: print number 13<br />
thread [1]: print number 14<br />
thread [1]: print number 15
</p></blockquote>
<p>如程式輸出的結果所示，只要在 for 迴圈前使用神奇的 <strong>#pragma omp parallel for</strong> 指令，並且開啟 OpenMP 的編譯選項，就能夠使迴圈中的程式碼以多執行緒的方式進行平行處理，而獲得執行效能上的提升。即使是在傳統單核心的處理器系統中，同樣能夠正確編譯並且執行上述的程式範例，不會造成任何問題。</p>
<p>對於既存的程式碼來說，只要正確地使用 OpenMP 技術，無須大幅更動現有程式碼的結構與計算邏輯，就能夠很快速地得到多核心運算的執行成效，甚至能夠在專案開發後期，經過效能量測之後，再對其中效能吃緊的迴圈與函式，進行平行處理的最佳化改善。然而，OpenMP 比較不適合用來處理複雜的多緒同步工作，我們仍然需要其他技術的輔助。</p>
<p>關於「鎖」這個東西，有位著名的文學家曾說過：<strong>「鎖，或是不鎖，都是個問題！（誤）」</strong>在多執行緒的程式中，由於任何一段程式碼的執行過程，都可能隨時被另一個執行緒中斷，因此在多核心技術的處理程序中，<strong>資料的同步化</strong>可以說是最令人頭痛而且十分艱鉅的任務。一般在多緒程式中常見的作法，是利用 Mutex 或是所謂的 Lock 機制，保護資料不會同時被多個執行緒進行讀寫動作：</p>
<pre name="code" class="cpp">
void MyClass::SetValue(int iValue)
{
    m_kMutex.Lock(); // 鎖定！
    m_iValue = iValue; // 安～心～操～作～～～
    m_kMutex.Unlock(); // 解鎖！
}
</pre>
<p>上述方法的缺點在於 Lock() 函式與 Unlock() 函式必須成對的使用，萬一發生忘記解鎖或是誤加兩次鎖的情形，就會造成資料鎖死而無法進行讀取與寫入的動作。因此，後來又發明了一種比較安全的鎖：</p>
<pre name="code" class="cpp">
void MyClass::SetValue(int iValue)
{
    Lock kLock(m_kMutex);
    m_iValue = iValue;
}
</pre>
<p>上例中的鎖，是一個在函式本體中宣告的物件變數，只會在它生存的 Scope 中發生作用。當 Lock 物件生成時，在建構式中會自動喚起鎖定的動作，然後當程式離開 SetValue() 函式時，物件變數自動被毀滅，就會在解構式中喚起解鎖動作解除鎖定。利用上述的方法，在進行資料賦值前，先鎖定資料的讀取，使其他的執行緒不能然後在賦值完畢後，再將鎖定解除。而能夠保持資料的同步。</p>
<p>但是使用這種「鎖定 - 操作 - 解鎖」的三步驟方法，會造成程式執行效能低落，以及容易造成資料鎖死狀況的發生，進而產生各種惱人難纏的程式臭蟲。所以在多緒的程式系統中，仍然要盡量減少 Lock 機制的使用，才不會對效能造成太多的負面影響。為了改善鎖定機制的不足之處，有人想出了更好更快速的 <a href="http://en.wikipedia.org/wiki/Lock-free_and_wait-free_algorithms"><strong>Lock-Free</strong></a> 資料結構與演算法，既無須開鎖關鎖，又可提升執行效能，總算為苦命的程式設計者帶來一線曙光。</p>
<p>所謂的 Lock-Free 演算法，是利用電腦硬體提供的<strong>基原操作 (Atomic Operation)</strong> 所完成的一種資料處理程序：</p>
<pre name="code" class="cpp">
// This function is atomic
template < class T >
bool CAS(T* addr, T exp, T val)
{
    if (*addr == exp) {
        *addr = val;
        return true;
    }
    return false;
}
</pre>
<p>範例中的 <strong>Compare and Swap (CAS)</strong> 函式，定義了一項基原操作：<strong>比較一個記憶體的值與一個已知的值，如果相等就將新的值存入記憶體中。</strong>以 <a href="http://en.wikipedia.org/wiki/X86">x86</a> 的硬體架構來說，能夠保證上述這項操作會一次處理完畢，不會被其他執行緒中斷，因此也就不需要仰賴鎖定與解鎖的機制以達成資料同步化的目標。</p>
<p>利用硬體所提供的 CAS 函式，以及其他的基原操作函式，就能夠免除 Lock 機制的問題，大幅提升多執行緒程式的執行效能。而所謂的 Lock-Free 資料結構，就是以 Lock-Free 演算法實作完成的一些基礎容器，例如 Linked List、Queue、Stack 與 Map 等等。然而，目前這些 Lock-Free Container 的實作方法相當複雜而不直覺，同時各種程式語言的支援與相關程式庫也在迅速發展中，未來應該能夠見到有如 STL 般的標準函式庫的誕生。</p>
<p>至此，已經介紹了多核心硬體發展的必要性、多核心技術的關鍵概念、OpenMP 基礎，以及神妙的 Lock-Free 演算法。如果以書籍中的資訊來看多緒技術在遊戲中的發展趨勢，在去年發行的<strong>《Game Programming Gems 6》</strong>，其中有一篇關於 Lock-Free 演算法，以及一篇 OpenMP 使用的文章，兩篇都是關於實作應用技術的內容。而在今年剛發行的<strong>《Game Programming Gems 7》</strong>中，則有一篇關於 Threading Engine 的設計與實作，以及一篇多緒工作系統的文章，已經進一步發展至高階的引擎系統架構。可以預見的是，未來將會有越來越多的網站資源、教學文章與論文期刊產生。</p>
<p>如同遊戲專案的開發流程，對於中央處理器來說，現在的年代已經不是單打獨鬥的英雄世紀了！取而代之的是，良好分工並且合作無間的團隊精神與合作模式。如果在遊戲的程式系統中，沒有善加利用多執行緒程式開發技術的威力，即使未來 CPU 硬體進步到 16 核或 32 核以上，依然是 1 個核跑得滿頭大汗，其他核在旁邊搧風納涼，完全發揮不了團結分工力量大的優勢。</p>
<p>想起不很久的從前，由 DOS 系統跨入 Windows 系統的年代裡，掛了一批跟不上腳步的人；之後由 2D 繪圖技術發展為 3D 繪圖技術時，又掛了另外一批人。未來會不會再發生如此巨大並且撼動人心的結構性變化？相較於其他領域的科學理論，電腦科學與計算機圖學正處於<strong>青春正甜</strong>的豆蔻年華，幾乎可以相當肯定地說：<strong>改變，是必然發生的未來。</strong></p>
<p>以我自己的情況來說，不論是多核心的相關知識或多執行緒程式的開發經驗，都十分有限而貧乏至極，仍然需要投注更多的心力與時間學習，才能夠真的將這些技術與知識應用於未來的專案之中。</p>
<p>遊戲界，即將全面跨入「多核多緒多樂趣」的新時代。<strong>而你，準備好了嗎？</strong></p>
<p><br/></p>
<p>參考資料：</p>
<ul>
<li><a href="http://www.zdnet.com.tw/white_board/intel/video-1.htm">Programming for Parallelism</a></li>
<li><a href="http://heresy.spaces.live.com/blog/cns!E0070FB8ECF9015F!1018.entry">簡易的程式平行化方法－OpenMP（一）簡介</a></li>
<li><a href="http://en.wikipedia.org/wiki/Lock-free_and_wait-free_algorithms">Lock-Free and Wait-Free Algorithms</a></li>
<li><a href="http://erdani.org/publications/cuj-2004-10.pdf">Lock-Free Data Structures</a></li>
<li><a href="http://blog.csdn.net/chinajxw/archive/2006/03/08/618865.aspx">锁无关的（Lock-Free）数据结构</a></li>
</ul>
<p>延伸閱讀：</p>
<ul>
<li><em>Game Programming Gems 6</em>: (1.1) <strong>Lock-Free Algorithms</strong></li>
<li><em>Game Programming Gems 6</em>: (1.2) <strong>Utilizing Multicore Processors with OpenMP</strong></li>
<li><em>Game Programming Gems 7</em>: (1.4) <strong>Design and Implementation of a Multi-Platform Threading Engine</strong></li>
<li><em>Game Programming Gems 7</em>: (1.9) <strong>Multithread Job and Dependency System</strong></li>
</ul>
<p><br/></p>
<img src="http://blog.monkeypotion.net/?ak_action=api_record_view&id=107&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.monkeypotion.net/gameprog/note/multicore-multithread-and-multifun/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>初探Nintendo DS程式開發與設計（一）</title>
		<link>http://blog.monkeypotion.net/gameprog/note/nds-dev-part-one</link>
		<comments>http://blog.monkeypotion.net/gameprog/note/nds-dev-part-one#comments</comments>
		<pubDate>Thu, 06 Mar 2008 12:31:08 +0000</pubDate>
		<dc:creator>半路</dc:creator>
				<category><![CDATA[學習筆記]]></category>

		<guid isPermaLink="false">http://blog.monkeypotion.net/gameprog/note/nds-dev-part-one</guid>
		<description><![CDATA[是的，你沒有看錯，本篇文章的主題是「NDS 程式設計」。 由任天堂公司所開發的 Nintendo Dual Screen 攜帶型掌上遊樂器，前幾年甫一上市就席捲了全世界的遊戲市場，而在 NDS 平台上的程式設計與開發，也已經是行之有年並且十分活躍的社群活動。一開始是由 GBA 平台，開啟了業餘愛好者在 Console 平台上自製程式的可能性，接著在效能強大的 NDS 掌上型遊樂器問世之後，很快地就有幾位能力高強的程式設計者，開發出能夠製作 NDS 程式的函式庫與工具。 我自己原來就非常喜歡玩 NDS 上的各種遊戲，在機緣巧合之下，最近剛好接觸到 NDS 程式開發的相關知識，覺得實在是非常新奇有趣又好玩！所以往後在這個新增的「學習筆記」分類中，我將會分享自己在學習過程中所獲得的一些心得與經驗；包括從門外漢一步步踏入這個新領域的過程中，所需建立的概念以及所需閱讀的文章，並且由淺入深地詳細解釋每個必要步驟，希望能夠對於有興趣學習 NDS 程式設計的讀者有一點幫助。如果其中內容有所錯誤或疏漏之處，也請不吝指正。 在搜尋引擎中，鍵入 NDS 程式開發或 NDS 程式設計的相關字句，立即就能夠找到許多相關於 NDS 程式開發的網站教學，然而，對於一個完全沒有 Console 平台程式寫作經驗的人來說，應該要從哪裡入門比較合適呢？在本篇文章中，將使用 libnds 函式庫與 devkitPro 開發工具，加上 PAlib 的環境設定為輔，在 Windows 平台下，初探 NDS 程式設計領域中 Frame Buffer 的使用以及 Input System 的操作，並提供完整的程式碼範例下載。 由業餘程式設計者自己製作的 NDS 程式，常被稱為是 Homebrew（自家釀造）程式。而在 NDS 的 [...]]]></description>
			<content:encoded><![CDATA[<p><img src='http://blog.monkeypotion.net/wp-content/uploads/2008/03/nintendo-dual-screen.jpg' alt='Nintendo Dual Screen' class='alignleft'/>是的，你沒有看錯，本篇文章的主題是<strong>「NDS 程式設計」</strong>。</p>
<p>由任天堂公司所開發的 <a href="http://www.nintendo.co.jp/ds/"><strong>Nintendo Dual Screen</strong></a> 攜帶型掌上遊樂器，前幾年甫一上市就席捲了全世界的遊戲市場，而在 NDS 平台上的程式設計與開發，也已經是行之有年並且十分活躍的社群活動。一開始是由 GBA 平台，開啟了業餘愛好者在 Console 平台上自製程式的可能性，接著在效能強大的 NDS 掌上型遊樂器問世之後，很快地就有幾位能力高強的程式設計者，<strong>開發出能夠製作 NDS 程式的函式庫與工具</strong>。</p>
<p>我自己原來就非常喜歡玩 NDS 上的各種遊戲，在機緣巧合之下，最近剛好接觸到 NDS 程式開發的相關知識，覺得實在是非常新奇有趣又好玩！所以往後在這個新增的<a href="http://blog.monkeypotion.net/category/gameprog/note"><strong>「學習筆記」</strong></a>分類中，我將會分享自己在學習過程中所獲得的一些心得與經驗；包括從門外漢一步步踏入這個新領域的過程中，所需建立的概念以及所需閱讀的文章，並且由淺入深地詳細解釋每個必要步驟，希望能夠對於有興趣學習 NDS 程式設計的讀者有一點幫助。如果其中內容有所錯誤或疏漏之處，也請不吝指正。</p>
<p>在搜尋引擎中，鍵入 NDS 程式開發或 NDS 程式設計的相關字句，立即就能夠找到許多相關於 NDS 程式開發的網站教學，然而，<strong>對於一個完全沒有 Console 平台程式寫作經驗的人來說，應該要從哪裡入門比較合適呢？</strong>在本篇文章中，將使用 <strong>libnds</strong> 函式庫與 <strong>devkitPro</strong> 開發工具，加上 <strong>PAlib</strong> 的環境設定為輔，在 Windows 平台下，初探 NDS 程式設計領域中 <strong>Frame Buffer 的使用以及 Input System 的操作</strong>，並提供完整的程式碼範例下載。</p>
<p><span id="more-62"></span></p>
<p>由業餘程式設計者自己製作的 NDS 程式，常被稱為是 <a href="http://en.wikipedia.org/wiki/Homebrew_(video_games)"><strong>Homebrew（自家釀造）</strong></a>程式。而在 NDS 的 Homebrew 領域中，首先要認識的是 <strong>libnds</strong>、<strong>devkitPro</strong> 與 <strong>PAlib</strong> 這三項開發 NDS 程式的利器：</p>
<blockquote>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Libnds"><strong>libnds</strong></a>：是由幾位熱愛 NDS 程式的開發者，經由反向工程所製作出來的一套非官方版本的函式庫。</li>
<li><a href="http://www.devkitpro.org/"><strong>devkitPro</strong></a>：開發各種 Console 平台程式的工具組，其中包含了開發 NDS 與 GBA 用的 <a href="http://www.devkitpro.org/category/devkitarm/"><strong>devkitARM</strong></a>，開發 PSP 用的 <a href="http://www.devkitpro.org/category/devkitpsp/">devkitPSP</a>，還有開發 Gamecube 用的 <a href="http://www.devkitpro.org/category/devkitppc/">devkitPPC</a>。</li>
<li><a href="http://www.palib.info/"><strong>PAlib</strong></a>：以 libnds 為基礎所建構起來的高階函式庫，能夠免除掉許多關於記憶體的操作與複雜的低階程序，相當便於 NDS 程式開發入門者使用。</li>
</ul>
</blockquote>
<p>在眾多素質參差不齊的網站資源與教學文章裡，想要選擇合適的對象做為踏入 NDS 程式設計領域的第一次親密接觸，我建議由<strong><a href="http://patater.com/manual">「Introduction to Nintendo DS Programming」</a></strong>這篇文章開始認識並且學習 NDS 的各種基礎知識，依序閱讀下列的幾個小節：</p>
<blockquote>
<ul>
<li><a href="http://patater.com/files/projects/manual/manual.html#id2479185">第一節</a>：瞭解 NDS 程式設計的背景資訊以及至關緊要的<strong>合法性</strong>問題。</li>
<li><a href="http://patater.com/files/projects/manual/manual.html#id2479708">第二節</a>與<a href="http://patater.com/files/projects/manual/manual.html#id2532307">第三節</a>：認識各種 Passthrough 裝置的緣由與沿革，瞭解如何在 NDS 遊樂器上利用這些裝置來執行自製程式。</li>
<li><a href="http://patater.com/files/projects/manual/manual.html#id2532768">第四節</a>：簡單介紹 libnds 與 devkitPro 兩者所扮演的角色與作用。</li>
<li><a href="http://patater.com/files/projects/manual/manual.html#id2533061">第五節</a>：以顯示<strong>背景圖片 (Background)</strong> 為目標，開始一步步建構起簡單的 NDS 程式。</li>
<li><a href="http://patater.com/files/projects/manual/manual.html#id2536488">第八節</a>：介紹 NDS 的<strong>輸入系統 (Input System)</strong> 行為與操作方法。</li>
</ul>
</blockquote>
<p>上述的教學文章裡還有許多其他的內容，不過我認為在一開始踏入不熟悉的領域時，能夠先搞懂這幾個部分的內容就已經很不容易了。特別是在第五節的內容中，開始進入程式設計的部分：那些 VRAM Bank 是怎麼回事？還有那些奇怪的操作行為是怎麼來的？為什麼在轉置矩陣的設定上要使用 <a href="http://en.wikipedia.org/wiki/Bitwise_operation#Bit_shifts">Shift Operation</a>？以上這些問題，可能已經讓某些讀者開始頭昏眼花了。這些部分都會在之後的文章裡一一解說，不過在本篇文章裡，會暫時先略過這些部分。</p>
<p>閱讀連結文章中的幾個部分，是為了對 NDS 程式設計領域建立起基礎概念與印象。知道開發 NDS 程式大概是怎麼個回事之後，接下來就可以開始閱讀<a href="http://www.dev-scene.com/NDS/Tutorials_Day_3"><strong>「NDS/Tutorials Day 3」</strong></a>這篇文章的內容，學習其中最簡單容易的 Frame Buffer 功能使用與 Input System 功能操作：</p>
<blockquote>
<ul>
<li><a href="http://www.dev-scene.com/NDS/Tutorials_Day_3#Frame_buffer...finally">Frame Buffer</a>：將 VRAM 視為最單純的 Frame Buffer 型態，進行螢幕畫面顯示上的操作使用。</li>
<li><a href="http://www.dev-scene.com/NDS/Tutorials_Day_3#Talking_to_the_keypad">Talking to the keypad</a>：如何偵測使用者按下了哪個按鈕與按鈕的作用狀態，並且操作 NDS 的基本輸入系統，。</li>
<li><a href="http://www.dev-scene.com/NDS/Tutorials_Day_3#Touching_things">Touching things</a>：介紹 NDS 上最特別的觸碰輸入系統。</li>
</ul>
</blockquote>
<p>具備了以上的知識以後，是不是已經覺得躍躍欲試了？不過在開始動手寫程式之前，需要先下載並且安裝幾項必備的軟體項目：</p>
<blockquote>
<ul>
<li><a href="http://www.microsoft.com/express/download/">Visual C++ Express 2005</a>（或 Professional 以上版本）：Windows 平台上的程式開發必備 IDE，相信不用多做介紹。
</li>
<li><a href="http://www.devkitpro.org/downloads/devkitpro-windows-installer/">devkitPro Windows Installer</a>：這是安裝 devkitPro 工具組用的 Updater，確認要安裝的部分之後，它會直接連上網路下載所需的最新組件。請記得<strong>要把 devkitPro 安裝在沒有空格的目錄中</strong>，例如 <strong>C:\devkitPro</strong> 是合法的安裝路徑，但是如果將程式安裝在 <strong>C:\Program Files\devkitPro</strong> 中就會出現問題，進行安裝時需要多加注意。</li>
<li><a href="http://sourceforge.net/project/showfiles.php?group_id=142901&#038;package_id=168612">PAlib 070717</a>：開發 NDS 用的高階函式庫，本文中並不會直接使用 PAlib，而是為了以下的 AppWizard 而先安裝這個函式庫。</li>
<li><a href="http://sourceforge.net/project/showfiles.php?group_id=114505&#038;package_id=124207&#038;release_id=481692">devkitARM release 20</a>：由於目前的 <strong>PAlib 070717 僅對應至 devkitARM release 20 的版本</strong>，所以需要另外下載這個比較舊版的 devkitARM。安裝完 devkitPro 之後，先將原來目錄中的 devkitARM 重新命名為 devkitPro_21，然後再將這個 devkitARM release 20 的版本，安裝至 devkitPro 的目錄中。</li>
<li><a href="http://www.thechip.net/images/PALibAppWizard.zip">PAlib AppWizard</a>：這是 PAlib 專案的設定精靈，在依照<a href="http://www.palib.info/wiki/doku.php?id=day1#installation">安裝方式</a>執行完畢後，就能夠很方便地在 Visual C++ 中創造出基本的 PAlib 專案，免除了 Make 檔相關的建置參數設定，可以幫助不熟悉 Unix/Linux 程式開發環境的程式設計者迅速上手。</li>
<li>Emulators：各種 PC 平台上模擬真實 NDS 執行環境的模擬器，能夠讓程式設計者很便利地檢視並測試程式的執行成果，這些模擬器包括有 <a href="http://nocash.emubase.de/gba.htm">No$GBA</a>、<a href="http://dualis.1emu.net/">Dualis</a>、<a href="http://www.ideasemu.org/">iDeaS</a>、<a href="http://desmume.org/">DeSmuMe</a> 等等，建議可以多下載幾個不同的模擬器交叉試用比較。</li>
</ul>
</blockquote>
<p>下載並安裝完以上的軟體之後，接著參照<a href="http://www.palib.info/wiki/doku.php?id=day1#using_palib_with_visual_c_2005_express">這篇文章</a>的指示，在 Visual Studio 中一步步設定好建置程式的執行環境。首先將 devkitPro 與 PAlib 所需的<a href="http://www.palib.info/wiki/doku.php?id=day1#setting_up_the_environment">環境變數設定完成</a>，接著進行<a href="http://www.palib.info/wiki/doku.php?id=day1#setting_up_an_emulator_inside_visual_c">外部工具 (External Tools) 的設定程序</a>；藉由外部執行檔與執行參數的設定，再加上快速鍵的按鍵組合設定，就能夠在 Visual Studio 中便利地執行模擬器，並且載入專案中的程式以迅速檢視執行成果。</p>
<p>具備了基礎概念與知識，並且將程式開發環境準備完畢後，終於可以真正開始在 Windows 上撰寫第一個 NDS 程式了！這裡的程式將以<a href="http://www.dev-scene.com/NDS/Tutorials_Day_3#Frame_buffer...finally">前述文章</a>中的範例為基礎，使用 NDS 中最單純的 Frame Buffer 顯示模式，在上螢幕中以像素點的移動，模擬出星星流動的樣子，並且偵測 NDS 上所有按鈕與觸控螢幕的輸入，以 Console 顯示的方式將文字訊息輸出在下螢幕中。</p>
<p>程式的主流程非常單純而且簡單，只需要依順序進行以下幾項步驟：</p>
<blockquote>
<ol>
<li>初始化 IRQ 與 Console 設定</li>
<li>設定 Video Mode</li>
<li>映射 VRAM Bank</li>
<li>偵測 Input System</li>
<li>處理程式的 Update Procedure</li>
<li>將 RGB 值寫入 VRAM</li>
</ol>
</blockquote>
<p>主要進入點的程式碼如下所示：</p>
<pre name="code" class="cpp">
int main(void)
{
    // Initialize IRQ settings
    irqInit();
    irqEnable(IRQ_VBLANK);
    // Initialize console setting
    consoleDemoInit();

    // Setup video mode to frame buffer usage
    videoSetMode(MODE_FB0);
    // Mapping vram to frame buffer usage
    vramSetBankA(VRAM_A_LCD);

    // Initialize game logic
    srand(time(NULL));
    ClearScreen();
    InitStars();

    // Main loop
    while (true) {
        // Wait for V-Blank
        swiWaitForVBlank();
        // Clear console output
        consoleClear();

        // Game logic
        for (int i = 0; i < NUM_STARS; i++) {
            // Update stars
            if (g_bStarsMoving) {
                EraseStar(&#038;g_tStars[i]);
                MoveStar(&#038;g_tStars[i]);
            }
            // Write stars (RGB values) to VRAM
            DrawStar(&#038;g_tStars[i]);
        }
        // Input system handling
        ProcessInput();
    }
    return 0;
}
</pre>
<p>這個程式中最重要的部分，在於以 <strong>videoSetMode(MODE_FB0)</strong> 指定 NDS 繪圖引擎使用 Frame Buffer 的繪圖模式，然後再使用 <strong>vramSetBankA(VRAM_A_LCD)</strong>，將 Bank A 的繪圖記憶體 (VRAM) 指定給螢幕顯示 (LCD) 使用。所謂的 Frame Buffer 模式，可以視為是<strong>直接由記憶體數值映射至螢幕顯示顏色</strong>的一種方式，所以只要將 RGB 值寫入對應的記憶體位址中，就能夠在螢幕上直接看到顏色值的顯示結果。</p>
<p>因此，在繪製星星的時候，就能夠將 <strong>VRAM_A</strong> 的內容，直接以<strong>一維陣列</strong>的方式存取並填入適當的 RGB 值：</p>
<pre name="code" class="cpp">
void DrawStar(Star* ptStar)
{
    VRAM_A[ ptStar->x + ptStar->y * SCREEN_WIDTH ] = RGB15( g_sStarColorR, g_sStarColorG, g_sStarColorB );
}

void EraseStar(Star* ptStar)
{
    VRAM_A[ ptStar->x + ptStar->y * SCREEN_WIDTH ] = BACKGROUND_COLOR;
}
</pre>
<p>這裡所使用的是 NDS 架構中的 <strong>15 Bits Color 模式</strong>，R、G、B 三原色分別各佔 5 Bits 的大小，然後透過 <strong>RGB15</strong> 這個 Preprocessor Macro 的使用，就能夠很方便的指定螢幕像素點的顏色值。</p>
<p><img src='http://blog.monkeypotion.net/wp-content/uploads/2008/03/nds-dev-frame-buffer-screenshot.jpg' alt='NDSDev_FrameBuffer Screenshot' class='alignright'/></p>
<p>在使用 PAlib AppWizard 創建 NDS 程式專案的情形下，按下「編譯」或「建置」都會使專案直接連結至 devkitPro 的相關工具以進行建置程序。在程式建置完畢之後，會產生出 <strong>*.nds</strong> 與 <strong>*.ds.gba</strong> 格式的 NDS 專用檔案，並不是一般 Windows 平台中常見的 <strong>*.exe</strong> 可執行檔。所以在此不能夠按下 F5 快速鍵來直接執行程式，而是<strong>必須使用之前設置好的外部工具</strong>，如 No$GBA 之類的模擬器，以執行建置完成的程式。左邊的圖，就是以模擬器執行這個簡單範例程式的畫面。</p>
<p>按照以上<strong>學習、安裝、撰寫</strong>的三拍子節奏行進，就能夠優雅而輕鬆地進入 NDS 的程式設計領域，<strong>親手寫出人生中的第一個 NDS 程式！</strong>接下來的文章裡，將會陸續介紹 NDS 的硬體架構與繪圖模式，並且開始學習如何載入與繪製背景圖片等等非常有趣的部分。</p>
<p>完整程式碼下載：<a href='http://blog.monkeypotion.net/download-manager.php?id=3'><strong>NDSDev_FrameBuffer.zip</strong></a> <small>(下載次數： 570 )</small></p>
<img src="http://blog.monkeypotion.net/?ak_action=api_record_view&id=62&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://blog.monkeypotion.net/gameprog/note/nds-dev-part-one/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
	</channel>
</rss>
