2007年11月16日 星期五

台大教授的程式作業批改系統

不知道有沒有人對YouTube上的一首曖昧改編版有印像 (曾經被PO在ptt上面)



笑點可能要修過程式設計課的人才會懂..

我有在ptt2潛水看台大兩位資工系教授的個人版。其中一位教授稱他的程式作業叫『使徒』(於是學生們就得擊敗眾多的使徒 XDXD)

那位教授也開發了一個web-based的程式批改系統(像是ACM judge),叫做『批改娘』。
『批改娘』的系統好像常常被助教惡搞,於是今天在版上就看到了這樣的畫面

使�

真是有趣的教授和助教啊....XD 有興趣的話前往ptt2應該不難找到那個版

2007年11月12日 星期一

Monte Carlo integration (蒙地卡羅積分)

蒙地卡羅積分是一個能夠求積分近似值的方法。這個方法應該有很多應用吧,我印像最深刻的是影像合成中的ray tracing演算法(可用於計算一個點受到的照度)。因為學弟的論文可能會用到這個方法,我曾經學過但是有點忘了,而且竟然找不太到中文的資源,所以復習一下順便做一下筆記。

假設有一個對x的定積分式


如下圖,如果f為折線的函式,則黃色區塊的面積即為自0到1積分的結果。

monte1

當然,我們很容易可以利用三角形面積公式,或者對f求定積分,而算出黃色區域的面積。然而,假設我們沒有這樣的先備知識,或者黃色區域的面積是難以求得的,那要怎麼辦呢?

2007年10月2日 星期二

下載檔案如何不造成lag?

前言:這篇想要寫的盡量簡單易懂,所以用了一些技術上不太正確的譬喻,高手看到請鞭小力一點。


劇情:小明在外租屋,房東家裡裝了一條ADSL網路,免費分給各位房客用。小明的朋友阿喵架了一個FTP站,在上面放了一些電影。有一天,小明在阿喵的FTP上抓電影時,房東的兒子打網路遊戲突然變的很不順,下達一個攻擊指令後過三秒才會有反應。房東的兒子和房東說了這樣的情況,於是房東就把房客們的網路線拔掉了...如果有在網路上下載檔案(FTP, BT, eMule)的人,應該常常會有這種經驗...打BBS或網路遊戲突然變的很lag(指網路回應時間很慢)。要怎麼避免這種狀況呢?

Wordpress忘記密碼的解決方法

此處指自己架的wordpress,並非wordpress.com提供的service。

第一種方法:在login畫面點選forget password,會寄送一封信到你的信箱,附帶一個可以更改密碼的link,但要是這方法失效的話...

進phpmyadmin,修改wp_users table,將使用者的密碼欄位清空 (原本會看到一串16進位的數字),打上新的密碼,並點選MD5。phpmyadmin會幫你把密碼用MD5 hash後存入資料庫,收工。

事實上sql指令夠熟的話,直接去console下sql...

UPDATE `xxx_blog`.`wp_users` SET `user_pass` = MD5( 'password' ) WHERE `wp_users`.`ID` =2 ;

參考http://codex.wordpress.org/Resetting_Your_Password

2007年3月19日 星期一

遊戲中常見的幾種音樂格式

這篇是貼我自己以前寫的文章。



介紹這些格式的文章應該不少,但我想大多過於專業
我試著用自己的話,盡量簡單地描述四種遊戲最常見的音樂格式

  1. MIDI:通常副檔名為mid,MIDI是Music Instrument Digital Interface的簡寫
    特色是檔案很小,非常小

    簡單的這麼解釋好了:

    mid檔就像是樂譜,記錄著「第5.02秒鋼琴要彈下sol、第6.31秒吉他要彈下ra」這樣的資訊
    然而mid檔裡本身並沒有吉他、鋼琴的音色。這也就是為何mid檔案小的原因

    那當我們的電腦撥放mid檔時,怎麼知道吉他、鋼琴的音色呢?
    這是因為mid檔通常遵守一種叫「General MIDI」的格式
    「樂器1」表示鋼琴、「樂器25」表示尼龍弦的古典吉他
    然而我們的音效卡上有一個「MIDI音源器」,負責按照這些樂器編號,把聲音撥放出來
    (當然有軟體音源這個例外,但不在此討論)

    單純使用mid檔的話有一些限制

    1. 音色是看使用者電腦上的音源,不同的電腦撥放出來的效果可能會不同
      (大部份的人應該都是爛爛的AC97音效卡)
    2. 音色有限,只有General MIDI提供的音色 (我記得是128種,不知道有沒有記錯)
    3. 效果有限,沒辦法自由的調整迴音、各種效果(equalizer, echo, reverb, flanger等....多不勝數)


  2. Wave:通常副檔名為wav
    特色是檔案很大,非常大

    大家可以把他想像成忠實的把聲音記錄下來的檔案格式

    音質最好,所以花費的空間多
    (大家可以把他想像成圖檔中的bmp檔)

    許多商業遊戲都是用CD音軌的方式來儲存音樂
    CD音軌其實也是屬於Wave的無失真儲存方式
    (CD音軌的資訊其實就等於Sample rate=44,100Hz 雙聲道的Wave檔)


    很常見的編曲方式就是:使用MIDI編曲軟體+自己的軟/硬體音源 => 混音成wave檔

    然後再看要不要做壓縮之類的
    和單純使用General MIDI比起來,這樣可以使用很多種的效果,處理上也有很大的彈性


    Wave和以下的mp3, ogg...等真正儲存聲音的,通稱為數位音訊檔
    接下來只是把Wave壓縮過的格式,我就不詳細介紹了
    主要的目的只是跟大家講數位音訊和mid檔之間的差別

  3. mp3
    相信大家都很熟悉吧 XD


    mp3是將wave失真壓縮(註)後的一種格式

  4. ogg
    可以參考維基百科: Ogg Vorbis

    一樣是失真壓縮
    據說同樣壓縮比下的音質比mp3還好,我自己倒是沒試過
    在一般使用者間這個格式還是沒有比mp3被廣為使用


註:失真壓縮
失真壓縮就是指在壓縮的過程中,損失一些原有的資訊

舉大家比較熟悉的圖片來說,bmp就好像wav的角色
png,gif就是一種非失真壓縮(畫質不會變差),jpg就是失真壓縮

2007年3月8日 星期四

GLUT+Code::Blocks+Mingw環境設定

GLUT....不介紹了,反正就是OpenGL的一些Toolkit,讓programmer避免處理low level的視窗控制。

其實我滿推薦寫程式的人不要「只」用微軟的IDE。之前看到某位網友抱怨,他們學校沒有正版的VC教育版,老師還要求要用VC寫作業。多推廣一下免費開源的IDE不是很好嗎?



Code::Blocks+Mingw的安裝可以參考這篇,如果不是要跑OGRE的話,直接去官網下載區抓安裝包應該比較簡單 (with Mingw compiler的)。

原本的GLUT Windows Binary和Mingw似乎不太相容,請抓GLUT for Mingw32,隨便解壓到一個目錄(我放在Mingw下),就可以了。

預備動作就這麼簡單 (不過我灌時試GLUT library試了一陣子,才發現要抓Mingw版的) ,進到Code::Block,New project -> GLUT project -> 到GLUT's location時輸入你安裝GLUT的位置。

這樣開好專案後會出現template的範例程式,compile&run看看...
C:\codeblock\GLUTMingw32\include\GL\glut.h:45: error: redeclaration of C++ built-in type `short'
疑...不知道怎麼回事的error,但我們見招拆招一下
方法一: 修改範例程式,再include之前加上# define _WCHAR_T_DEFINED
方法二: 修改glyt.h第45行,將typedef unsigned short wchar_t;這行給註解掉

兩者任做一項,這樣就可以run囉...

2007年2月21日 星期三

TJS特訓99解說篇

上一篇忘了說,我的架構主要是參考這篇TJS實作教學的

先講一下類別的設計:
  • MovingObject:所有戰場上會移動的物件,Player及Enemy的父類別
  • Player:玩家自機,畫面上那個藍點
  • Enemy:敵機,不停撞過來的那些小紅點
  • GameMaster:管理整個遊戲的迴圈與邏輯
  • GameWindow:遊戲視窗
GameWindow類別
作用其實很簡單
  • 建立一個視窗,設定大小為400x400。
  • 建立一個Layer,該視窗的主要圖層。
  • 然後建立一個GameMaster物件,就這樣。接下來遊戲的進行都交給GameMaster控制。
GameMaster類別
  • 建立fore(前景)圖層,所有遊戲的物件都畫在這個圖層上。
  • 建立玩家物件及99個敵人物件,敵人物件用一個陣列(Array)存放。
  • 建立一個Timer,每25ms呼叫一次 (理想狀態下40fps),負責玩家、敵機的移動、碰撞判定。
MovingObject類別
  • 之前說過這個範例不要想用到圖檔,所以我們使用正方形來表示自機與子彈。size是邊長,color是顏色
  • 繼承Layer物件,建構時將自己放在fore這個圖層上
Enemy類別
  • 建構時指定顏色(子彈是紅色)及邊長。
  • init函式負責初始化敵機 (在畫面的邊緣出現,向畫面內非)
  • check_bound函式負責檢查敵機是否出界了,如果是的話,呼叫init來重置敵機。
  • update函式負責敵機的移動
Player類別
  • 建構時指定顏色(藍色)
  • update函式負責移動,使用System.getKeyState()來判斷方向鍵是否已被按下。
我就不一行一行的講解程式碼了,有興趣的話可以留言討論。



延伸練習:這只是個小小範例,我的code有滿多問題的,如果你想練習的話...
  1. 我忘了寫Player出界的判定,請補上
  2. 碰撞判定的公式其實是不對的,你發現了嗎?請修正
  3. Frame穩速的機制是否有比使用Timer還要好的方式呢?
  4. 敵機移動的速度目都只是整數,請修改init函式讓敵機的移動速度不要那麼規律。
  5. 找找看還有沒有其他bug吧 :)

2007年2月20日 星期二

吉里吉里TJS特訓99

特訓99是數年前在巴哈姆特kuso版看到的一個遊戲,規則很簡單:「閃避畫面上的99顆子彈,看你可以撐幾秒」自從看到了這個遊戲之後,每到一個可以寫遊戲的環境,我都把他當hello, world來寫了,幫助熟悉整個程式環境。(印像中寫過C++/DirectDraw, J2SE, J2ME on 手機, VisualBasic, Ruby/RPG Maker XP還有什麼忘了)

剛剛把試著用吉里吉里的TJS寫了一下,心得...喵的文件有夠不完整,要到處翻來翻趣,有的地方乾脆去template裡直接看他怎麼寫的比較快。寫出來就這樣囉:


不停的閃子彈閃到死吧 XD
  • 其實code寫的很差,我只是一直翻文件然後找出一個能寫出來的方法而已。很多地方應該可以再修正的。碰撞判定的公式其實也是錯的,可是我懶得改了,可以跑就好了啦,只是做個小範例而已 @@
  • 為了簡化範例,被擊中game over時就直接跳出遊戲了 (好我懶得做game over畫面了 XD)
  • 為了簡化範例所以沒有貼圖,都是用方塊表示。這樣只需要把程式複置貼上就可以跑囉


把以下程式複置貼到startup.tjs (原本的程式清掉) 即可

class MovingObject extends Layer{
var master;
var size;
var color;

function MovingObject(gm, window, parent) {
super.Layer(window, parent);
master = gm;
visible = true;
setSize(size, size);
fillRect(0, 0, size, size, color);
}
}

class Enemy extends MovingObject {
var vx, vy;
function Enemy(gm, window, parent) {
size = 6;
color = 0xffff0000;
super.MovingObject(gm, window, parent);
init();
}
function init() {
var side = Math.floor(Math.random()*4);
switch (side) {
case 0:
top = 0;
left = Math.floor(Math.random()*400);
vx = Math.floor(Math.random()*5)-2;
vy = Math.floor(Math.random()*2)+1;
break;
case 1:
top = 400;
left = Math.floor(Math.random()*400);
vx = Math.floor(Math.random()*5)-2;
vy = -Math.floor(Math.random()*2)-1;
break;
case 2:
left = 0;
top = Math.floor(Math.random()*400);
vy = Math.floor(Math.random()*5)-2;
vx = Math.floor(Math.random()*2)+1;
break;
default:
left = 400;
top = Math.floor(Math.random()*400);
vy = Math.floor(Math.random()*5)-2;
vx = -Math.floor(Math.random()*2)-1;
break;
}
}
function check_bound() {
if (top>-5&&top<405&&top>-5&&top<405)
return;
init();
}
function update() {
top += vy;
left += vx;
check_bound();
}
}

class Player extends MovingObject {
function Player(gm, window, parent) {
size = 10;
color = 0xff0000ff;
super.MovingObject(gm, window, parent);
top = left = 200;
}
function update() {
var speed = 2;
if (System.getKeyState(VK_DOWN))
top += speed;
if (System.getKeyState(VK_UP))
top -= speed;
if (System.getKeyState(VK_LEFT))
left -= speed;
if (System.getKeyState(VK_RIGHT))
left += speed;
}
}

class GameMaster {
var window;
var parent;
var fore;

var player;
var enemies;

var timer1;

function GameMaster(win, par) {
window = win;
parent = par;

fore = new Layer(win, par);

with (fore) {
.visible = true;
.setSize(400, 400);
}

timer1 = new Timer(onTimer1, "");
timer1.interval = 25;
timer1.enabled = true;

player = new Player(this, window, fore);

enemies = [];
for (var i=0; i<99; ++i) {
enemies.add(new Enemy(this, window, fore));
}

}
function onTimer1() {
player.update();
for (var i=0; i<enemies.count; ++i) {
var e = enemies[i];
e.update();
if (Math.abs(e.top-player.top)<10&&Math.abs(e.left-player.left)<10)
window.close();
}
}

}

class GameWindow extends Window {
var master;

function GameWindow() {

super.Window();

add(new Layer(this, null));
primaryLayer.setSize(400, 400);
setInnerSize(400, 400);

caption = "特訓99";
visible = true;

master = new GameMaster(this, primaryLayer);
}
function finalize() {
super.finalize();
}
function action(ev) { }
}
var win = new GameWindow();


解說篇....下次再說吧 XD

2007年2月16日 星期五

初探Shattered Ruby(4) - 結論

前四篇對於Shattered Ruby似乎是貶多於褒,但其實我覺得這東西還是滿有趣的。有很多implement的功能是這裡沒提到的,大家可以詳閱WikiRDoc。其實他們已經做了很多了....

說實話,我沒看過一個工具建構以3D介面為基楚的遊戲那麼容易的,這應該是他最大的優點吧。要是目前這個專案還有人持續在maintain,那我應該毫不考慮的繼續玩下去。

Shattered Ruby中的設計流程其實很高階,我們可以看到Tutorial幾乎都是用DSL的方式在寫作。但根據抽象滲漏法則,當專案被擴大的時候不知道會不會遇到麻煩...

好啦,Shattered Ruby就寫到這囉,我應該短期之內不會再碰他了。有什麼意見的話也歡迎提出來吧 :)

初探Shattered Ruby(3) - 回到舊版本玩Example

我們可以看到Shattered Ruby Wiki首頁上有好幾個Tutorial&Example,但除了第一個之外,其他都不能在最新的版本上跑。如果我們要快速的玩玩看Example的話(不想去改code),可能回到舊版本是比較快的選擇。

我們以俄羅斯方塊為例吧....先把他解壓到某個地方,然後我們開始裝0.3.3版的Shattered Ruby

首先為了避免混淆,我們先將舊版本的ShatteredRuby全都反安裝。執行

C:\temp\mygame>gem uninstall shattered*

Select RubyGem to uninstall:
1. shattered-0.4.0.1
2. shattered_ogre-0.4-windows
3. shattered_pack-0.4.0.1
4. shattered_support-0.4.0.1
5. All versions
> 5
Successfully uninstalled shattered version 0.4.0.1
Successfully uninstalled shattered_ogre version 0.4
Successfully uninstalled shattered_pack version 0.4.0.1
Successfully uninstalled shattered_support version 0.4.0.1


然後再安裝0.3.3版的ShatteredRuby,可能又需要一些時間,因為另一個revision的DLL必需重新下載一次,安裝方式如下:

C:\temp\mygame>gem install -y shattered -v 0.3.3
Bulk updating Gem source index for: http://gems.rubyforge.org
Bulk updating Gem source index for: http://gems.rubyforge.org
Select which gem to install for your platform (i386-mswin32)
1. shattered_ogre 0.4 (windows)
2. shattered_ogre 0.3.3 (windows)
3. Cancel installation
> 2
Bulk updating Gem source index for: http://gems.rubyforge.org
Bulk updating Gem source index for: http://gems.rubyforge.org
Successfully installed shattered-0.3.3
Successfully installed shattered_ogre-0.3.3-windows
Successfully installed shattered_pack-0.4.0.1
Successfully installed shattered_support-0.4.0.1
下略

這樣就裝好了....但我們執行tetris時卻會發生錯誤....經過一連串的試誤,我發現在gemspec中這樣定義:shattered 0.3.3版需要shattered_pack及shattered_support0.3.3「以上」的版本。但事實上是不相容的。所以我們必需手動移除,再灌正確的版本。 (如果你熟希gems的話,你可以一開始就灌正確的版本,這樣比較快)

執行這四行吧:

gem uni shattered_support -i
gem uni shattered_pack -i
gem i shattered_support -v 0.3.3
gem i shattered_pack -v 0.3.3


事情還沒結束,還要上wiki的補丁。見A Confession這段。 (我試過了,上面手動灌0.3.3版和這個補丁兩者都是必需的,缺一不可,當初gemspec沒寫好的關係 Orz)

再跑一次tetris的script/runner



好啦....終於成功了....真累....

初探Shattered Ruby(2) - Follow the first tutorial

如果你玩過rails,你大概一秒鐘就可以知道shatter所產生的每個目錄的功用。不知道也沒關係,一開始我們需要注意的目錄其實很少:
  • app:整個遊戲的主程式都在這裡面
  • config:一些設定檔
  • script:一些ruby的script,runner可以啟動遊戲、generate可以幫助產生程式碼等等...
接下來就請跟著官網wiki的Your first move這篇教學做吧。
ruby script/generate state observation
與rails一樣,這個框架有內建一些generator幫助產生程式碼。他的概念大概是整個遊戲會在不同的state間轉換(state machine),每個state會有不同的input/output。現在我們產生了一個名為observation的state。但現在還不能執行,因為還沒有指定初始狀態。請照wiki上的步驟修改config/environment.rb (哇咧,連這個也要取和rails一樣的名字)

執行ruby script/runner吧,程式順利啟動了,雖然因為我們沒在場景上放上任何物件,所以畫面是一片全黑的,但至少應該可以順利執行了。

接下來,跟著教學做吧... ruby script/generate actor dirty_ruby
我們可以看到程式又自動產生了一堆檔案。跟著Wiki上的教學修改ObservationState物件,我們可以看到這是一個Domain Specific Language應用的例證...code真是有夠簡單的 :) 注意因為最新版的generator有問題,可能會當機,請參閱wiki的說明從範例source code中得到正確的mesh檔。

接下來我就不詳細講,跟著wiki跑完這個簡單的tutorial並沒有問題。你應該可以得到一個旋轉的紅寶石,並且可以控制他左右移動。



注意一點:我覺得這裡的View並不是MVC架構中的View,反而比較像是Data Model(定義了物件的mesh和行為)。如果你本來懂MVC架構,這可能是比較容易混淆的地方。

初探Shattered Ruby(1) - 簡介&安裝&Getting Start

之前在OGRE language binding中曾經稍微提到過Shattered Ruby,實在是對他滿好奇的,所以今天就抓下來玩了一下。還滿有趣的,但不是每件事都那麼美好:
  • 先說優點,玩起來真的很像在用Ruby on Rails寫網頁,操作有夠簡單,很少的程式碼就可以寫出一個小遊戲。
  • Shattered Ruby已經快要半年沒有更新了,根據官方blog的說法,開發人員有其他事情在忙著。
  • 有些功能在最新版本(0.4)反而無法使用,作者在wiki上說很快就會修正,但很久沒動靜了。另外,Wiki上很多tutorial及example並不能在最新版本上跑,document is out of date.
  • Lack of support,開發人員不知道什麼時候才會回來,搞不好一輩子都不會開發了,除非你有自己去延續開發的心理準備,否則...XD
  • Python-OGRE裡至少有提供了ODE, OgreNewt這些物理引擎的介面。如果在Shattered Ruby做碰撞判定,你不是要自己寫native binding,不然就是要拿慢到要死的Ruby去計算? (我是Ruby的擁護者,但Ruby的長處絕不在物理運算 XD)
目前看起來,做做小遊戲可以,拿來開發中大型遊戲還差的遠。所以我大概分成三篇稍微介紹一下Shattered Ruby就會結束了。

Shattered Ruby的安裝其實很簡單。首先你要先裝好Ruby及RubyGems (RubyGems是Ruby的套件管理系統)。在Windows系統下最方便的方法就是直接安裝Ruby One-click Installer就好了。

安裝好之後,進入命令提示字元,輸入
gem i -y shattered
他就會自動幫你下載/安裝好。台灣最近連RubyForge的速度好像變慢了些,下載的過程中包括了OGRE, mingw, DirectX的DLL檔,所以可能需要比較久的時間,請耐心等候。

OK...Let's getting start

先建立一個空目錄,然後進到命令提示字元,切換到該目錄下...如:

cd \
mkdir temp
cd temp
shattered mygame

最後一行是幫你建立一個遊戲專案目錄,讓我們看看裡面有什麼

C:\temp>dir mygame
磁碟區 C 中的磁碟沒有標籤。
磁碟區序號: 1CD9-4BA7

C:\temp\mygame 的目錄

2007/02/16 下午 04:57 <DIR> .
2007/02/16 下午 04:57 <DIR> ..
2007/02/16 下午 05:01 <DIR> app
2007/02/16 下午 04:59 <DIR> config
2007/02/16 下午 04:57 <DIR> doc
2007/02/16 下午 04:57 <DIR> log
2007/02/16 下午 04:57 454 Rakefile
2007/02/16 下午 04:57 905 README
2007/02/16 下午 04:57 <DIR> script
2007/02/16 下午 04:57 <DIR> test
2007/02/16 下午 04:57 <DIR> vendor
2 個檔案 1,359 位元組
9 個目錄 7,678,304,256 位元組可用


有用過Ruby on rails的人應該都看的出來,這架構明顯是抄rails的吧 XD 只是一個用來寫網頁一個用來寫遊戲。

到這裡...簡介與安裝就結束囉。我們下篇繼續

2007年2月15日 星期四

(閒聊)在blogspot貼程式

不知道什麼狀況會造成縮排的空格全部不見 (好像要到撰寫模式才會,但大部份狀況下又都不會)。對於python尤其可怕,這是我唯一一個用過的語言用縮排做blocking的....Orz

2007年2月14日 星期三

OGRE初學筆記--以Python為例 索引篇

原本只是想把筆記整理一下而已,根本沒有想過會寫到那麼詳細...在將近12小時之內打了十篇文章,連我自己都嚇了一跳。

其實我自己也是OGRE初學者,對Python的也是很不熟(我是Ruby的愛用者)。很可能會有錯誤,也請大家幫忙指教。這些筆記和原本的Wiki相比少了很多東西,建議大家還是詳閱Wiki比較好。希望大家有了這些中文筆記,能夠比較快上手一些。
近期應該會再補上OIS輸入、使用py2exe發佈遊戲....今天實在太累了,到此為止囉。

就這樣囉,之前研究一陣子的東西,今天有空一次把他寫出來,超累的....祝大家去死去死節快樂 XD

Python-Ogre Tutorial 4&5

請先閱讀「寫在OGRE Tutorial前面」,本文假設您的目錄配置都和我一樣。
本文參考PyOgre Beginner Tutorial 4, PyOgre Beginner Tutorial 5

為什麼要把這兩個tutorial合起來呢?這兩篇講的是OGRE Input,但是OGRE內建的Input是不被推薦使用的,所以我並不想詳談。

簡單的說,教學四講的是unbuffered input,在迴圈中不斷的去polling有沒有輸入事件發生。教學五講的是buffered input,比較像event-driven的感覺,Listener Pattern有寫過Java的人應該都不陌生。好....Input講完了。

輸入的部份,推薦另外用OISSDL處理。Python-OGRE已經內建了OIS了。

事實上我在Python-OGRE裡根本試不出內建Input怎麼用,SampleFramework裡也是直接使用OIS,所以找不到範例,在forum也找不到討論。但是看到這段程式碼:

if (ogre.OgreVersion[0]+ ogre.OgreVersion[1]) == "12": # ogre 1.2.3 is latest SDK
from Ogre.sf import *
else: # assume it's ogre version 1.3, the CVS version that needs OIS
from Ogre.sf_OIS import *

似乎指出現在(1.4版Ogre)的Python-OGRE已經不能用內建輸入了。

另外請詳閱Wiki,了解什麼是FrameListener。




之後會再弄個OIS輸入處理篇吧,這一系列的文章就到此結束。

Python-Ogre Tutorial 3

請先閱讀「寫在OGRE Tutorial前面」,本文假設您的目錄配置都和我一樣。
本文參考PyOgre Beginner Tutorial 3

先建立C:\Python25\Python-Ogre-0.8\demos\tutorial\tut3.py,貼上這些程式碼:


# coding=big5

import Ogre as ogre
import SampleFramework

class TutorialApplication(SampleFramework.Application):
def _createScene(self):

self.sceneManager.setWorldGeometry("terrain.cfg")
self.sceneManager.setSkyBox(True, "Examples/SpaceSkyBox")

def _chooseSceneManager(self):
self.sceneManager = self.root.createSceneManager(ogre.ST_EXTERIOR_CLOSE)
# 選擇場景管理器

if __name__ == '__main__':
ta = TutorialApplication()
ta.go()


wiki上使用getSceneManager,到Python-OGRE好像要用createSceneManger。這部份我不太確定,不過反正他work了:



有關SceneManager的細節我就不多談,有興趣的請自行查閱。我把焦點放在Terrian, Sky, Fog上。

地型
在一開始我們就copy了一些檔案到tutorial資料夾下,包括了terrian.cfg。其中指定了地型的材質、參數等等。最重要的是這一段:
# Heightmap source
PageSource=Heightmap
# Heightmap-source specific settings
Heightmap.image=terrain.png

terrian.png中,其實就是一個以顏色表示各點高度的heightmap。

天空

天空的貼圖法有三種:
  1. SkyBoxes,將天空貼在一個cube上
  2. SkyDomes,將天空貼在一個球體上 (但事實上他還是render在一個cube)
  3. SkyPlanes,將天空貼在平面上
Wiki上的推薦使用法:
  • 如果你會看到y軸為負的方向(例如從天空之城向下看),使用SkyBoxes
  • SkyDomes在接近y=0的地方會沒貼到圖,所以如果你的場景是被山或建築物圍繞著,推薦使用。
  • SkyPlane的優點是GPU cost極小,另外,其他兩種貼圖方式和fog並用時可能產生問題 (請詳閱wiki種fog一節)



再修改一下_createScene...

def _createScene(self):
self.sceneManager.setWorldGeometry("terrain.cfg")

fadeColour = (0.9, 0.9, 0.9)
self.sceneManager.setFog(ogre.FOG_LINEAR, fadeColour, 0, 50, 515)
self.renderWindow.getViewport(0).backgroundColour = fadeColour

self.sceneManager.setSkyDome(True, "Examples/CloudySky", 5, 500)




被濃霧籠罩的山谷...




有關fog及plane的搭配請務必詳閱wiki,我在這就不多談了...

Python-Ogre Tutorial 2

請先閱讀「寫在OGRE Tutorial前面」,本文假設您的目錄配置都和我一樣。
本文參考PyOgre Beginner Tutorial 2

先建立C:\Python25\Python-Ogre-0.8\demos\tutorial\tut2.py,貼上這些程式碼:

# coding=big5

import Ogre as ogre
import SampleFramework

class TutorialApplication(SampleFramework.Application):

def _createScene(self):
pass

def _createCamera(self):
self.camera = self.sceneManager.createCamera("PlayerCam")
# 建立PlayerCam (玩家攝影機物件)
self.camera.setPosition = (0, 10, 500)
# 設定位置
self.camera.lookAt((0, 0, 0))
# 設定方向
self.camera.nearClipDistance = 5
# 太近的東西看不到 (距離小於5)

def _createViewports(self):
vp = self.renderWindow.addViewport(self.camera)
vp.backgroundColour = (0, 0, 0)
self.camera.aspectRatio = vp.actualWidth / vp.actualHeight

if __name__ == '__main__':
ta = TutorialApplication()
ta.go()

  • 這次我把註解直接打在程式碼裡面了,如果你的程式碼有中文的話,記得加上第一行coding的宣告,否則會沒辦法執行
  • 注意紅字的地方,position property沒辦法用,要用setPosition

經過了這段修改,我們得到一個....全黑的畫面?是的。

由於TutorialApplication是繼承SampleFramework.Application類別,就算我們不設定Camera及Viewport,上層的物件也會自動幫我們建立。現在我們只是Override上層的method,手動建立一次而已。

Camera簡單講就是玩家視角,Viewport這裡不用詳細解釋,有興趣深入了解請詳讀wiki吧。

光線與陰影 (Light&Shadow)

重頭戲來了....廢話不多說,我們來試試吧....修改_createScene函式:

def _createScene(self):
sceneManager = self.sceneManager
sceneManager.ambientLight = (0, 0, 0)
# 不要ambient光源
sceneManager.shadowTechnique = ogre.SHADOWTYPE_STENCIL_ADDITIVE
# 設定陰影種類

ent = sceneManager.createEntity("Ninja", "ninja.mesh")
ent.castShadows = True # this is not actually needed
# 設定這個entity要有陰影效果
sceneManager.rootSceneNode.createChildSceneNode().attachObject(ent)
# 在scene中放置一個忍者

plane = ogre.Plane((0, 1, 0), 0)
# 建立地板平面
mm = ogre.MeshManager.getSingleton()
mm.createPlane('ground', ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME,
plane, 1500, 1500, 20, 20, True, 1, 5, 5, (0, 0, 1))
# 建立地板平面的Mesh

ent = sceneManager.createEntity("GroundEntity", "ground")
sceneManager.rootSceneNode.createChildSceneNode().attachObject(ent)
ent.setMaterialName("Examples/Rockwall")
# 將地板明面貼上岩石材質
ent.castShadows = False

light = self.sceneManager.createLight("Light1")
light.type = ogre.Light.LT_POINT
# 建立一個點光源
light.setPosition(0, 150, 250)
# 設定光源位置

light.setDiffuseColour(1.0, 0.0, 0.0)
light.setSpecularColour(1.0, 0.0, 0.0)

注意紅色的部份,是與PyOgre API不相容的地方。先看看結果吧:



漆黑的忍者拖著長長的倒影 (驚)

注意我們把ambient光源設定為0了,在這裡我要稍微解釋一下。
一般3D圖學中的打光效果分為:
  • Ambient (我不太會翻譯...這個字是"周圍的"的意思)
    舉個例子,在一個漆黑的房間中,有一個門縫、窗縫透了一點光進來。這時整個房間都會近乎均勻的被打到一點點的光線。這個其實是經過很多反射、散射所造成的效果,但是真的去計算這些物理現像實在太難了,所以我們就假設所有物體都會被定量的光線照到。(就算不是realtime rendering,在photorealistic rendering也是有用ambient的)
  • Diffuse (散射)
  • Specular (鏡面反射)


現在我們將ambient設為零,而光源在忍者的背面,所以從正面看當然就是一片漆黑了。注意程式的最後兩行將光源設定圍紅色。

除了ambient外,Diffuse和Specular都是假定光線是從某種方向照過來的。OGRE裡光源有三種:
  1. Point:點光源
  2. Spotlight:聚光燈、手電筒,需要設定廣角範圍
  3. Directional:指向性光源,假設光源從無線遠處射來(如太陽)。需要設定範圍但不用設定位置。
先來個有三種光源的範例吧,在剛剛的_createScene後再加上:

light = sceneManager.createLight("Light3")
light.type = ogre.Light.LT_DIRECTIONAL
# 從無限遠處射來的光線
light.setDiffuseColour(.25, .25, 0)
light.setSpecularColour(.25, .25, 0)
# 設定顏色
light.setDirection(0, -1, 1)
# 要指定方向,不用指定位置

light = sceneManager.createLight("Light2")
light.type = ogre.Light.LT_SPOTLIGHT
# 聚光燈形式的光源
light.setDiffuseColour(0, 0, 1.0)
light.setSpecularColour(0, 0, 1.0)
# 顏色
light.setDirection(-1, -1, 0)
light.setPosition(300, 300, 0)
# 位置方向
light.setSpotlightRange(ogre.Degree(35), ogre.Degree(50))
# 廣角




看到三個方向的影子了,雖然這張圖看不出什麼差,但這是三種不同光源(藍字)造成的。照慣例,紅字是與wiki不同的地方。

另外,陰影模式也有三種:
  1. SHADOWTYPE_TEXTURE_MODULATIVE 最差,最少運算資源
  2. SHADOWTYPE_STENCIL_MODULATIVE
  3. SHADOWTYPE_STENCIL_ADDITIVE 最強,最多運算資源
有興趣深入了解的請看wiki

Python-Ogre Tutorial 1-2

上一篇..

座標系

OGRE的座標系系統是:x左右, y上下, z前後
有些其他系統會將y和z顛倒過來

再修改一下_createScene:

def _createScene(self):
sceneManager = self.sceneManager
sceneManager.ambientLight = (1, 1, 1)

ent1 = sceneManager.createEntity("Robot", "robot.mesh")
node1 = sceneManager.rootSceneNode.createChildSceneNode("RobotNode")
node1.attachObject(ent1)

ent2 = sceneManager.createEntity("Robot2", "robot.mesh")
node2 = sceneManager.rootSceneNode.createChildSceneNode("RobotNode2", (50, 0, 0))
node2.attachObject(ent2)


放了兩台機器人:)
注意倒數第二行紅字的部份,第二台機器人有(50, 0, 0)的位移量。



def _createScene(self):
sceneManager = self.sceneManager
sceneManager.ambientLight = (1, 1, 1)

ent1 = sceneManager.createEntity("Robot", "robot.mesh")
node1 = sceneManager.rootSceneNode.createChildSceneNode("RobotNode")
node1.attachObject(ent1)

ent2 = sceneManager.createEntity("Robot2", "robot.mesh")
node2 = node1.createChildSceneNode("RobotNode2", (0, 100, 0))
node2.attachObject(ent2)

再注意倒數第二行,我們這次不把第二台機器人接上SceneManager的root SceneNode,而接上第一台機器人的SceneNode,並且給了(0, 100, 0)的位移量 (正上方一百單位)。



注意:ent2的位置現在完全跟隨著ent1,要是第一台機器人移動,第二台也會跟著動。Wiki中提到可以拿來做grouping (像星海的航空母艦子機 XD)

來看看旋轉類的效果吧:


def _createScene(self):
sceneManager = self.sceneManager
sceneManager.ambientLight = (1, 1, 1)

ent1 = sceneManager.createEntity("Robot", "robot.mesh")
node1 = sceneManager.rootSceneNode.createChildSceneNode("RobotNode")
node1.attachObject(ent1)
node1.yaw(ogre.Degree(-90))

ent2 = sceneManager.createEntity("Robot2", "robot.mesh")
node2 = sceneManager.rootSceneNode.createChildSceneNode("RobotNode2", ogre.Vector3(50, 0, 0))
node2.attachObject(ent2)
node2.pitch(ogre.Degree(-90))

ent3 = sceneManager.createEntity("Robot3", "robot.mesh")
node3 = sceneManager.rootSceneNode.createChildSceneNode("RobotNode3", ogre.Vector3(100, 0, 0))
node3.attachObject(ent3)
node3.roll(ogre.Degree(-90))




注意紅色那三行就對了,不同的轉向函式。

最後,關於縮放(scaling)功能...

Wiki上的語法都不能用:
node.setScale(0.5, 0.2, 1)
node.scaleBy(2, 10, 1)


要用這個:
node.setScale(0.5, 0.2, 1)



這裡有一些PyOgre及Python-Ogre的差異,可是沒寫到scale,害我自己找半天 :(
http://python-ogre.python-hosting.com/wiki/FAQindex:見Converting from PyOgre




Tutorial 1的部份寫了最多,接下來的幾個Tutorial會寫必較少。不是我偷懶喔,而是點到為止就好了。尤其是OGRE Input處理的部份,最後做遊戲時是不太會用到的。

Python-Ogre Tutorial 1

請先閱讀「寫在OGRE Tutorial前面」,本文假設您的目錄配置都和我一樣。
本文參考PyOgre Beginner Tutorial 1

讓我們跟著上面那個tutorial的步驟一步一步做。首先我們看到這段程式,使用你熟悉的編輯器將他存到C:\Python25\Python-Ogre-0.8\demos\tutorial\tut1.py中

from pyogre import ogre
import SampleFramework

class TutorialApplication(SampleFramework.Application):
def _createScene(self):
pass

if __name__ == '__main__':
ta = TutorialApplication()
ta.go()


執行看看
cd \Python25\Python-Ogre-0.8\demos\tutorial
python tut1.py

結果當然有error囉,因為他引用了PyOgre的package名稱而非Python-Ogre

將程式的前兩行改成

import Ogre as ogre
import SampleFramework




It works!!右下角有OGRE的mark,左下角有FPS的效能數據。整個畫面黑黑的一片,因為我們還沒有放任何東西在場景中。其實PyOgre和Python-OGRE最大的差別應該就是在這個import package name吧,其他程式碼幾乎都可以直接work。

接下來我就簡化地follow tutorial,還是推薦大家詳閱原版的tutorial喔!!

我們稍微改寫一下_createScene函式:

def _createScene(self):
sceneManager = self.sceneManager
sceneManager.ambientLight = ogre.ColourValue(1, 1, 1)

ent1 = sceneManager.createEntity("Robot", "robot.mesh")
node1 = sceneManager.rootSceneNode.createChildSceneNode("RobotNode")
node1.attachObject(ent1)


然後再執行看看...


可以按上下左右、移動滑鼠試試喔!

第一行取得了sceneManager物件,在OGRE的架構中,所有你看的見的東西都是被這個物件管理的。
第二行設定了光源,要是拿掉了這行,畫面就會變成一片漆黑了。ambientLight是一種光源的類型,等到光源那節我再詳細介紹。

第三到五行在畫面上放置了一個機器人,這部份比較複雜一些,我們需要了解一些物件:
  • MoveableObject:任何可以移動的物體。其實我們在這裡都不會直接取用他
  • Entity:MoveableObject的子類別,你可以將任何可以用3D Mesh表示的物件看成一個Entity。(例如:人、魚、鳥、車)
    然而燈光、攝影機...等等沒有3D模型的東西,他們是MoveableObject但不是Entity。
  • SceneNode:這個概念比較抽象...我真的不太會解釋,請務必看wiki原文。
    總而言之,每一個SceneNode都可以有他的parent及children,形成一個tree的結構。SceneNode記錄著相對於其parent的位置及方向。
    SceneManager中有個root SceneNode,一般來說他應該是一切其他SceneNode的根源。我是把他想成類似世界原點座標..


簡單來說,要在場景上放上一個物件至少要做三件事:
  1. 建立Entity
    程式碼第三行,載入robot的mesh並建立了這個entity
  2. 建立SceneNode
    程式碼第四行,注意他是從SceneManager建立這個SceneNode的。SceneNode必需以某種方式與SceneManager的root連結,我們才可以看的到他。
  3. 將Entity附在SceneNode上(程式碼第五行)


文章太長了,拆成兩篇...請看Python-Ogre Tutorial 1-2

寫在Python-OGRE Tutorial前面

接下來讓我們來看一些Tutorial吧。

從介紹OGRE的第一篇我就提到,學OGRE從Tutorial開始是最快的。在OGRE官方Wiki上有一系列的Tutorial教學

另外在PyOgre頁面中也有五個Tutorial,這五個Tutorial其實和上面的Basic Tutorial 1~5內容是一樣的,只是程式語言從C++變成了Python。

但是,目前並沒有為最新的Python-Ogre寫的Tutorial。事實上PyOgre及Python-Ogre的API根本就差不多,只有一些細微的差異。但如果你用PyOgre的程式直接貼到Python-Ogre,還是會出一點錯的。

接下來我會把這五個Tutorial的筆記整理出來,並且在Python-OGRE上執行。希望可以讓大家很快的上手Python-OGRE。

如果你的遊戲運算量、動作性沒有那麼高,那用Python開發應該不錯。但這五個Tutorial只是教你OGRE的基本操作,無論如何,C++版本的那整套Tutorial還是推薦大家一定要去去看一下的。




開始囉,讓我們先準備一下環境。我很偷懶的直接在c:\Python25\Python-Ogre-0.8\demos建一個tutorial的目錄,並且將所需的檔案copy過去,在命令提示字元下執行這些指令就可完成:

cd \Python25\Python-Ogre-0.8\demos
md tutorial
cd tutorial
copy ..\ogre\SampleFramework.py .
copy ..\ogre\*.cfg .


SampleFramework.py是一個寫好的遊戲框架,定義了一些行為(例如說按上下左右攝影機就會移動,移動滑鼠就會轉向)。真正的implementation應該是在C:\Python25\Lib\site-packages\Ogre\sf.py,在前五個教學中我們先不用管他是什麼。

*.cfg是一些設定檔案,如果你完全照我的目錄配制的話,那就先不用管他是什麼東西了。

好,讓我們進入第一個Tutorial吧...

待續

Python-Ogre安裝

Python-Ogre的安裝反而比C++環境的安裝簡單的多。
  1. 安裝Python 2.5 Windows installer (假設裝到C:\Python25)
  2. 抓下Python-Ogre 0.80 Windows Release for Python 2.5,解壓至C:\Python25
  3. 進到命令提示字元,輸入
    cd \python25\Python-Ogre-0.8
    ..\python setup.py install
  4. 抓下Media for Python-Ogre,解壓後將C:\Python25\Python-Ogre-0.8\demos\media覆蓋
這樣就安裝完成囉。令外OGRE Wiki推薦安裝Psyco,可以加速python程式的執行。

來執行看看吧

cd \Python25\Python-Ogre-0.8\demos\ogre
\Python25\python.exe Demo_Lighting.py




成功!!




可以在我的電腦->內容->進階->環境變數->系統變數->path
在變數值後面加上;C:\Python25
記得重新開啟command console

就不用執行\Python25\python.exe 直接打python就好了,筆者的電腦因為有兩個版本的python,所以不做此設定。

OGRE language binding

縱觀近年來程式語言的發展,我想大家應該都會同意動態、腳本語言可以縮短程式開發的時間。例如:
等等...

OGRE本身是使用C++寫的,但其龐大的社群使得我們可以找到一些其他語言binding的資源:

PyOgre

PyOgre的歷史應該滿悠久的,使用SWIG將OGRE與Python語言做結合,讓我們可以在Python中直接取用OGRE的物件。

PyOgre最新的版本應該是1.2版,但是官網的binary release竟然停留在2005年的1.0.6版,不知道在想什麼 =.= Wiki上面有熱心的網友自己編譯的win32 binary,但連結已失效了。目前取得最新版本的唯一方式就是自己去SVN checkout然後編譯。

PyOgre的優點應該是比較stable(應為已經開發很久了)、官網tutorial(在PyOgre的下半部)一定可以work。除此之外,官網論壇上的人似乎都比較推薦Python-Ogre。

Python-Ogre

Python-Ogre是另外一套Ogre的Python binding,相較於PyOgre使用SWIG,Python-Ogre使用Py++來自動化的做Warpper的動作。當然使用者可以不用理會這些,重點是Python-Ogre雖然發展不到半年的時間,但開發的動作相當迅速,應該可望完全取代掉PyOgre。

Shattered Ruby

Shattered Ruby是一套使用OGRE為底層的Game Framework,他不僅是做Warpper的動作而已,還開發了一些工具來加速遊戲設計的流程。

我想這個開發者一定對於Ruby on Rails的網頁框架十分熟悉,而把Rails這個網頁開發的很多概念拿到遊戲開發上,如:
  • DRY: Don't repeat yourself
  • CoC: Convention before configuration
  • Generator
  • Domain Specific Language
等等...我覺得是一個相當有潛力的project,可惜使用者族群沒有Python的多。




當然,有其利必有其弊。這些動態語言的開發速度雖然勝過C++,但執行速度是一定比不過C++的,如何平衡當然就看你的應用囉。例如:
  • 格鬥遊戲、3D射擊遊戲:別鬧了,乖乖用C++寫吧。動態語言可以拿來輔助做流程控制用。
  • 益智遊戲、冒險解迷:在運算量不大的前題下,整個遊戲都使用動態語言寫也是有可能的。

OGRE + MinGW + Code::Blocks 安裝

OGRE本身是用C++寫的,所以利用OGRE撰寫程式最直接的方法也就是使用C++。在OGRE官網 提供了幾種開發環境的SDK,當然你要自己在其他環境compile應該也是可以的,但新手的話最好照著他的instruction做吧。
  • VC.net 2005:雖然我滿不想用微軟的IDE,但為了方便我一開始試灌了這個方案。結果我發現還要另外抓四百多MB的windows platform SDK後,一怒之下就放棄了。
  • VC.net 2003:聽說這版不用灌platform SDK,我不太確定,要試的人請自己survey。
  • MinGW+CodeBlock:最後我還是選擇了gcc當我的底層compiler,於是就使用了這個方案。灌起來應該比VC麻煩一些吧。
如果你已經決定了使用MinGW+CodeBlock方案的話,請先詳閱這篇文章並跟著一步一步做。(注意:在Code::Block 1.0 stable release後,安裝的步驟應該會變簡單很多,請隨時注意該wiki文章的內容有沒有更新)

MinGW

MinGW是什麼?有試著在windows下用gcc的人應該都知道,MinGW和cygwin是目前兩個將gcc port到windows下的方案。所以我們的編譯器就靠他了。

注意,不要下載官方的MinGW,因為OGRE對於libstdc++做了一些修正,請下載OGRE提供的MinGW ToolBox

Code::Blocks

這是一套聽說要追過Dev-C++的Open Source IDE (我最近很少在寫C,所以不太清楚)

注意:不要使用Code::Blocks 1.0 RC2及以前的版本,會當機。
Code::Blocks的官方論壇幾乎每天會放出新的nightly build。OGRE官網是建議抓最新的nightly build,不過抓之前可以看看該thread有沒有重大的bug :)

除了抓Wiki那兩個檔案外,記得抓mingwm10.7z,才有和mingw溝通的DLL

OGRE SDK

沒啥好說的,one-click installer

Direct9 SDK

最好裝一下,如果你跟我一樣不爽裝(XD)的話....

先 把sample build & run看看,如果跳出找不到D3DX9_??.dll (??可能是某兩個數字),去google 搜尋這個檔案的名稱就可以抓到了,把他丟進windows\system32之中。 (記得掃毒,但google明列前矛的網站應該還OK)

這樣做的話,在debug build中可能會需要D3DX9_??d.dll (多了一個d字,debug版本的dll)而當掉。如果你還是不想用DX的話...

在C:\OgreSDK\bin\debug (或你的安裝資料夾下) ,找到plugins.cfg中
Plugin=RenderSystem_Direct3D9
將這行最前面加個井號註解掉,不要使用D3D,完全用OpenGL就好了。

來玩OGRE吧

Wiki最下面的那些setting可以先不要看,如果你抓的是2007.2.13之後的Code::Block nightly build,他裡面就有附OGRE的template project了。



正常的話可以直接build



萬歲!!成功 XD

(下一章應該寫OGRE + Python)

2007年2月13日 星期二

What's OGRE?

OGRE是目前最大的open source 3d rendering engine之一,這四個字其實是Open soure GRaphic Engine的縮寫。

這套引擎的歷史已經很悠久了,相信很多人沒玩過也聽過。那我只在這裡做簡單的介紹:
  • 他是一個繪圖引擎(rendering, 對岸好像稱為絢染)
  • 他不是一個遊戲引擎,這個函式庫專注在3D繪圖上,少了音效、輸入、網路(optional)等部份。(事實上OGRE有很粗糙的輸入處理功能,但正式製作遊戲時不建議使用)
為什麼要用OGRE?他與DirectX, OpenGL有什麼不同?
  • DirectX及OpenGL都是較底層的繪圖函式庫,直接與硬體做溝通。
  • DirectX是微軟的產品,只能在Windows系列上面跑。OpenGL是工業標準,能夠跨平台在Windows, Linux, Mac OSX上執行。
  • OGRE提供了比較高階的C++物件導向封裝,學習起來比較簡單。他可以選擇呼叫底層的DirectX和OpenGL,達到跨平台的目的,在windows下也可使用表現可能較好的DX。
介紹OGRE的一些link

學習OGRE最快的方法應該就是照著OGRE Tutorial跑一遍,千萬不要一開始就去看API Documentation,保證你看的頭昏腦脹。由於目前OGRE的繁中資源還滿少的,接下來我應該會把我的筆記簡單的整理出來,其實我自己也是最近在跑tutorial而已,對整個開發流程還只是一知半解,如有謬誤也請大家幫忙更正。

P.S. ogre這個字本身是食人魔的意思,開發人員明顯都是愛玩遊戲的人,固意把名稱縮成這四個字 XD