2009年2月19日 星期四

java副檔名過濾器

import java.io.File;
import java.io.FileFilter;

public class ExtensionFileFilter implements FileFilter {

private String[] extension;


public ExtensionFileFilter(String[] s) {
extension=s;
}

public boolean accept(File file) {
if (file.isDirectory()) {
return false;
}

String name = file.getName();
// find the last
int index = name.lastIndexOf(".");
if (index == -1) {
return false;
} else if (index == name.length() - 1) {
return false;
} else{
for(int i=0;i<extension.length;i++){
boolean b=extension[i].equals(
name.substring(index + 1).toLowerCase());
if(b){
return true;
}
}
return false;
}
}
}
用法
String[] fileExtension = {"jpg", "jpeg"};
files = path.listFiles(new ExtensionFileFilter(fileExtension));

關於java中初始化界面中jProgressBar設計

//設定初使化的JFrame
frame = new InitFrame();
Thread thread = new Thread(new Runnable() {

public void run() {
//在這裡做設定主畫面的設定
for(){
frame.progress.setValue(i);
}

}
});
thread.start();

JFrame置中

//要放在pack,setVisible之後。
frame.setLocationRelativeTo(null);

2009年2月13日 星期五

Script language~Lua和Python比較




Lua

Python

效能

普通
檔案



功能和套件普通

齊全
使用率

22名(0.46%)

7名(4.7%)

版權宣告

MIT

Python license

安裝需求-MAC

不用,可嵌入主程式MAC內建

安裝需求-WINDOWS

不用,可嵌入主程式須安裝Python

更新疑慮

無更新問題

可能有







GUI-wxWidget



安裝需求-MAC須安裝wxLua,wxWidgetMAC內建
安裝需求-WINDOWS須安裝wxLua,wxWidget須安裝wxPython,wxWidget
附註

wxLua和wxWidget有整合版wxPython和wxWidget要分開安裝
更新疑慮無更新問題可能有
其他問題



wxPython目前不支援Python 3k

(Python 3k向下不相容)







ftp功能

須LuaSocket

內建


2009年2月12日 星期四

Embedded Python + wxPython + OpenGL

先看一下wxPython官網關於opengl的教學
try:
import wx
from wx import glcanvas
except ImportError:
raise ImportError, "Required dependency wx.glcanvas not present"

try:
from OpenGL.GL import *
except ImportError:
raise ImportError, "Required dependency OpenGL not present"

class GLFrame(wx.Frame):
"""A simple class for using OpenGL with wxPython."""

def __init__(self, parent, id, title, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
name='frame'):
#
# Forcing a specific style on the window.
# Should this include styles passed?
style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE

super(GLFrame, self).__init__(parent, id, title, pos, size, style, name)

self.GLinitialized = False
attribList = (glcanvas.WX_GL_RGBA, # RGBA
glcanvas.WX_GL_DOUBLEBUFFER, # Double Buffered
glcanvas.WX_GL_DEPTH_SIZE, 24) # 24 bit

#
# Create the canvas
self.canvas = glcanvas.GLCanvas(self, attribList=attribList)

#
# Set the event handlers.
self.canvas.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent)
self.canvas.Bind(wx.EVT_SIZE, self.processSizeEvent)
self.canvas.Bind(wx.EVT_PAINT, self.processPaintEvent)

#
# Canvas Proxy Methods

def GetGLExtents(self):
"""Get the extents of the OpenGL canvas."""
return self.canvas.GetClientSize()

def SwapBuffers(self):
"""Swap the OpenGL buffers."""
self.canvas.SwapBuffers()

#
# wxPython Window Handlers

def processEraseBackgroundEvent(self, event):
"""Process the erase background event."""
pass # Do nothing, to avoid flashing on MSWin

def processSizeEvent(self, event):
"""Process the resize event."""
if self.canvas.GetContext():
# Make sure the frame is shown before calling SetCurrent.
self.Show()
self.canvas.SetCurrent()

size = self.GetGLExtents()
self.OnReshape(size.width, size.height)
self.canvas.Refresh(False)
event.Skip()

def processPaintEvent(self, event):
"""Process the drawing event."""
self.canvas.SetCurrent()

# This is a 'perfect' time to initialize OpenGL ... only if we need to
if not self.GLinitialized:
self.OnInitGL()
self.GLinitialized = True

self.OnDraw()
event.Skip()

#
# GLFrame OpenGL Event Handlers

def OnInitGL(self):
"""Initialize OpenGL for use in the window."""
glClearColor(1, 1, 1, 1)

def OnReshape(self, width, height):
"""Reshape the OpenGL viewport based on the dimensions of the window."""
glViewport(0, 0, width, height)

glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-0.5, 0.5, -0.5, 0.5, -1, 1)

glMatrixMode(GL_MODELVIEW)
glLoadIdentity()

def OnDraw(self, *args, **kwargs):
"Draw the window."
glClear(GL_COLOR_BUFFER_BIT)

# Drawing an example triangle in the middle of the screen
glBegin(GL_TRIANGLES)
glColor(0, 0, 0)
glVertex(-.25, -.25)
glVertex(.25, -.25)
glVertex(0, .25)
glEnd()

self.SwapBuffers()

app = wx.PySimpleApp()
frame = GLFrame(None, -1, 'GL Window')
frame.Show()

app.MainLoop()
app.Destroy()
但是不能執行,這是因為mac中沒有pyOpenGL。而我們也不想安裝pyOpenGL,所以先把opengl的部份換掉
try:
import wx
import mymodule
from wx import glcanvas
except ImportError:
raise ImportError, "Required dependency wx.glcanvas not present"

class GLFrame(wx.Frame):
"""A simple class for using OpenGL with wxPython."""

def __init__(self, parent, id, title, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
name='frame'):
#
# Forcing a specific style on the window.
# Should this include styles passed?
style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE

super(GLFrame, self).__init__(parent, id, title, pos, size, style, name)

self.GLinitialized = False
attribList = (glcanvas.WX_GL_RGBA, # RGBA
glcanvas.WX_GL_DOUBLEBUFFER, # Double Buffered
glcanvas.WX_GL_DEPTH_SIZE, 24) # 24 bit

#
# Create the canvas
self.canvas = glcanvas.GLCanvas(self, attribList=attribList)

#
# Set the event handlers.
self.canvas.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent)
self.canvas.Bind(wx.EVT_SIZE, self.processSizeEvent)
self.canvas.Bind(wx.EVT_PAINT, self.processPaintEvent)

#
# Canvas Proxy Methods

def GetGLExtents(self):
"""Get the extents of the OpenGL canvas."""
return self.canvas.GetClientSize()

def SwapBuffers(self):
"""Swap the OpenGL buffers."""
self.canvas.SwapBuffers()

#
# wxPython Window Handlers

def processEraseBackgroundEvent(self, event):
"""Process the erase background event."""
pass # Do nothing, to avoid flashing on MSWin

def processSizeEvent(self, event):
"""Process the resize event."""
if self.canvas.GetContext():
# Make sure the frame is shown before calling SetCurrent.
self.Show()
self.canvas.SetCurrent()

size = self.GetGLExtents()
self.OnReshape(size.width, size.height)
self.canvas.Refresh(False)
event.Skip()

def processPaintEvent(self, event):
"""Process the drawing event."""
self.canvas.SetCurrent()

# This is a 'perfect' time to initialize OpenGL ... only if we need to
if not self.GLinitialized:
self.OnInitGL()
self.GLinitialized = True

self.OnDraw()
event.Skip()

#
# GLFrame OpenGL Event Handlers

def OnInitGL(self):
"""Initialize OpenGL for use in the window."""
mymodule.OnInitGL()

def OnReshape(self, width, height):
"""Reshape the OpenGL viewport based on the dimensions of the window."""
mymodule.OnReshape(width,height)

def OnDraw(self, *args, **kwargs):
"Draw the window."
glClear(GL_COLOR_BUFFER_BIT)

# Drawing an example triangle in the middle of the screen
mymodule.OnDraw()

self.SwapBuffers()

app = wx.PySimpleApp()
frame = GLFrame(None, -1, 'GL Window')
frame.Show()

app.MainLoop()
app.Destroy()
這時你因該注意到mymodule這個未知的Module吧。當然現在還不能執行。
我們必須在C的檔案中,定義mymodule(詳細說明請參閱thinker的為 Embedded Python 擴充 Submodules)。
PyObject *OnInitGL(PyObject *self) {
glClearColor(1, 1, 1, 1);
return PyString_FromString("GL Init");
}
PyObject *OnReshape(PyObject *self, PyObject *args) {
int a, b;

if(!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL;
glViewport(0, 0, a, b);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-0.5, 0.5, -0.5, 0.5, -1, 1);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return PyString_FromString("Reshape");
}
PyObject *OnDraw(PyObject *self) {
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(0, 0, 0);
glVertex2f(-0.25, -0.25);
glVertex2f(0.25, -0.25);
glVertex2f(0, 0.25);
glEnd();
return PyString_FromString("Draw");
}

PyMethodDef methods[] = {
{"OnInitGL", (PyCFunction)OnInitGL, METH_NOARGS, "OnInitGL document"},
{"OnReshape", (PyCFunction)OnReshape, METH_VARARGS, "OnReshape document"},
{"OnDraw", (PyCFunction)OnDraw, METH_NOARGS, "OnDraw document"},
{ NULL, NULL, 0, NULL}
};
在main()中加入
Py_InitModule("mymodule", methods);
整個主程式
//將SCRIPT_PATH設定成py檔和執行檔的相對路徑
#define SCRIPT_PATH "/"
//設定mymodule
PyObject *OnInitGL(PyObject *self) {
glClearColor(1, 1, 1, 1);
return PyString_FromString("GL Init");
}
PyObject *OnReshape(PyObject *self, PyObject *args) {
int a, b;

if(!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL;
glViewport(0, 0, a, b);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-0.5, 0.5, -0.5, 0.5, -1, 1);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return PyString_FromString("Reshape");
}
PyObject *OnDraw(PyObject *self) {
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(0, 0, 0);
glVertex2f(-0.25, -0.25);
glVertex2f(0.25, -0.25);
glVertex2f(0, 0.25);
glEnd();
return PyString_FromString("Draw");
}

PyMethodDef methods[] = {
{"OnInitGL", (PyCFunction)OnInitGL, METH_NOARGS, "OnInitGL document"},
{"OnReshape", (PyCFunction)OnReshape, METH_VARARGS, "OnReshape document"},
{"OnDraw", (PyCFunction)OnDraw, METH_NOARGS, "OnDraw document"},
{ NULL, NULL, 0, NULL}
};
int main(int argc, char* argv[]) {
char pch[1024]={0};
char scriptpath[1024]={0};
int i=strrchr(argv[0],'/')-argv[0];
strncpy (pch,argv[0],i);
strcpy(scriptpath,SCRIPT_PATH);
strcat(pch,scriptpath);
strcat(pch,"/gui.py");
FILE *fp;

Py_Initialize();
//啓動mymodule
Py_InitModule("mymodule", methods);
fp = fopen(pch, "r");
PyRun_SimpleFile(fp, "gui.py");
fclose(fp);

Py_Finalize();
}
編譯時記得連結 -lpython2.5 -framework OpenGL

Embedded Python + wxPython

上一篇依樣,只是這次是用另依種Embedded的方法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wx
app = wx.PySimpleApp()
frame = wx.Frame(None, -1, u"你好, 世界")
frame.Show(True)
app.MainLoop()
要注意的是,這次是用file open所以設定PYTHONPATH是沒用得。要改的是open file的路徑。
#include <Python2.5/Python.h>
#include <stdio.h>
//將SCRIPT_PATH設定成py檔和執行檔的相對路徑
#define SCRIPT_PATH "/"
int main(int argc, char* argv[]) {
char pch[1024]={0};
char scriptpath[1024]={0};
int i=strrchr(argv[0],'/')-argv[0];
strncpy (pch,argv[0],i);
strcpy(scriptpath,SCRIPT_PATH);
strcat(pch,scriptpath);
strcat(pch,"/gui.py");
FILE *fp;

Py_Initialize();
fp = fopen(pch, "r");
PyRun_SimpleFile(fp, "gui.py");
fclose(fp);

Py_Finalize();
}
如果你遇到如下的問題
This program needs access to the screen.
Please run with 'pythonw', not 'python', and only when you are logged
in on the main display of your Mac.
請參考,http://yycking.blogspot.com/2009/02/mac_12.html

Embedded Python

我是依thinker的文章(Embedded Python 之一)來作學習。
連結:http://heaven.branda.to/~thinker/GinGin_CGI.py/show_id_doc/234
原始的程式碼
#foo.py
def hello(a, b):
return a + b
#include <Python.h>
#include <stdio.h>

int main() {
PyObject *foo;
PyObject *hello, *ro;
int r;

Py_Initialize();
foo = PyImport_ImportModule("foo");
hello = PyObject_GetAttrString(foo, "hello");
ro = PyObject_CallFunction(hello, "ii", 15, 20);
r = PyInt_AsLong(ro);

printf("result is %d\n", r);

Py_DECREF(ro);
Py_DECREF(hello);
Py_DECREF(foo);
Py_Finalize();
}
因為執行環境是mac
所以先將
#include <python.h>
#include <python2.5/python2.5>
編譯時記得連結 -lpython2.5
編譯完成候執行時,一直說Bus error。
這是因為PYTHONPATH的問題。
解決發法如下。
Py_Initialize();
//只要利用PySys_SetPath,就可以設定PYTHONPATH
PySys_SetPath("py檔路徑");
foo = PyImport_ImportModule("foo");
hello = PyObject_GetAttrString(foo, "hello");
ro = PyObject_CallFunction(hello, "ii", 15, 20);
r = PyInt_AsLong(ro);
但是問題來了:
我在terminal下./tool(我的執行檔)可以正常工作,但是在tool的圖示點二下,卻在PyObject_GetAttrString的地方,顯示bus error。這是為甚麼?
Thinker said ..
可能和 current working directory 有關或環境變數有關係,請檢查透過桌面執行時的 current working directory 和相關的環境變數。
(在此,再次感謝thinker的協助)
所以還是老問題PYTHONPATH,只好在程式執行是動態來判斷路徑。
整個主程式如下:
#include <Python2.5/Python.h>
#include <stdio.h>
//將SCRIPT_PATH設定成py檔和執行檔的相對路徑
#define SCRIPT_PATH "/"
int main(int argc, char* argv[]) {
char pch[1024]={0};
char scriptpath[1024]={0};
int i=strrchr(argv[0],'/')-argv[0];
strncpy (pch,argv[0],i);
strcpy(scriptpath,SCRIPT_PATH);
strcat(pch,scriptpath);

PyObject *foo;
PyObject *hello, *ro;
int r;

Py_Initialize();
PySys_SetPath(pch);
foo = PyImport_ImportModule("foo");
hello = PyObject_GetAttrString(foo, "hello");
ro = PyObject_CallFunction(hello, "ii", 15, 20);
r = PyInt_AsLong(ro);

printf("result is %d\n", r);

Py_DECREF(ro);
Py_DECREF(hello);
Py_DECREF(foo);
Py_Finalize();
}

在mac中,編譯opengl注意事項

發生問題

error: GL/gl.h: No such file or directory

error: GL/glu.h: No such file or directory


解決辦法



#include <GL/gl.h>

#include <GL/glu.h>

改成

#include <OpenGL/gl.h>

#include <OpenGL/glu.h>

編譯時注意-lGLU -lGL -framework OpenGL

在mac中,建立應用套件

建立一個叫tool的資料夾。
之後,在tool資料夾裡,建立一個叫Contents的資料夾。
再建立MacOS和Resource二個資料夾。
將執行檔丟入MacOS資料夾。
將圖示丟入 Resource資料夾。再打開Developer->Applicationis->Utlities的Property List Sditor依圖說明,
先建立二個item分別為Executable file和Icin file。
再將tool和tool.icns分別改成你的執行檔名和圖示名。
再將檔案存在剛剛建立的Contents中,命名為info.plist

最後將一開始建立的tool資料夾,重新命名為tool.app就完成了。

在mac中,建立應用程式的圖示(icns)


要先安裝xcode(在第二片安裝光碟中),之後在用Developer->Applicationis->Utlities的Icon Composer編輯圖檔,就可存成icns檔

mac畫面截取

1.蘋果鍵+Shift+3 截取全螢幕
2.蘋果鍵+Shift+4 截取選取範圍
3.蘋果鍵+Shift+4+空白 截取視窗

2009年2月2日 星期一

[轉貼]獨立遊戲開發者的獨孤九訣

以下文章轉貼至猴子靈藥。我個人,覺得翻譯的很用心,所以貼出來和大家分享。

原文出處:Nine Paths To Indie Game Greatness


本文的作者,是一位曾製作過 PC、Console 以及 Mobile 平台遊戲的專業開發者。自從他以《Quake》內附的編輯器,製作出第一個遊戲關卡的那一刻起,他就領略到了創造遊戲的那種無上樂趣,也因此下定決心開始學習如何製作遊戲。然而當他在遊戲業界中,經歷了六年的工作生涯後,反而開始質疑自己是否擁有繼續開發遊戲的渴望。就在他決心離開遊戲業界,去嘗試其他的事物之後,反倒從如新生嫩芽般的獨立遊戲社群中,再次重新找回自己對於創造遊戲的熱情


在經歷過許多已經關門倒閉的遊戲公司以後,他充分地瞭解,開發商業遊戲需要非常廣泛的作用力與各種面向的考量,耗費於其中的時間,甚至遠超過投注於實際遊戲開發程序的時間。越瞭解這項事實,他就越加覺得自己已經游離了當初那個使他感到興奮雀躍的遊戲開發根源。他不禁懷疑,世界上是否有任何地方,能夠以他剛起步時的那種精神來創造遊戲。藉由這個捫心自問的問題,他發現了正迅速成長中的「獨立遊戲」。


所謂的「商業遊戲開發者」與「獨立遊戲開發者」,實際上的不同之處為何?當一間商業公司啟動一項新專案的時候,他們經常會問的問題是:「誰會給我們所需的資源來支付薪資與帳單?」如果這間公司夠幸運的話,他們可以問:「誰會給我們所需的資源來製作我們想做的遊戲?」而當獨立開發者啟動新的專案時,他們經常會自問的問題則是:「如何以現成可用的資源,製作出我所想要的遊戲?」這就是兩者間最大的不同之處。


在 1982 年時,由 Namco 公司所生產製作的《Pac-Man》,只需要 10 萬美金的開發費用;現今,開發一款 PS3 平台的遊戲,平均花費估計約為 1500 萬美金左右。即使經過通貨膨脹的調整之後,遊戲作品的開發費用仍然呈現出相當大的躍升幅度。而當近年的遊戲開發預算,已然呈現出數以倍計的增長局面之後,銷售量與收益仍然很難產生相對的具體變化。為了抵抗如此情勢,有些缺少開發流程經驗的獨立開發者,會盲目而不加思考地貿然前進,但是那些最成功的獨立開發者們,則能夠善用現存的資源大展身手。


開發遊戲這件事,最令人神魂顛倒之處,莫過於創造這些互動世界的能力,僅侷限於我們所能夠想像以及我們的技術能力所及之處;只要擁有想像力與技術力,你就能夠創造出一個充滿歡笑與樂趣的夢想國度。在這篇文章裡,作者提出九項非常實際有效的訣竅,並且引用了相當多成功的遊戲案例交互印證,帶領各位讀者進一步踏入獨立遊戲開發領域的康莊大道。



身為獨立開發者,通常只能夠運用相當有限的起始資源。藉由不向外借貸資源的方式進行開發,優勢在於他們能夠為自己創造出一個免於外在影響與限制的開發環境。身為獨立遊戲開發者所擁有的唯一義務,就是對他們自己以及購買遊戲的玩家們負責。但是該如何在如此險峻艱困的情況下,利用少量的資源,達到最大化的效益?以下是給獨立遊戲開發者們的九大心訣:


第一訣:設計層面的效率


圍繞著設計層面的考量,往往能夠為你節省最多的資源。為了達到有效率的遊戲設計,開發者可以著重於下列幾個面向:



  • 多人遊玩模式:如果只是製作單人模式與大量單線式的遊戲性內容,並不是一項很有效率的設計。一款設計良好的多人線上遊戲,只需要提供玩家們一組遊戲規則,就能夠以很少的遊戲內容開發時間,創造出近乎無限數量的遊玩時間,同時也能夠大幅增加遊戲的生命週期。

  • 使用者自製內容:如果你沒有資源去製作大量的遊戲內容,那麼為何不給玩家工具讓他們去做?如果提供給玩家合適的工具,幾乎可以斷定,玩家會製作出你八百輩子都沒有想過的東西。《Line Rider》是一款由斯洛伐尼亞學生製作的 Flash 遊戲,在遊戲中玩家能夠自由地畫出可供遊戲角色滑行其上的軌道,藉由這項遊戲機制,玩家們能夠自由發揮創意,網路上也出現許多令人瞠目結舌的遊戲創作影片。只要給玩家們發揮創意的機會,就有可能開啟一場玩家社群中的「創意核武競爭」。

  • 程序化內容:只要利用得當,使玩家能夠由既有設計創造出無限內容的生產系統,將會帶來指數性的設計效益。除了作者所提到的 《DEFCON》以外,《Spore》更是一個將程序化內容發揮得淋漓盡致的絕佳典範。藉由強大的生物創造器與載具創造器,玩家們不僅能夠展示自己獨一無二的創造能力,更能夠非常便利地以 PNG 圖片的格式,將創作物寄送分享給其他的朋友。

  • 避免寫實取向的美術風格:任何遊戲只要採用了寫實取向 (photorealistic) 的美術風格,立即就會落入與《Crysis》等遊戲競爭視覺效果的局面。而這個牌桌上的最低籌碼門檻,每年都在不斷地向上猛漲。隨著每個世代的硬體花費增加,美術素材的製作費用也越來越昂貴,如果繼續發展下去,到最後可能只有那些最大的遊戲開發商們,有能力負擔寫實取向遊戲的開發預算。

  • 重新探索已探索過的技術:最後,還有一項以較少資源取得有效成果的方法,就是去探索那些 15 至 20 年前的主題與設計。《Fez》就是一個相當出色的例子。第一眼看到這個遊戲,會覺得它不過是個尋常的 2D 捲軸式動作遊戲而已,但是當玩家將場景轉向 90 度之後,立即就會發現整個遊戲的場景,實際上是以 3D 的方式巧妙建構而成!(推薦各位觀賞這段展示影片,《Fez》的設計概念確實相當新奇有趣!令我想起《紙片瑪莉歐》與《無限迴廊》的混合體。目前遊戲似乎仍處於開發階段。)


第二訣:利用既存免費、便宜或者開源的技術


攀上前人的背部並不可恥,特別是當你正試圖達到「以少得多」的目的之時。例如全球知名的 FPS 遊戲《Counter Strike》,就是起源於《Half Life》的修改模組。作者 Minh Le 與 Jess Cliffe 藉著對《Half Life》引擎做出基本修改的方式,改變遊戲規則、武器行為以及創建出全新的美術素材,最終製作出震撼整個遊戲界的偉大成果。如果扣除掉時間成本,製作《Counter Strike》的預算近乎於零。


所以如何找到合適並且利用免費的技術來源,對於獨立開發者來說,絕對是必須認真仔細研究的課題。以下列出一些比較著名的免費技術或便宜技術:



  • 繪圖:Ogre、Irrlicht、HGE、Panda3d、SDL。

  • 遊戲引擎:Shockwave、NeoAxis、Torque、Unity、XNA、Game Maker。

  • 物理:ODE、Bullet、PhysX、Havok。

  • 網路:RakNet、SmartFoxServer、HawkNL。

  • 腳本語言:Lua、Python、GameMonkey、Ruby。

  • 音源:OpenAL、SDL_mixer、irrKlang、Audiere、BASS。

  • 使用者介面:AntTweakBar、Guichan、Navi、LibUFO。


更詳盡的遊戲開發函式庫及工具清單列表,請參照「Free Game Development Libraries」


第三訣:數位式發佈


能夠讓遊戲開發者直接將遊戲遞送到玩家們的手上,以效率來說是一項非常巨大的進展。相較於推廣、包裝以及運送實體遊戲到全世界的商店中,藉由網際網路散佈遊戲所需的資源可說是微不足道。《Red Orchestra》是一個以二次世界大戰為主題的 Unreal 引擎修改模組遊戲,在他們獲得競賽首獎並贏得 1 百萬美元的獎金之後,同時也獲得了 Unreal 引擎的正式授權。為了銷售這款遊戲,他們與數間遊戲發行商進行溝通協商,但沒有人對他們的遊戲感興趣。最終與 Valve 接觸後,使這款遊戲登上了 Steam 平台,能夠不受限於發行商與實體通路的限制,在全世界進行販售。


對於開發者或者創作者來說,網際網路的盛行無疑是件值得稱頌盛讚的事情。數位式發佈,就意味著有更多將遊戲送達玩家手上的方法,也代表遊戲作品能夠經過更少的中介者,遊戲開發者不僅能夠得到更高的報酬、保有作品的智財權,也得以直接取得使用者的相關資訊。


第四訣:在開放式平台上開發


如果去詢問任何一位專業開發者,他們都可以告訴你,將遊戲弄上一個封閉式平台需要非常多的資源。新的數位化平台例如 Xbox Live Arcade、WiiWare 或者 PlayStation Network,對於可用資源較少的遊戲來說,能夠視為比較有利的銷售管道。然而 Garage Games 公司的創辦人之一 Jeff Tunnell,也是《Marble Blast Ultra》這款 XBLA 遊戲的開發者,對於 XBLA 平台遊戲的開發預算提出這樣的意見:「目前為止,我不會考慮嘗試以 10 萬美金的預算,去製作一款 XBLA 的遊戲。光是開發套件以及品質認證程序的費用,就會吃掉一半的預算,而只留下很少的資源能提供給實際的遊戲開發程序運用。」(註:上述發言的時間為 2006 年,與現況是否相符仍有待商榷。)


而除了在封閉式平台上競爭所需的高漲花費以外,另外存在的議題,是必須要將你的遊戲弄上平台開發商的遊戲目錄上。Jeff 則將這種情形,比喻成像是「威利旺卡的黃金票卷」般難以取得。如果藉由像是電腦或網路的開放式平台開發遊戲,就能夠將原來必須拿來滿足封閉式平台需求的資源節省下來,投注更多的資源在遊戲本身的開發過程上。


第五訣:共同合作


以少量資源進行遊戲開發的關鍵要點,就在於與其他擁有你所需資源的人共同合作。包括美術設計者、音樂創作者以及程式設計者在內,獨立開發者會尋找具有相近焦點、願意相互合作,或者是願意進行商業交易的其他開發者。另外,多數的獨立遊戲開發者,與那些被商業競爭規範所限制的開發者不同,總是會積極地參與線上社群的活動,並且熱心分享在開發流程中最直接深入的細節。


像是 TIGSource 以及 Indiegamer 這樣的網站,都是獨立遊戲開發者進行討論以及尋求合作的溫床。另一方面來說,共同合作的渴望雖然能夠帶來效益,卻也可能會造成傷害;換句話說,比起現實可靠的支票來說,所謂的熱情與熱忱往往顯得反覆無常而多變。最成功的合作模式,往往集中於那些能夠共同分享目標與動機的開發者們身上。


第六訣:考慮非傳統的賺錢方法


除了傳統的商業模式以外,應該還存在著其他更具有效率的賺錢方法。即使是一個被設計為免費遊玩的遊戲,仍然能夠以各種不同的方法賺取金錢;廣告、微型付款、販售虛擬道具,或甚至在遊戲的製作過程中就開始販賣遊戲。


其中一個實例是由 Gene Endrody 所營運的 MaidMarian 網站。他使用 Shockwave 軟體,製作出讓玩家能夠在網頁上遊玩的 3D 多人線上遊戲,不僅不向玩家收取遊戲費用,同時也沒有對遊戲內容做出任何限制。藉由這樣的運作方式,MaidMarian 每個月可以吸引多達 150 萬的獨立造訪人次,並且達到 4000 人的同時上線人數,僅仰賴著網頁中的廣告,就能替他帶來相當可觀的收益。當 Gene 離開他所擔任的技術美術設計主管職務,全職專注於製作網頁遊戲時,他所得到的收益已經比之前的工作所得更多。


第七訣:重新定義成功


一個只使用超級大作 (blockbuster) 部分資源開發的遊戲,同樣也不需要賣到與超級大作相同的銷售成績,才能夠獲得財務面的成功。多數的獨立開發者,並不會花費資源試著去開發那些迎合大眾市場的功能特點,他們通常會將資源花費在他們認為重要之處,同時也是目標客群認為重要之處。


《Gish》這個遊戲為例,遊戲開發公司 Chronic Logic 曾公開分享他們的銷售成績。根據數據顯示,他們總計獲得了約 121,000 美元的款項收入。這筆數目對許多大型遊戲開發商來說,或許只是微不足道的零頭而已,但是對於一個只有 3 位開發者的團隊,6 個月的製作時間,約 5,700 美元開發預算的遊戲來說,這筆數目已經取得足夠的成功,並且讓他們得以繼續進行遊戲續作的開發了!


然而,上述的例子並不表示獨立遊戲開發者無法賺大錢。《RuneScape》是一個由 Andrew Gower 與 Paul Gower 兩兄弟,以 Java 為基礎開發的多人線上角色扮演遊戲。在提供給玩家基礎版本的免費遊戲之後,他們也實作出了一個可選擇的會員服務機制,提供給付費玩家更多額外的區域、任務以及道具。而對於沒有付費的玩家,則會在遊戲的視窗上頭顯示廣告連結。


任何一位開發者見到《RuneScape》,都能夠看出這個遊戲的製作資源非常稀少,既沒有閃閃動人的圖像,也缺少特別錄製的音軌;當然,遊戲本身的遊戲性,必定要能夠對到某些市場區隔裡的玩家胃口。而時至今天,Andrew Gower 已經是英國排名第 654 位富有的人。


第八訣:使用替代的資金來源


只要設法有效降低開發遊戲所需的資源數,能夠找到不同資金管道挹注的機會,也會呈現出指數性的提升。


《Mount & Blade》是一款中世紀的戰爭模擬管理角色扮演遊戲,由土耳其的一對夫妻所開發製作。遊戲本身一直持續在開發新功能的狀態中,新玩家能夠下載免費的試玩版,而完整版遊戲的價格,則會隨著遊戲增加的功能項目而逐漸向上增加。最早的付費玩家,僅需付出 6 美元的價格購買遊戲,而現在的標價則已提升至 30 美元。如此新穎的商業模式,不僅為他們提供了良好的資金來源,也給予了早期購買遊戲的忠實玩家們更多的優惠及利益,無形中強化了遊戲社群的忠誠度。


第九訣:個人化


如果你所開發的東西正好擊中玩家的心弦,那麼圍繞著你的遊戲的玩家社群,將能夠成為一項非常具有威力而且免費(或便宜)的資源。讓他們行銷你的遊戲,任命他們擔任論壇的板主,准許他們創造遊戲的內容吧!


《Kingdom of Loathing》是一個簡單到不可思議程度的網頁式多人線上遊戲。在這個具諷刺風格的冒險遊戲中,玩家所扮演的英雄既沒有精美細膩的 3D 模型,也不是俏皮可愛的 2D 風格,而僅僅是一個如火柴棒般的人物形象!儘管如此,遊戲目前的註冊玩家人數,已經超越了 140 萬人!除了玩家之間的口耳相傳之外,遊戲開發者並沒有做其他太多的行銷與宣傳。這款遊戲所獨具的個人特質,已經證明了自己的成功。


根據 Wired 網站的一篇報導指出,有相當數量的玩家選擇用捐贈 (donate) 的方式,付出少量金錢給遊戲的創造者 Zack Johnson。光是捐贈的收入,就能夠讓他辭去原本的日常工作,並且還雇用了六位成員以協助處理遊戲的臭蟲問題、伺服器及經濟體系。甚至在 eBay 網站上,還有人願意以每 1 美金購買 480,000 枚遊戲中的貨幣。


以上,就是每位獨立遊戲開發者都應當牢記於心的獨孤九訣。



By taking a look at Independent games, developers might find some inspiration for solutions to the challenges they face.


上述九項秘訣,或許不是什麼全新的創見,但是非常有助於導正目前許多關於獨立遊戲開發領域的誤解。很多遊戲開發者,可能會認為在商業遊戲與獨立遊戲之間,有一道巨大而不可跨越的鴻溝存在。其中一邊,是擁有巨額預算的超級遊戲大作;另一邊,則只是些如同 Match-3 消除類型的小遊戲。而在這兩者中間的巨大斷層面,沒有任何東西能夠生存。


所幸事實並非如此。每一年,我們都能夠看到獨立遊戲開發者所展現的創意活力與遊戲作品,當科技更加進步並且能夠更容易地接近全球玩家社群時,遊戲開發者也會被賦予更多在斷層面生存的機會與空間。正如作者所述,所有的專業遊戲開發者們,都應該清楚認識持續成長中的獨立遊戲社群。看一看這些風格各異、匠心獨具的獨立遊戲,或許你也能夠找到克服眼前挑戰的靈感與解決方案。


如果你和我一樣,已經在遊戲業界裡翻滾了四、五年以上或者更久的時間,應該多少都會對於文章最前頭的一席話有些感觸吧。套句星爺電影中的台詞,身為遊戲開發者,你是否有時候也會「覺得孤單寂寞覺得冷」?是否在現實與理想的拉扯掙扎中,已經埋葬了當初剛踏入遊戲公司的那份渴望?是否在每日沈重反覆的工作中,已經忘卻了從前立志時的初心?但是,誰說遊戲人只能有一種出路?從這篇文章裡,我們已經在許多成功的實例中,見識到「遊戲開發」的各種不同的途徑以及發展的可能性。


最後要提醒各位的是,即使獨立遊戲開發領域看起來是如此地美妙動人而且充滿機會,但是同樣別忘了在開發者背後,所需承擔的風險壓力與所需具備的超卓才能。就我所知,大多數成功的獨立遊戲開發者,都是原先在遊戲業界裡具有一流專業能力與深厚工作經驗的 Pro 級達人。所以如果你抱持著遠大的理想,期望自己能夠製作出跨越全球市場的獨立遊戲,那麼請先在遊戲公司裡,好好的訓練、磨練、鍛鍊自己吧!