2018年2月7日 星期三

[翻譯]製作20XX的故事(上)

原文:
https://www.gamasutra.com/blogs/ChrisKing/20171115/307769/Making_20XX_Listening_Well_and_Lucking_Out.php

嗨,我是Chris King,我最近釋出了遊戲20XX。20XX是個洛克人X、ROUGE-LITE、結合傳統橫向捲軸動作(classic action platforming)及自動生成關卡(random level generation)及永久死亡(permanent death)的遊戲。
20XX的最終美術畫面,我們重新設計了好幾次。

這文章中,我將說明開發中的重大時間點,並且稍微說明過去這四年我們學到了什麼。

開發真相


20XX是用C++自製引擎製作。我為了履歷而自己製作引擎,本來打算遊戲完成後可以用遊戲開發者的稱號來找工作。我從如何畫出三角形開始做起,20XX也是從這時候開始。我們實在應該直接用Unity的。

產品開發時間軸

第一天: 2013/7/1

Steam Early Access: 2014/11/25(512個開發日)

推出1.0版(離開Early Access) 2017/8/16(1507個開發日)

詳述時間軸

2013/7/1 開始開發20XX

我做了一個非常簡單的20XX雛型,且到reddit(/r/gamedevclassifieds, /r/forhire)去找到遊戲美術,Zach Urtes。我們簽下了6個月的專案合約,準備去完成我們絕對完成不了大架構。他創造出了第一個Rollster角色(看下面的雛型美術)。

我存下了第一個雛型的畫面,還有"ix"的血量條。
ix代表了第9個我們開發的角色模組,但我們在Mighty No. 9出現後,把它改掉了。
紫色的條狀是梯子,這是我們所捨棄的企劃,很可怕的梯子。

2013/8/31 洛克人之父"稻船敬二"發起了Mighty No. 9 Kickstarter

我花了一個星期的時間去觀查他們的Kickstarter及它所帶來的群眾效應,我覺得被完全打敗了。我花了很多時間(我那時候非常沮喪)研究MN9的專案價值,而且我得到2個重點:

1 MN9的盛大迴響證明了傳統洛克人遊戲在市場上仍有需求

2 我們都有著各自的想法,但我們是非常不同的遊戲。我知道嘗試去做一款和洛克人一模一樣的遊戲是不明智的,所以我做了一些基本上的重大變更。加入我最喜歡的roguelikes及重複可玩性,我們給予了20XX隨機生成關卡及永久死亡懲罰。加入我喜觀的各種怪物及莫明奇妙的特色。我們同時設計區域及線上的多人遊玩模式。這些都是MN9沒有的大賣點。

我們幾乎要放棄20XX了。但是我選擇了去相信這世界是足夠容納20XX及Mighty No. 9的。

2014/1/1 Zach的合約超過6個月的預訂週期

我們決定四月去PAX參展,並開始我們的Kickstartar及Greenlight專案,幾乎是同一時間。我為所有我能找到的發行商做介紹,並全部被拒絕了。(我相何他們做了很合理的決定,我們在那時候還不是很好的遊戲。我們用來簡報的雛型也很簡陋)

我開始尋找音效設計師(啊!!我們真的用暫代的音效展示給開發商)。我們第一次出擊並不讓人印象深刻,我們是這樣告訴自己的。

- 我現在還記得我很興奮地送出開發版本,也記得因為對方不想幫20XX做音效所遭受的打擊 -- 因為這是個不好的專案。我很想為自己辯解,但我選擇問他哪裡不好、哪裡我們該改進,並且感謝他給予我們一長串的改進建議。這給了我們很大的改進空間,並讓遊戲變很更好。這教了我如何從批評中取得資訊。這成為20XX開發的基本要素之一。

我們找到了音效設計師Brandon Ellis(Cityfires),他為我們做了20XX中所有驚人的音樂。Brandon 確定了我們的風格,讓遊戲立刻大大地躍進。

2014/4月  我們同時參加PAX East、開始greenlight及Kickstartar專案。我們開始有點厭惡自己了。

Kickstartar是個全職的工作,在第一天我們決定不要一直盯著專案網頁看。若你打算將Kickstartar與展覽結合,在展覽結束後再開始KS並積極地搜集所有展場上對你有興趣的玩家的EMAIL,可以讓你不會忙到崩潰。

- 我們從第一次出展PAX中學到了很多,這些經驗我過去曾經記錄過,過去的我可能比現在的我更能說明這些經驗。

- KS是殘酷且高壓的事情,我們做的是正確的事但沒辦法將所有時間花在其中。事情過後,我們瞭解到專案過早進行了,等視覺做的更好後可以有更好的成果。但在那個時間我們並不知道這些事。我們的回覆也太過於輕浮且不專業,這會傷害到我們自己。

KS的這個月是我人生中最痛苦的一個月。我若沒有強大的耐心及朋友的幫助是撐不過來的,包含了Bianca(早期行銷、社群推廣、注意東方市場、處理KS之外的垃圾事務、和我結婚"這特別重要")、DJ(雖然沒有和我結婚但幫我建立了初期我們所需的網路推廣)、Chase(測試一堆我們早期的糟糕版本、在我做出一堆垃圾時還持續支援我)。
2014/4月的美術畫面

2014/9月 我們與Fire Hose Games合作

我在IGDA 的會議上遇到了Eitan。我們同意他幫助我們讓遊戲變得更專業,令它可以到足夠販賣的程度。這是我們其中做過比較好的決定之一。Eitan有很多我們所缺乏的業界經驗與人脈。

我們將專案的規模放大了一點。取而代之不在11月釋出最終版本,而是決定釋出Early Access版本,且藉由社群的意見建構剩下的部份。

2017/11月 20XX釋出Early Access

第一天我們只賣出了64份20XX。我嚇到了,我們做了一連串的承諾給社群,而且盡了我們最大的力氣去維持。

- 我們承諾在Early Access期間,每兩週更新一次,我們在每二個週三的晚上6:00更新。我們在遊戲內放了個時鐘,清楚地告訴玩家下次更新什麼時候會出。我們很準時依Early Access的計畫更新,大家都很喜歡我們的更新時鐘。這使我們獲得很好的社群印像,而且給我們很穩定、有週期的方式得到我們接下來工作的反饋。

* 千萬不要許下自己做不到的承諾。若你訂下個日期但沒做到,那會比沒有任何承諾還糟糕。如果我沒記錯,在70次的更新內,我們有2次沒有準時達到,一次延遲了1天、一次延遲了1小時。

- 我讀過每篇在Steam社群的文章、每篇在reddit /r/20xxgame 的回覆。(/r/20xx是被廢棄的舊討論區了),及每篇e-mail。我回覆每一個人,而且當社群成長,有一部份玩家會對我提出更多平常的問題。

* 我們的成功一大部份來自於長時間的分析社群反饋。篩去情緒上的字眼再去分析剩下的。當玩家很喜歡你的遊戲時,不會只用客觀的角度提出建議。同時也不要將憤怒玩家的好建議給踢除。

- 我儘量將我們所做的事對社群公開,讓玩家的意見來決定我們的規劃。若大部份的玩家對某Bug十分在意,我們會停下手邊工作來先修復它。若社群認為我們的方向錯了,我們就認真的論是否我們要修正方向。

* 在2015 9月時,我們花了很多時間改造Ace及Nina的角色設計。
上面是20XX重新設計的角色(2015夏季)
下面則是之前的設計

- 新的設計讓我們獲得了前所未見的負評。我們十分沈重的討論,放掉所有我們決定在Beta所做的項目,將心力花在重新打造舊的角色設計(藉由之前錯誤設計的教訓)且剛好完成新的展示影片及宣傳在beta釋出前。
重新設計後的角色們,在推出前又再次改進了一次

- 這次的調整是我們最大幅度的修改我們的計劃。我很確定如果我們堅持修正我們的槍而不去重新設計我們的角色,我們在beta時就會淹死了。

* 對社群開放不表示你要過渡承諾,分享你可以掌控的項目。

2015/3月 了解到我們的遊戲需要提供視覺設計,我們雇用了Clemens Scott

Clemens(最近的作品是Broken Rules工作室的Old Man’s Journey)花了6個月,將我們的遊戲由"沒有吸引力的"變成"極具特色的"(巨大的躍進)。沒有了Clemens的幫忙,20XX宣傳不會如此成功。

2015/4月 我們第二次在PAX展出我們的作品,這次在我們喜愛的Indie MEGABOOTH展出。

這次比我們上次展出好多了,而且我們甚至得到了些宣傳。接下來的一個月,MANvsGAME 及Zeke(Ezekiel_III)開始實況20XX,那時是凌晨兩點,我正在Bianca(女友/現在是未婚妻)的家裡。我一直看到凌晨五點,好險他們沒有把我怎樣。有了Clemens的第一波美術改進很令人興奮,這是很有幫助的合作,我們在隔天就賣出了64套遊戲。而且我簡直如同過節般的快樂。

在9月15號,Mighty No. 9也宣佈了他們的第一次延期。這並不是什麼壞事,延期總是會發生,這時他們也解釋了為什麼會延期、且告訴大家正在努力。

2015/5月 資金快要見底了。Bianca私人大力的贊助我們。她是真的是我們的天使。我去銀行貸款了,所以我可以繼續雇用Zach。

2015/8月 Mighty No. 9第二次延期了。這次他們處理的比較不好(有很多文章講這件事-例如這篇https://kotaku.com/the-story-behind-mighty-no-9s-shady-delay-1722766843)。Mighty No. 9損失了不少支持,我們覺得這是宣傳的好機會。

在MN9宣佈延期不久後,我們發佈了Beta版本。我們受到前所未有的媒體關注,我們由販賣不到多少款變成銷售成績不錯的遊戲。

我們仍然很辛苦的在開發,但從現在開始20XX賺得的錢比開發費用還要多了。蜂擁而來的玩家、收入、與評價完全出乎我們意料之外。之後的一個月,我們保持著穩定的銷售狀況且終於還完了借款。

2016/1月 20XX已經成長了。我們決定在每個更新前都需要社群的意見。

我開啟了我們的"超級好友"群組,為了在上線前的早期開發就能取得玩家的意見。我們盡了最大努力讓早期版本能覺得好玩。上線前看新的物品及關卡是很有趣的,但測試引擎的改版則是不是。

我們提供他們初期的開發關卡,能完成這些關卡真的是需要很多好運。第一版的這些關卡還需要很大部份的改進,而超級好友的意見正是改進的重要來源。

2016/5月 我們決定20XX應該有些過場畫面(所有專業的遊戲都有過場畫面),所以我們開始徵人。

過場畫面真的是件很難做好的工作。我們花超出預算且整個流程花了14個月完成。我們之中沒有人專精於此,所以要討論我們到底要找什麼東西是困難的,但經過了這一切,相較於我們學到了許多東西,我更覺得我們是僥倖存活了下來。

2016/6月 Mighty No.9 PC正式版釋出
玩Mighty No. 9不如玩這個吧.

在MN9釋出之前,20XX最中肯的評論是"如果你不想再等Mighty No. 9,你可以來玩這款"。Mighty No. 9發售之後,雖然它做得還不錯,但仍不如它所宣稱的那麼好。這些評論也改變了,很好運地,MN9釋出的日期正好差不多是Steam的夏日特賣,20XX剛好接收了對MN9失望的玩家。

從此開始20XX的開發變得順利許多。我們維持了一定的銷售量,並在每次特價時期提供20-25%的折扣。直到2017/8月1.0正式版釋出為止都十分順利。

2016/12月 我們發佈了"piecemaker"試驗,讓玩家可以使用我們恐怖的關卡設計工具。

這個遊戲工具糟透了。超過一百位自願實驗者,只有兩位有拿出一點成果,而其中之一將它放入了遊戲。這百分之百是我們的錯 - 我們的關卡設計系統太笨重且很難以上手。

- 做一個更好的編輯器成了1.0之後的問題。我們仍然(至2017/11月為止)在思考這是否為20XX合理的計畫。(如果你很有經驗做遊戲編輯工具快來聯絡我吧!)

2017/8月 過了71個持續性的隔週更新,20XX離開了Early Access,在PC上推出了。

我們做了新的展示影片,但這100%仍然是動作取向。(歡迎大家關於這點提出意見,我們認為這方面還有很大的改進空間)

之後的1個月,我們和幾乎每個對我們遊戲有實況主接觸,並親自寫了信件給媒體。我們並沒有得到很多回應。有3個比較大的媒體會在發行時幫助我們,這是其中一個。(Tim的影片十分強大,即使你不喜歡20XX)。當Kotaku影片發佈時(約遊戲發行後的第3天),我們的銷量比前面的2天暴增了30%。雖然很難指出多少是被影片吸引的(20XX在週末也總是賣得比較好),但這仍然給了我們很大的幫助。

有一大部份來自於我們的發售時在Steam的銷售排行前20-25名。Steam的排名拯救了我們的能見度。雖然我們在媒體上沒有很到很大的曝光,但在Steam的演算法下我們賺到了不少,我們在發佈的首週表現得很有競爭力。

最終產品

未完待續(下篇會歸納從製作中習得的經驗)

2018年1月30日 星期二

[翻譯]專案管理 Part1: 避免遊戲工作破壞腦袋


原文:
http://www.midnighthub.com/blog/2017/11/01/project-management-part-1-avoid-brain-damage-from-working-with-games/



作者:Sara Casen
遊戲開發者,過去在Paradox及Tarsier任職。現在經營Midnight Hub,之前在Casen Crowd,社群經營公司。

為什麼這麼多的遊戲工作室超時工作?如何避免你的組員的熱情燃燒殆盡?以及如何讓你的開發夥伴投注熱情及創意在工作上?及有什麼方法可以取代惡名昭彰的遊戲開發?若你是個管理者從專案開始便將超時工作視為正常,或是認為燃燒熱情本來就是製作遊戲的一部份,那這一系列的開發文章不是為你所寫的。但是你若好奇為何我們花上數個小時來寫這篇文章,你想去最小化你的團隊的壓力、或你想要些遊戲專案管理的秘方,那就讀下去吧。

遊戲製作生涯的平均長度

這個夏天我參加了一個遊戲CEO及管理者的聚會。這個聚會在德國旁的小島上。北歐國家的管理人都在一起討論遊戲產業的現況。在這會議中,大家談論了北歐遊戲製作生涯的平均長度。猜猜看是多長?答案是4年!平均開發者會在4年之後離開遊戲產業。他們可能燒光了熱情、犧牲了健康、或花光了資產。我並沒有確切的數字,但有報導指出這個產業是如何一次次的消耗熱情,使其產業活下去。超時工作被視為常態,一週工作80小時並不少見。但科學證明這並不能讓遊戲變得更好。

在過去百年來的工業研究證明了以上的問題,過渡工作的工人造成更多的錯誤、計劃延宕、損壞工具、多餘的支出、降低貨物的品質、甚至影響收益。他們對於專案、管理、員工、其它人甚至自己都是危險的。
– Evan Robinson, IGDA “Six Lessons On Why Crunch Mode Doesn’t Work”.

在2015年我們開始了自己的工作室 "Midnight Hub",我們想要做些改變。之前我們曾經在Minecraft、The Division及其它專案工作。我們的目標是用穩定的方法持續產出獨特的遊戲。我們是5位獨立開發者,現在花了兩年在第一個遊戲上。"Lake Ridden"。這是個單人第一人稱充滿神祕的解謎遊戲。我們並非總是做對所有事情,但我們仍然努力去嘗試。

這段時間我們儘量不超時工作,我們仍有效利用工作時間且產出許多我們遊戲的文件。我們團隊儘力做出最大效率的產出,並且確實記下每個人的壓力狀況。我將在這分享專案管理的哲學。我們是如何最小化壓力所帶來的影響,並且讓遊戲可以準時在2018春季上市。這包含了許多的層面,所以我將它拆成三篇文章,每篇專注在不同的方向。

提醒一下大家,我們是生活在社會主義的瑞典,這邊一般工作約佔每週40小時,而且員工有權力要求數週的休假及帶薪產假、陪產假。遊戲產業在這邊會更加的扁平化及外國人稱讚我們會主動幫助工作上的夥伴。我們通常也鼓勵彼此做各種嘗試,並且也不認為上司會知道所有的知識。不幸地我們也是會被壓榨的,很多人在遊戲業的工作時數也很長。犧牲假期去追求永遠也無法完成的完工日期。最後迫使他們帶著極大的工作傷害離開遊戲業。

為什麼我們以這種方式工作?


我第一個遇到擁有真實人生的遊戲開發者,是在DICE的Battlefield 1942團隊。他告訴我團隊是如何被消耗到最終失去了他們的熱誠。他們睡在自己辦公桌而不回家,他們的腦袋漸漸地變得緩慢,人們忘了如何去談話、憂鬱、失眠、漸忘。這些都是持續強烈的壓力的徵兆。這些逐漸地傷害你的身體、神經系統、及腦袋。思考看看由製作遊戲造成的腦部傷害。這是有可能被修復的,即使在有社會醫療良好的瑞典,也要花一段很長且辛苦的時間。

在這種工作環境很容易發現一個問題,一些很優秀且具熱情的開發者會不斷的流失。而且當他們開始覺得這是正常的,所以認為忍耐才可以讓事情完成,認為這樣才可以讓遊戲做出來。遊戲當成藝術媒介或故事媒介,這並不是個累積業界經驗的方法。因為有想像力的創作者會在幾年內被榨乾,大多數的人們不會想再次回到遊戲業。那個他們花費許多投資及時間去完成作品的地方,同時也將他們燃燒殆盡。若你也曾經因過渡工作而腸思枯竭,甚至你還需要面對這種症狀數十年。而我們是如何讓遊戲媒體帶邁出這種困境的。

當你擁有創意的人、對工作的熱誠、完美主義者,並將他們混進數千萬預算及糟糕的排程,事情通常會變得很糟糕。人們被強迫超時工作,最終失去他們。不僅是過渡工作同時也無法讓遊戲品質提升。

公司的佈告欄,給予大家分享及鼓勵彼此

為何要用穩定的方式製做遊戲?


所以為什麼我們想要減少壓力、避免超時工作及從第一天就開始防止員工流失?也許是我瘋了。但能成為一個正常的人類感覺很好。若你想用同一批優秀的人才做出更多好遊戲,你該避免讓他們過渡工作。壓力降低了我們的耐心及溝通(你是否看過團隊成員在類似狀況"我不想立刻和你解釋,快去處理它")。遊戲開發是一個團隊運動,若你不打算溝通會造成分崩離析。據我的經驗,壓力是人們開始爭吵的主要因素。

另一方面,人類(抱歉,我遇到某些管理者視人類只是種資源),我的專案管理經驗中,大多份管理者只把人類視為一個可壓迫的變因。若金錢、時間、其它因素是不可變的,那人們就會驅向更加壓迫人類變因。那是因為你很難去衡量能由人類身上取得多少。有的人每週可工作80小時,有些人每天工作6小時後就沒力了。所以管理傾向儘量壓迫人類資源。你知道你在銀行的金錢什麼時候會燒光,但你的員工呢?當你知道迫使人們會失去熱誠,可能已經為時已晚。在我的觀點中,取得金錢要比修復人們的工作傷害要簡單多了。

較短工時的科學根據


有很多研究顯示更多的工作時間並不等於更多的產出。就像是做多件工作比較划算一樣(還需要付出切換工作的成本),讓你感到很有效率但事實不然。若你是在做高腦力的工作,像是編寫程式、寫故事,實際上人腦一天只能夠專注約六小時。過了這段時間,你開始懈怠且開始犯錯(無論如何,這仍然會讓人們感到有效率),然而你必須在隔天整理昨天幹的蠢事。專注於聰明的工作,而不是努力的工作。我相信聰明的管理者該妥善利用各項資源,來做出最佳的遊戲。為什麼不該把這些技能也用在員工身上。

找到新的團隊成員、安置他們、提升效率、確定他們符合團隊等等,是一項艱難的工作。所以照顧員工一直以來都是個好的投資,總比盡力壓榨他們然後再找新的人來取代。老實說這也是因為我們相信一定有更好、更人性的方法來做遊戲,讓開發者有效開發及玩家喜愛。

製作迭代前後比較。迭代是Midnight Hub的主要遊戲製作流程。白色方塊是測試元件,如上圖

事實上大部份的壓榨及超出排程、浪費金錢來自於錯誤的計畫、規模。當然像是引擎意外更新、相同市場上的競爭者、主要成員的離開等重要的因素也是。但我想你若能用乾淨的方法管理你的遊戲專案,會比你增加員工的工時,讓遊戲更有機會準時上市且打造更長遠的團隊。更重要的是,這是你少數你可以真正控制的變因,你無法控制主企劃突然去了地球另一端、或是你的遊戲被破解、或新的遊戲引擎版本讓你的遊戲壞了。但你可以且應該控制你的計劃、遊戲規模、及專案管理,不該有任何藉口。

關於縮減壓力的實際例子


這是我們讓所有人不被壓垮在Midnight Hub做的事。記著若你是領導者或資深開發人員,所有員工都會看著你所做的事。若你不想要讓別人超時工作且親自執行,那所有人會開始真的相信他們不該待在公司太久,言教不如身教。

-減少在公司的時間。我們待在公司9:00-17:00,且有1小時的午餐時間。
-所有人同時間離開公司,沒有人會獨自留下辬公。
-管理必需避免員工超時工作及壓榨。
-所有員工放的到特休
-若你正在處理巨大的任務,可以調整成只工作80%或50%時間。因為那些任務可能會榨乾你的精力。他們可能是你從來沒做過的事,非常困難,要花了數週、數月去完成,而且做錯了會有很嚴重的後果。像是擬定開發合約、對投資人的簡報等。
-規畫時間緩衝放假的心情
-在假期內完全關閉辦公室,不讓任何人單獨工作。
-員工生病假的第一天就可以拿到80%薪水(瑞典通常第一天不支薪)
-在規劃時期給出更佳且更實際的時間估算
-用ABC法則製做遊戲(接下幾篇文章會提到)
-在每週開會評測每人的壓力


整理一下

在Midnight Hub開始時,我寫了有關如何白箱測試遊戲的文章,迭代開發每個部份。我們如何用敏捷開發的方法,將測試用美術轉成可正式美術素材每次一個迭代。這方法讓我們不用花費過多時間就可以有機會測試它,我們現在將其粹取出來。我會在下一章解釋如何用這方法,讓你有機會試用這配方,我們叫它"ABC"。之後我將告訴你,我們如何用實際的金錢去估算時間且規劃我們的迭代。我的目的是分享這方法幫助大家建構長久的團隊,且讀者的回饋也可以幫助改良我們的方法。你在工作上所耗費的心血將不會對你自身造成傷害。

2017年11月10日 星期五

如何避免你的程式大爆炸了?

原文連結

在1960/10/24,蘇聯拜科努爾太空發射場發生了一場大爆炸。在準備飛行的期間,火箭意外爆炸了,造成了火災及無數的破壞。超過70個人在這天死亡。USSR的戰略火箭計劃大大的退步。這場大災難被命名為Nedelin,這個計劃死去的執行負責人。

我們重新檢視造成這場大災難的步驟:

1. 工程師在非常緊湊的排程下工作

2. 因為時間不夠,許多安全程序都被省略或者沒正確執行。

3. 許多意外及缺失在發射準備期間被忽略或沒仔細分析過程

4. 在爆炸當天,為了加速發射流程,許多測試被迫不照順序同時執行。

5. 因為火箭啟動的延遲,工程師沒有正常理由就直接在正式環境作業。

6. 當天有人看到發射按鈕沒在原位而將它擺回原始位置。

7. 其它元件也被放在錯誤的地方,造成系統執行發射命令並且點燃了第二階段引擊,使得沒準備好的火箭爆炸了。

對照遊戲開發,我列出了以下狀況


Initialization, update and deinitialization patterns
Bad input data
Too much control exposed in data
Dereferencing null pointer
Big classes
Division
Vector normalization

同時我也提供了如何防範錯誤的做法及範例。

防衛性程式及合約式設計(Design by Contract)

防衛性程式
不該相信輸入的資料,而是預先檢查資料的正確性並給予安全的數值。
下例:即使不正確的參數,仍然給予安全的答案:零。

float GetSquareRoot(float Argument)
{
    if (Argument <= 0.0f)
    {
        return 0.0f;
    }
    return sqrt(Argument);
}

合約式設計:
設定程式與用戶關聯性
程式必須設定前置條件、後置條件和其不變性。

條件可被下列方式呈現:
assert
comment
etc. (a set of test)

下例:使用註解(comment)及assert設定程式的合約
// Argument needs to be equal to or greater than zero.
float GetSquareRoot(float Argument)
{
assert(Argument >= 0.0f );
return sqrt(Argument);
}


這兩項方法都能幫助我們寫出更良好的程式。
無論如何,我相信遊戲程設要能其特別的狀況而被特別對待。

1. 最大化執行速度是遊戲的重點

防衛性程式無法用在程式的每個地方,因為這會拖慢程式速度,且額外的程式會造成其它錯誤。我們的遊戲就遭遇過執行速度的問題。同時太多的防衛性程式使錯誤不容易發現(被防衛性程式擋下)。

2. 程式在變更迅速的環境下被完成。

遊戲程設包含了快速開發、創意驗証。但是因為要求速度會造成未來維護的問題。

3. 程式必須同時能用於產品及測開發階段

當我們在製做新的功能時,編輯器的新工具或改良也在同時進行,我們的第一個使用者是其它團隊成員、設計師、美術人員。他們也在製作遊戲及使用我們的工具。這表示了我們的程式同時被開發團隊及消費者所使用,即使在釋出給玩家之前。用assert強制程式對於工程師是好方法,但是同時會中斷其它成員的工作流程。

另外你要面對的問題可能和你遊戲、團隊、工作模式有關。Rendering的工作就會以速度為優先考量,而AI的工作就會需要更多穩定的程式。

我們將會介紹遊戲開發會遇到的常見幾種狀況。

Initialization, update and deinitialization patterns

讓我們看這個RAII(Resource Acquisition Is Initialization)的實作
class MyClass
{
public:
    MyClass()
    {
        m_Data1 = new DataClass1;
        m_Data2 = new DataClass2;
    }
    ~MyClass()
    {
        delete m_Data1;
        delete m_Data2;
    }

    void Update(float FrameTime)
    {
        m_Data1->Update(FrameTime);
        m_Data2->Update(FrameTime);
    }

private:
    DataClass1* m_Data1;
    DataClass2* m_Data2;
};
我們在constructor創造2個物件,然後在destructor刪除它們。並且包含了一個常見的Update函式,會在每個frame被呼叫一次。

現在讓我們把記憶體管理移出constructor、destructor,並且創造一個函式來控制這些資源。這是為了避免在同一個地方執行太多的操作。通常資源管理是個佔效能的操作,所以我們會希望在遊戲中能方便的控制它們。我們創造2個函式Initialize and Deinitialize來管理記憶體。

這是新的程式碼:


class MyClass
{
public:
    MyClass() { }
    ~MyClass() { }

    void Initialize()
    {
        m_Data1 = new DataClass1;
        m_Data2 = new DataClass2;
    }

    void Deinitialize()
    {
        delete m_Data1;
        delete m_Data2;
    }

    void Update(float FrameTime)
    {
        m_Data1->Update(FrameTime);
        m_Data2->Update(FrameTime);
    }

private:
    DataClass1* m_Data1;
    DataClass2* m_Data2;
};

這程式仍然十分單純,但這有許多的問題。

1 這沒有適當的initialization在constructor中
2 如果Initialize被呼叫了兩次會發生什麼事?這有可能造成memory leak
3 在Deinitialize也有可能會發生相似的問題
4 另外個問題關於Update,若沒有先呼叫Initialize而直接執行Update會發生什麼事情?會因空指標或被初始化物件造成crash.
5 若複製這個物件,有可能造成多次刪除的問題。

我們這裡沒有特別做合約式設計,我們只是考慮這個類別有可能的使用狀況。

為了使此程式穩定、減少出錯。我們需要

1 提供適當的initialization給我們的成員變數
2 加上bool參數m_Initialized 使memory leak、及多次刪除不會發生。
3 正常狀況下,若指標已被釋放,我們應將指標位置清除
4 由destructor呼叫Deinitialize,避免使用者忘記呼叫。
5 定義private copy constructor and copy operator, 防止它們被複製。

更改後如下:

class MyClass
{
public:
    MyClass();
    ~MyClass();

    void Initialize();
    void Deinitialize();

    void Update(float FrameTime);

private:
    MyClass(const MyClass&);
    MyClass& operator=(const MyClass&);

    bool m_Initialized;
    DataClass1* m_Data1;
    DataClass2* m_Data2;
};

MyClass::MyClass()
    : m_Initialized(false)
    , m_Data1(nullptr)
    , m_Data2(nullptr)
{
}

MyClass::~MyClass()
{
    //Prevent a memory leak if the client 

    //does not call Deinitialize.
    Deinitialize();
}

void MyClass::Initialize()
{
    if (m_Initialized)
        return;

    m_Data1 = new DataClass1;
    m_Data2 = new DataClass2;

    m_Initialized = true;
}

void MyClass::Deinitialize()
{
    if (!m_Initialized)
        return;

    delete m_Data1;
    m_Data1 = nullptr;

    delete m_Data2;
    m_Data2 = nullptr;

    m_Initialized = false;
}

void MyClass::Update(float FrameTime)
{
    if (!m_Initialized)
        return;

    // Defensive approach to check the pointers separately.
    if (!m_Data1 || !m_Data2)
        return;

    m_Data1->Update(FrameTime);
    m_Data2->Update(FrameTime);
}

Bad input data
(壞掉的輸入資料)

壞掉的輸入資料從資訊產業誕生的那一天就有了。檔案遺失或例外參數都會造成遊戲或編輯器當掉。

通常工程師會特別注意這問題。我們的程式需要為了參數遺失,特別寫預設的參數,像是預設的貼圖或可視的物件來提醒輸入錯誤,給予錯誤訊息及記錄也是有效的方法。沒有程式應該直接掛點。

在多數的遊戲引擊,我們可以很輕易地給予編輯器參數。讓我們可以利用這參數在一行指令就生成數個物件。

int NumberOfProjectilesToSpawn;

通常我們只會給他0,1,5這種數量。但有時使用者會犯錯或給與一個超大的數值像是100萬、或負數?那時候我們的程式碼還可以正常運作嗎?

好的方法是去考量現況決定參數的預設值及範圍。另外一種方案,如果我們想要不被限制的超大參數,我們提醒使用者輸入的參數可能會造成問題。

在防衛性程式,我們可以很清楚的區分有危險的參數。我們將所有input視為不可信的,但有些被驗證的內部資料是可以相信的。例如,我們認定所有外部檔案、編輯器輸入參數、玩家輸入參數、網路上參數都是有不可信的。驗證及修正函式需要將這些參數轉成可信的參數。

void SetNumberOfProjectilesToSpawn(int NumberOfProjectilesToSpawn)
{
    m_NumberOfProjectilesToSpawn = NumberOfProjectilesToSpawn;
    ValidateAndCorrectParameters();
}

void ValidateAndCorrectParameters()
{
    m_NumberOfProjectilesToSpawn = clamp(m_NumberOfProjectilesToSpawn, 0, 8);
}

在遊戲開發中,越早驗證是越好。例如:在編輯器輸入參數時就驗證,在存成檔案之前,或是在檔案一讀進來。越早確認使其更容易減少執行遊戲不必要的測試及效能衝擊。

Too much control exposed in data
(太多對資料操作的方式)

由壞掉的輸入資料所造成的問題再更進一步思考,我們所有的系統都是資料導向的。這些系統能被設定或在沒有工程師的狀況下被操作,完全透過所輸入的資料。

通常會將所有操作及遊戲特色給每個組員,尤其是非工程師。但是工程師必須問問自己,我們真的可以提供所有組合參數嗎?或者反而造成更多例外狀況?

創造一個真正資料導向的系統是非常困難及花時間的。

當我們提供任一個參數,這都會成為整個巨大資料導向系統的一部份。很自然的,我們會加入一個介於程式與資料之間中間層。

讓我們看這用來控制敵人的簡單結構

struct EnemyBehavior
{
    float m_WalkSpeed;
    float m_TurningRate;
    bool m_JumpAllowed;
    float m_JumpSpeed;
    // …
};

這個程式提供m_WalkSpeed 的小參數,也同時提供m_TurningRate 大參數。當m_WalkSpeed 很大時跳躍的功能仍然可以正常運作嗎?

一個較安全的方案是準備一組預先設定好的參數組,而這些參數都是可正常執行的。使用者只允許使用這組先定好的參數。

enum EnemyBehaviorPreset
{
    EnemyBehaviorPreset_TightCorridors,
    EnemyBehaviorPreset_IndoorHalls,
    EnemyBehaviorPreset_OutdoorOpenSpace
};

我們只提供一個參數讓使用都可以在編輯器使用。

EnemyBehaviorPreset m_EnemyBehavior;

這個方法雖然限制使用者的自由,但是對工程師有更佳的穩定及維護性。

Dereferencing null pointer
釋放空指標

Dereferencing null pointer是造成遊戲或編輯器當掉的一項常見因素。

在Unreal Engine 2 and 3,其中一項程式語言UnrealScript的設計目標就是防止這類問題。並且創造不需指標的開發環境。存取物件references在UnrealScript一直是安全的,即使它們沒有指向任何物件。

因為執行速度是很重要的,所以我們不能在所有釋放指標的地方做檢查。無論如何每個部門處理指標的方針也許不一樣。繪圖工程師會犧牲防衛機制去換取速度,但是AI或gameplay則會希望更多的防衛機制,尤於gameplay時常更動。所以如何處理指標會按其環境而定。

-Avoiding pointers(避免使用指標)

第一、我們可以避免使用指標當函式的參數且用C++參照(references)取代,下例即是說明如何取代指標

void MyFunction(ControlData* InputData)
{
    if (InputData)
    {
        int parameter1 = InputData->GetParameter1();
        // …
    }
}

改成用reference的版本

void MyFunction(ControlData& InputData)
{
    int parameter1 = InputData.GetParameter1();
    // …
}

這樣使用者在用這個函式時就會提供適合的參數,並確認及釋放指標。

-Public and private methods(公開及私有函式)

實際上,有項不好的慣例是在公開的函式傳入的指標當作參數,

若這函式是屬於公開介面的話,我建議要檢查這函式是否為空。

void MyClass::AnalyzeInputData(Data* Input)
{
    if (!Input)
        return;
    // …
}

若函式是屬於私有實作的一部份,我會傾向將先前的程式移除。在這例子,函式有註解標明、或是函式會發出警告訊息去確認指標狀況。

class MyClass
{
public:
    // Method always tests if the pointer argument is null.
    void AnalyzeInputData(Data* Input);

private:
    void UpdateObject_NoTestForZeroPointer(ObjectClass* Object);

    // Method does not check validity of pointer arguments:
    void UpdateObject(ObjectClassA* Object1, ObjectClassB* Object2);
};

另外在私有函式UpdateObject及UpdateObject_NoTestForZeroPointer的開頭,我們可以利用assert驗證指標

// Method does not check validity of pointer arguments:
void MyClass::UpdateObject(ObjectClassA* Object1, ObjectClassB* Object2)
{
    assert(Object1);
    assert(Object2);
    // …
}

無論如何這不是最實際的做法,若我們的assert中斷成員在使用編輯器。要改進這個方法,我們可以創造一個防禦性軟驗證。

-“Soft” assertions

軟驗證應該允許程式繼續執行,但仍會回報問題給開發者

void MyClass::UpdateObject_NoTestForZeroPointer(ObjectClass* Object)
{
#ifdef CONTRACT_SAFE_CHECK
    reportIfNull(Object); // Soft assert
    if (!Object)
        return;
#endif
    // …
}

這方法通知程式人員不正常的狀況(將訊息送到資料庫)且不會中斷執行。利用#ifdef設定是否要執行這項檢查。開發設定應效能分析(profiling)及正式環境可執行。

編譯前的參數也要考慮到是否在編輯器執行或是遊戲內會造成其它問題。

Big classes

有些class成長速度太快了。所以有些原始檔會變得比其它檔案還大。若你去檢查你正在執行的專案,你會發現有些檔案很明顯地較其它檔案大上不少。這些檔案通常是負責角色、玩家或是遊戲管理者(manager)。這並不令人驚訝。當我們增加新的特色進遊戲時,即使是最小的步驟,我們也會將它加入現有的class底下。這種增加速度是會造成問題的。這使得我們很難去維護preconditions, postconditions and invariants。維護大型的類別是很困難的,且他們也會造成更多問題。從Initialization, update and deinitialization patterns要發現問題會比較簡單。通常他們包含了許多沒有分類好的子元素。這有許多可能的方法可以解決這個問題。我在這展示其中一個方法。

若我需要在現有的class中加入一項新的元素,我開始評估是否該為其建立nested class。下例在ObjectBehavior 內新增成員參數及函數去為了完成Special Action 1。

class ObjectBehavior
{
public:
    // …
private:
    // …
    // Begin Special Action 1.
    bool m_ShouldStartSpecialAction1;
    float m_TimeLeftToStartSpecialAction1;
    float m_SpecialAction1Parameter;
    void InitializeSpecialAction1();
    void DeinitializeSpecialAction1();
    void UpdateSpecialAction1();
    // End Special Action 1.
    // …
};

以nested class方式實作:
以子類別
class ObjectBehavior
{
public:
    // …
private:
    // …
    class SpecialAction1
    {
public:
        void Initialize();
        void Deinitialize();
        void Update(ObjectBehavior* ParentObject);
    private:
        bool m_ShouldStart;
        float m_TimeLeftToStart;
        float m_Parameter;
    };

    SpecialAction1 m_SpecialAction1;
    // …
};

哪一個方法比較好?我們適度的包裝了新的參數在獨立的私有類別中。只有SpecialAction1 nested class被存取到。這使得類別在未來可以更容易被區分出來。

除法(Division

電玩遊戲用了大量的浮點數運算。例如,我們經常使用除法在我們的程式內。這是非常基本的數學運算,但這仍然要注意不要讓除數為0。若我們除數為0時,會造成浮點數的例外狀況,或者浮點數的例外被擋掉了,算出來的結果會是無限(INF)或非數字(NaN)。

請也考慮到下面的狀況。當兩個數字都是合法的浮點數,但是結果會是INF,因為他們超過32-bit浮點數可表現範圍。

float f1 = 100000000000000000000.0f; // 1.0e20f
float f2 = 0.0000000000000000001f; // 1.0e-19f
float f3 = f1 / f2;

f3即為INF。另一個例子:

float f4 = 0.0f;
float f5 = 0.0f;
float f6 = f4 / f5;

參數f6是NaN

我們可以繼續執行程式,因為INF是個合法且可計算的特別記號。無論如何,在多數狀況下,我們不會想去處理這種狀況。許多函式庫會因這些例外狀況中斷。

Unreal Engine 4 提供了時別的函式去偵測浮點數太接近零。

bool FMath::IsNearlyZero(float Value, float ErrorTolerance = SMALL_NUMBER);

在 C# Unity Engine可以被寫成這樣:

bool IsNearlyZero(float Value)
{
    return Mathf.Abs( Value ) <= 0.00000001f;
}

我們可以在做除法之前先確認我們的除數。

float denominator = b – a + c;
if (!IsNearlyZero(denominator))
{
    result = d / denominator;
}

要確認特別的浮點數,我們擁有以下函式:

float.IsInfinity(float) and float.IsNaN(float) in Unity Engine
FMath::IsFinite(float) in Unreal Engine 4

同時我們經常忘記某些特別狀況會用到除法。請考慮以下的update函數。

void Object::UpdateLogic(float FrameTime)
{
    Vector3 Velocity = (GetCurrentPosition() – m_PreviousPosition) / FrameTime;
    // …
}

我們可以很簡單的假設FrameTime總是大於0。當然程式會在FrameTime為零時出錯。這是有機會發生的,例如當遊戲在製作時我們相要將時間暫停,但是update仍然被呼叫。在編輯器,我們通常不會更新world logic,但在某些特別的狀況,我們會為了模擬而更某些物件的邏輯。這簡單的程式會因為這些特例而掛掉。

Vector normalization

在Vector normalization會出現某些例子上。通常是計算3d數學時。normalization是為了計算出單位向量的方法,會將其向量除以自身長度。這些函式都非常單純,我們再次回想起了除法的例外。當向量為零時或非常接近零時會出錯。

在Unreal Engine 4,我們有以下兩個函式:

FVector::GetUnsafeNormal() – 這是不會做額外檢查的normalization,但比較快速。

FVector::GetSafeNormal() – 這是會檢查除數是否為零,若長度接近零時,結果回傳零。

有趣的是它用很直接的寫在函式名稱上提醒使用者。

Unity Engine在C#則是只有提供安全的normalization

Vector3.normalized

Vector3.Normalize()

所以它會回傳單位向量或是零向量。

“Soft” methods

我同時也會提供些通用的方式來提升程式品質。

測試你自己的程式碼:
自動化測試、
視覺化除錯及文字記錄、
採用有意義的命名、
程式檢閱(code review)、
靜態程設分析工具、
程式文件

總結:

在這文章中,我展示了遊戲程式設計上常見的錯誤及一般問題的來源。我們知道追求安全及穩定的工程方法並非新的挑戰。這也不是簡單的工作,尤其在特殊的狀況下,像是遊戲開發。主要困難來自於去知道、預測及決定什麼地方該用防禦性程式及該用多少的安全措施才足夠。

將高安全性軟體工程的做法完全放到遊戲開發是不明智的。例如:太空計劃。無論如何,這可以用來了解人們在這些產業是如何完成他們的目標。

NASA太空中心主管及Saturn V rocket首席工程師,特別喜歡實作大型及安全的軟體工程。這些通常會在開始時花不少時間,但最終帶領美國完成了阿波羅11的成功。第一個上太空的人也安全的回來了。Saturn V 也是唯一帶著人類離開地球的火箭。


2017年9月25日 星期一

Failbetter Games首席編劇的五點建議

原文連結

在經過了幾年的鬥爭及無數個敵手的死亡,去年我成為了Failbetter Games的首席編劇。我管理內部的劇情小組及外包工作,包含有Sunless Sea, Fallen London, Dragon Age: the Last Court及其它專案。

我仍然在尋找正確的道路!這裡有五項我過去學到的教訓。

1 優先提升團隊能力,即使這需要犧牲劇情的品質。

"這份專案是Fallen London,別把他搞砸了"

很明顯的我當時十分緊張。我必須完成這個專案,我是個超級的完美主義者。如果這有曾經做錯的事,就是要團隊照著範例做事。

我深信提供案例可以讓團隊快速學習,但這包裝好的學習方式,使我放棄了團隊自我成長的機會,他們可以自己克服困難並發展出他們自身的解決方式。而我應該學會退後一步並點出幾個重複的問題。我的建議必須是清楚、泛用、可達成的。例如:"將字數減半"、"確定玩家知道自己的每個步驟"。

團隊提出的解決方案通常比我的還好。這花了我一段時間了解,我最重要的工作是去幫忙團隊去做到最好。有時候這代表著你要咬緊牙關讓團隊去做你不喜歡的事。而且總是會不斷發生。

2 防止你的團隊的不斷的提出新點子

我在這件事上有著慘痛的經驗。我們是創意產業,每個人都會貢獻自己的意見。我們有邏輯及也會批評(必要時)。如果你的團隊提出一大堆不實用的點子,當你是個管理者時,你應該要坐下和他們好好談談,幫團隊踩剎車。

問些嚴肅的問題,尤其是他們並沒有辦法確實回答(例如:"這有商業的案例嗎?"、"這件事是如何做錯的?"、"這件事真的實踐會花費多少時間")。如果這些點子不能通過你的檢驗,那自然也無法通過別人的檢驗。

最差的狀況是一個無法執行的點子沒被阻止。它進入了開發流程並且浪費了許多資源,且最終還是必須結束它。沒有任何一個人會因此開心。

3 不得不犧牲好點子

有時候你必須對些好點子說NO。因為現在不是適當的時間、適當的專案、適當的團員。但是不要就這麼浪費它們。將他們記下來。規劃時間去檢視它們。過了三個月後,它們也許會看起來不一樣。

4 你的知識是珍貴的,保護並且栽種它

了解小說世界的基礎。他的定義是什麼(哥德!),不是什麼(蒸氣龐克!)。闡述你的故事。不斷檢視更新對它的認知。使用書籍、影集、電視、音樂為它寫評論:"這感覺像是Melville,而不是Roddenberry"。評論自己的每次更新,不只是內部的,對外包工作者也是。

外包工作者並不會被工作環境每天影響。為了補足這部份,我們會讓團隊及外包工作者加入同一對話群組。用這方法,他們可以提出問題並參與部份團隊的討論。

5 培養自主權

Fallen London有超過150萬字。Sunless Sea則有30萬字。一個人的腦袋很難去管理這些。我們可以將這些做成摘要文件,或讓我們的文件系統是可以搜尋的。但是前者很難完成、而後者很花時間。

取而代之,我們開始讓團員擁有它們自己的劇情。像是Cash創作出了角色W,且我希望在故事中使用W,我會請Cash來幫助我了解問題。如果對開發角色有不同意見的時候,Cash會被負責做最終決定,這很有效率,而且對創意有正面的回饋。

2017年9月16日 星期六

深入遊戲設計:創造適合Reigns的故事

原文連結


Who: Nerial的遊戲設計師François Alliot


我是一位獨立遊戲設計師及遊戲開發者。我開發及設計Devolver Digital發行的冒險遊戲Reigns。我從三年前開始做遊戲,這是我的第二項職業。


What:撰寫包含機率的故事


與美術Mieko第一件決定的事就是遊戲的核心機制:利用滑過去的動作(有點像是Tinder)使國王選擇2個王國的選項,透過一組卡片來表示你的不同顧問。

每個選項都會衝擊到你的王國,你的目標就是讓王座能維持越久越好。事情很快變得越來越糟糕,而你會在各式狀況下悲慘的死去。

一開始、Reigns很像是債務管理的模擬遊戲:你必須去平衡四項權力(宗教、人民、軍隊、金錢)且應付顧問提出的各項選擇。

初始遊戲機制在情感及實際都運作的很好。這是一個隱含驚喜的玩具(一個很聰明的人某天告訴我的)。我們評估選項對四項權力的影響。我們給予這個玩具深層的意義。

前十分鐘我們感到很有趣。相信我這是個很好的開始。我們只需要再吸引玩家2個小時。

我們發現了玩家很快就把50張設計的卡片給玩完了。在不同的事件間又產生了不同的意義,我們自己從未想過的連結。像是飢荒或婚約。

我們決定建立在這結果之上,更加充實玩家在這前十分鐘所創造的故事。



Why: 為什麼?


為了達成這目標,我們將隨機抽出的卡片加入機率系統。這很難用簡單的方法來解釋。

想像一個包包擁有適合你內容的大小。一般來說,這包包含有所有遊戲卡片。當我選出一張卡片時,我開始將不適合這王國現況的卡片由包包中移除。

這有很多的組合,例如:如果這區塊的卡片都和皇后有關,但是你還沒結婚,就會被移除。像是卡片只會在宗教強大的時候觸發,但你的宗教很虛弱,也該被移除。同時也移掉玩家最近玩過的卡片。

這創造出整組玩家每次都玩不膩卡片。最後讓卡片都是佔有不同空間。卡片越大張會佔住包包越多空間。

最後你會擁有一個混合卡片的背包,大的卡片佔住大部份空間及其它小卡片。我就可以從包包內挑一張卡片來給玩家看。

卡片佔空間造成有趣的現像。大卡片被選到的機率大於小卡片。但是若大卡片在前幾步已經被抽掉了,那剩下的卡片機率就會大大增加。

這就是包包的擴張、縮小尺吋能力為什麼這麼重要。若你與鄰國開始了戰爭,那與些戰爭相關的10張卡會佔比較大的機率。且會把包包大部份的空間佔去,大於其它不相關連的卡片,例如小丑卡。這最大卡你挑到戰爭相關的卡片,而縮小挑到小丑的機率。

一旦戰爭結束,戰爭卡將被移出包包,系統有更大的機率去挑到小丑卡,因為它的機率上升了。



有些事件、像是地下城、決鬥會把系統縮限在一小塊區域。這時包包會非常小,只包含一點點的卡片。這小區域也可能只會有一張卡片,用來創造線性的故事(像是遇到惡魔)。

這機率系統的多樣性是創造遊戲的關鍵。我認為這樣的工具或規則來使遊戲變得奇幻及不可預期,甚至比明確定義故事內容的封閉系統還要完美。

我不介意Reigns是不完美的,這是這遊戲的不可缺少的一部份,這就是遊戲的特色。我們總是小心的創造每個事件能圍繞遊戲核心(有趣及遊戲特性),但我們不特別限制讓玩家特別磇到驚喜劇情(或者干擾玩家)。

創作Reigns真的是件類似探索的工作。你可以把事件照你所想的一樣緊緊包在一塊。我從單張的卡片開始,接著嘗試短時間的短篇故事線,然後持續增加,像是新角色及新的事件永久加入牌組,再來是惡魔的故事線及一堆圍繞著核心遊戲的卡片,帶領玩家進入未知的體驗。

例如(背叛者):有在森林中盲目奔跑、挑選奇幻香菇、在瘟疫中破解大臣的問題、處理沒有人性的奴隸問題、如何在愛情中表現適當、進入鴿子世界、和鴿子約會...



結論:


透過這個系統,我們可以創造不確定的故事,在非常廣泛的劇情卡、特別事件的分支劇情、碰到連製作團隊都沒遇過的特別組合。

Reigns遵從著Failbetter Games(製作SUNLESS SEA的團隊)高品質故事的製作方針。我們的品質來自於每次在有限的包包內放置的卡片組合。

最後,Reigns的核心體驗混合了每次所選的卡片,由特定方式隨機抽出的組合。這產生出非常有趣的組合。一但玩家發現有些牌是由前些選項所造成的,會使得每張卡都具有意義,因為玩家很難去辬別出隨機卡片會怎麼出現。

即使一小部份的卡片讓你覺得曾經看過,但整個遊戲會變得奇幻且難以預測,促使玩家去創造他們的故事。誰能說發現奇幻香菇不是僱用女巫造成的影響?又或者是瘟疫的一部份?

我也不能如此斷言。

2017年8月31日 星期四

[翻譯]Darkwood的開發故事

原始連結:http://imgur.com/a/xVhDz
如果你喜歡這些內容,可以去原始連結給製作者一些鼓勵。

我們都很害怕玩恐怖遊戲,所以我們離職去做一款沒有jump scares的恐怖遊戲。這是我們的堅持,我們的故事。

我們是Acid Wizard Studio。3位從波蘭來做遊戲的大學生(及一條狗)。經過4年的開發,我們剛剛將Darkwood完成了。一款由上往下視角且沒有jump scares的恐怖生存遊戲(但是仍然能嚇你一大跳)。

一開始我想先說這不是一趟輕鬆的旅程。事實上,我們每一個人在開發過程中都經歷過崩潰的低潮。

在做Darkwood之前,我們並非恐怖遊戲的玩家。在開發遊戲的過程,我們計劃逐漸克服自身對於恐怖遊戲及電影的恐懼。即使如此,Alien: Isolation對我們來說還是太可怕了。

在開始做Darkwood之前,我們並不太瞭解遊戲開發。我們在動畫及視覺設計上有經驗,但是在製作、程式、遊戲設計上沒有太多的涉略。

最原始的想法是在一個月內做一款塔防遊戲。
在過了無數個沒有睡眠及惡夢的夜晚之後,啟發了這個專案未來的雛型,我們在2013年做出了這個遊戲的雛型。我們利用它做出了遊戲展示影片,然後玩家的反應大大的超出了我們的遇料。

這個時候我們覺得這是個遊戲開發的大好機會,所以我們後半年開始在Indiegogo上募資,幫助我們完成遊戲。這次募資比我們原先所想的大大幫助了我們。

在募資本來狀況並不好,玩家覺得我們無法做好它。但是在最後一週,我們釋出了第2個遊戲的展示影片。這讓我們得到很多的助力,且讓募資趨向成功。我們訂下遊戲的完成日期為西元2014年。還真是天真的想法。

不幸的,我們在開發上的缺乏經驗使得日期大大地延遲。我們擴大了Darkwood的規模,及在遊戲核心上做出了些重大的改變,這花掉了我們很多時間。

財務上的問題迫使我們需要在Steam Early Access推出。玩家給予遊戲很好的評價,且銷售狀況也大大出乎我們意料。這鼓舞了我們,我們打算再次的將遊戲的規模擴大。

在這之後我們持續投入心力來儘快完成這遊戲。我們原定計劃沒有一項是準時完成的。由於過慢的開發速度,玩家的負評開始湧進來。我們也因此被影響使得開發速度更慢,甚至興起了放棄這遊戲的念頭。

開發變得非常的緩慢且沮喪。我們選了一條遠超過我們所能承擔的道路,但是我們知道我們擁有和別人不同的東西。我們必須完成我們理想中的遊戲。

現在是2017年,我們終於辦到了。遊戲已經發佈了1個禮拜。遊戲的評價都非常正面,我們被玩家的反應嚇到了。

我們沒有很多的時間來慶祝,還必須花很多時間來修正錯誤,解決玩家遇到的各式問題。我們其中一位成員(Gustaw)最近才剛滿30歲。他花了一整個晚上嘗試決解玩家遇到的存檔問題。

我們被湧入的信件所淹沒了。它們大大的超多我們所能回覆的數量。不幸的是有一大部份是圾垃信件。有些人稱自己是實況主或部落格來索取遊戲序號。這些序號有些流入了二手交易平台。事實上,我們十分厭惡這件事。這使我們無法從這些事情中脫身,或者將序號真的送到沒有錢購買遊戲的人。

Steam允許玩家在遊玩2小時內退貨,而且開發者可以看到這些人退貨的理由。我們看到一位玩家因為不想被家長看到月底的消費帳單而退貨。這讓我們感到十分不開心。

我們決定做件事來改變這狀況。如果你沒有錢可以買這款遊戲,我們放出了安全的種子在Pirate Bay,完全不綁定平台(DRM-free)。裡面沒有添加任何的惡意修改。我們只有一個要求,如果你喜歡這個遊戲,並且希望我們未來能持續做遊戲,請考慮未來在Steam、GOG、Humble Store買下它,即便是特價期間。但請不要在序號交易網站購買。若你在序號交易網站購買,你只會助長了非法交易的人們,他會逐漸侵蝕這整個產業。

感謝大家看完我們的故事。

2017年6月9日 星期五

[翻譯]Outlast是如何誕生的(下)

原文來源
上篇

遊戲開始了


由於這款遊戲要能被一隻小團隊所完成,我們需要讓這團隊具備所有能力。我們第一個雇用的人是Rejean Charpentier,一個了解Unreal並且有能力完成各式gameplay的軟體工程師。他和我們一同在解雇前在EA所停掉的專案工作。我們接著雇用一個有經驗的動畫師及美術人員Alexandre Sabourin,他有技術也有美術的天份。接著是另一位的gameplay的軟體工程師,Patrick Lalonde,,他擅長遊戲AI。

我們開始尋找角色建模師(character modeler)。我們需要外包人手,但我們在Montreal無法找到一個自由接案者,我們也不想找一個遠端工作的人。我們知道我們沒有時間去管理遠端工作。最後我們找到了Marc-Antoine Senécal,他只願意以全職工作的身份加入我們。我們當時不確定是否有足夠的工作給全職的角色建模師,後來證明了雇用他是正確的決定。

因為之前18個月在尋找資金的時間,也使得我們有很多時間去思考遊戲並且規劃流程。到了此時,團隊成員加入後,我們很清楚我們打算做什麼。即使還是有許多的事情需要去處理,但主要工作一直是不變的。所以我們與Game On Audio的作曲家Samuel Laflamme、及動畫師立刻開始製作demo。

我們的demo並不如想像中進展順利。雖然我們目標已經確定了,但在製作細節上,仍然花了許多時間。因為demo是遊戲製作的起點,所有玩家第一次見到我們遊戲的時機,它應該要是很完美的。雖然我們很希望能在10月底完成,但更重要的是讓整體工作流程能順利進行。

團隊內部的建議使得我們做了不少更動。做恐怖遊戲困難的部份在於恐懼是種讓人有意料之外的感覺,我們可以知道如何製作恐怖的事件,但很難評估它的效果如何。所以藉由外部人員提供反饋並加以調整是很重要的。

在萬聖節到來時,我們公開了遊戲demo。它受到很好的迴響,大幅地超過了我們的預期,讓我們真的被注意到。看著youtube的觀看次數持續上升,讓我們受到很大的鼓舞。

我們持續地製作遊戲demo,音樂整合是一個印像深刻的事件。在某個星期五的下午,我們一起把遊戲跑過一遍,大部份的開發人員還沒聽過音樂及音效,當NPC抓到了玩家並響起追逐音樂時,這就是我們所要的緊張感。

並非每一件事都是完美的,我們的動畫師就進展的不太順利。我們需要一位更加有經驗及自主的人。

我們雇用了新的動畫師Stefan Petryna,他曾經參與Far Cry 3製作,也有第一人稱遊戲的經驗。我們需要他立刻加入團隊並讓工作順利進行。

我們完成了第一次的遊玩測試,雖然不算是大災難,但也是個新遊戲的第一次上場。玩家可以看出遊戲的潛力,但我們要完成目標仍有許多工作要完成。這是個艱難的時刻,因為我們感到時間的壓力,以及知道要達到完整的產品但卻沒有明確的目標是個大災難。我們沒有延遲計劃的空間。我們曾經想過引入其它投資使我們有更多時間,但最後我們還是決定以現有的資源完成這遊戲。

我們從遊玩測試得知遊戲步調是其中一個大問題。玩家相當的融入遊戲的世界中,但是感到恐懼的時間太晚了。玩家覺得自己不是主角,而是個旁觀者。我們決定將第一章的一部份拿掉,將它放到第三章,在玩家次次到這個區域時。我們在這第一章加入一些恐怖的片段,使它變得完全不一樣。

另一個問題在於控制攝影機及開門的機制,這看來不夠直覺,需要讓它更加流暢。

視覺及音樂呈現出的整體氣氛受到玩家不少好評。玩家可以感受到我們營造的張力,並有動力去繼續探索遊戲。

我們花了大約2個月去解決這些問題,這段時間,我們出現了資源不足的問題。我們會再需要一個環境美術及在演出過程的動畫師。幸運的是,我們用不著多少時間就找到了正確的人手。Patrice Côté是曾經參與Splinter Cell: Conviction及Thief並且熟悉Unreal 3的美術。Jamie Helman是擁有15年經驗的動畫師,參與過Army of Two、Dead Space 2。他們加入的第一天就馬上開工了。

在2013年2月,我們已經準備好給記者的遊戲展示報導。我們看著他遊玩,並且開心的看著他被嚇到、大叫、咒罵。他寫了一篇非常正面的報導。雖然並非每件事都很完美,但我們朝著目標努力著。我們已經準備好完成遊戲剩下來的部份。

我們的下一個目標是PAX East。我們讓部份人手專注於這次展示,剩下的人繼續完成遊戲。我們試玩有40分鐘長度,為了這次展示需要將它濃縮到15分鐘。經過一番修改之後我們完成了。我們覺得這版本效果不錯,但不知道在PAX時是否能嚇到玩家。

為了增加遊戲氣氛的衝擊,我們把攤位佈置成可以隔離玩家,並儘量讓他們待在黑暗中。我們提供了2個由布簾隔開的座位。這表示玩家經過時沒辦法看到遊戲展示。我們也去找了所能找到最高級的抗噪耳機。我們感到十分焦慮,不知道到時會發生什麼狀況。

來我們的攤位試玩的玩家慢慢地增加了,尖叫聲開始出現在現場。玩家的試玩及在排隊狀況讓我們覺得很滿意。有兩次玩家因為嚇的跳起來撞到了攤位的牆壁。到了第三天,玩家願意花2小時排隊來試玩,我們非常高興。

最後衝刺


當我們回到Montreal時,整個團隊都很興奮。但我們還有很多工作必須在期限內完成。我們訂下了一個不允許出錯的緊湊計劃。我們剩下的時間只剩下各一次的內部與外部測試,但是並沒有時間去規劃測試回應後的修正。

我們沒有其它選擇了,這個遊戲必須在2013年秋天推出。

幸運的是我們的團隊已經完全進入狀況。所有人明白這是一個大挑戰,我們只能夠成功。

在過去我所參與的案子中,有件事是不變的。在開發過程,團隊會覺得自己是在處理一堆垃圾。這些狀況在我所參與的好遊戲、壞遊戲所感覺是一樣的。在製造的核心中,團隊因為將遊戲看的太仔細。沒有遊戲是完美的,所有遊戲都有Bug存在。這需要決心及有根據的信仰來克服這困難,在當時狀況下做出最好的表現。最重要的是,你要傳達給玩家創作的初衷及承諾。像是Outlast,它的初衷就是要把玩家嚇的半死。

在2013年7月初,我們做了外部測試。我們聽到有個玩家在精神病院外面遊蕩了40分鐘,因為她覺得壓力大到不敢進去。我們雖然有點罪惡但感到十分滿意。我們嘗試創造感情上的雲霄飛車而且看起來成功了。

大部份的玩家反應都很好,但是有些部份還需要再加強。遊戲的結尾是其中之一。

不幸的是,大多數遊戲,你在做結尾的時候通常時間也不怎麼多了。你雖然想要把它好好完成,但是不可必免的,你必須和現實妥協。在製作開始時,我們希望在黑暗恐懼的氣氛下結束遊戲,這是傳統恐怖電影的方式。但我們第一個版本的方向並不正確。

我們的動畫師們提出了一些有趣的點子,即使我們只有幾個星期的時間能夠完成。因為這要加入更多過場事件且調整目前的時間點,這表示聲音、音效也要跟著調整。這使整個團隊造成很大的壓力。

我們知道Outlast的結局有些玩家不太喜歡。我仍然不太確定是執行的問題還是氣氛不合。但是我們過幾個月所做的DLC的結局,比原來的結局更加合適。

在2013的夏天我們沒有看到幾次的太陽。這是個壓力極大的關鍵時刻,但這也是一個好遊戲及優秀遊戲的差別。製作上的最後衝刺會使這遊戲變的更加驚人。

我們的孩子誕生了

Outlast離完美還很遙遠,有些系列作的第一款作品常出現的問題,像是重複性過高、太少變化、急促的結局、關卡太短。我們從多數玩家得到的反饋,很榮興地,它有將其初衷傳達給玩家。

在2013 9月4日遊戲PC版發佈了,那天晚上我們很晚才入睡。即使我們做了所有的測試,它仍然存在兩個重大的BUG需要儘快修正。其中一項是可以在出貨前,最低需求配備的電腦上被測出來的,但我們太早停止在那台電腦的測試。另一個BUG是在某些電腦上DLL會造成衝突,但是剛好測試用電腦都沒有發生。有位玩家花了一整晚幫助我們的軟體工程師找出問題。我們將更新寄給他測試。我們非常感謝他並且將他加入遊戲製作的感謝名單。

玩家評價不斷的冒出來,有些正評,也有些沒有那麼好。但我們對整體評價感到滿意,尤其是在看到恐怖遊戲專門網站的評價時。

在發行後,我們計畫做DLC,此時工程師正在做PS4及Xbox One的版本。所以我們費了一番功夫才開始製作DLC。

我們快速的明白我們將會需要雇用兩位重要的幫手,Mathieu Gauthier,繪圖工程師(rendering programmer),他將幫助我們接下來的跨平台及強化視覺工作。開發Outlast也讓我們知道將會花費很多時間在臨時的音效外包上,所以我們也雇了一位音效專家Francis Brus。

不幸的是,這表示我們必須讓我們第二位遊戲工程師離開。我們還不確定未來的營收如何,所以我們沒有足夠的資源同時雇用三位工程師。

我們花了一些時間分析評論及玩家的反饋來決定DLC要做什麼改進,不幸的是工程師此時正忙於跨平台的工作。我們決定去強化我們的寫作工作。

在2013年11月,我們計畫花六個月去製作DLC。因為我們想要強化敘事效果,這代表了兩件事,更多的過場事件及對話。同時也增加了動畫師及音效人員的工作。

接著我們發出了PS4的版本。我們和SONY達成了協議,使PS+的玩家能免費下載這款遊戲。這件事在財務上很難說是正確的。但有件事是肯定的,我們開發下個系列作品時,我們會擁有更多聽過我們作品的玩家。我們也確實地達到了目標,在第一個月PS+就有1,800,000人次的下載。

在2014年5月6日DLC:Whistleblower在PC及PS4上推出了,過了2個月後,主程式及DLC也一起在Xbox One推出了。

本來並沒有打算在次世代主機上推出。因為那個時間,我們在專注於PC版本,但是SONY希望我們能將Outlast出在PS4上,我們得到了第一個出在次世代平台的恐怖遊戲的機會。最終因為Epic有提供很好的跨平台解決方案,這讓我們省去了很大的功夫。

一段時間後


回想在2013年9月,我們在PC上的銷售情況並不如我們所預期,但主要是因為我們對PC市場不夠了解,事實上大多數玩家會在遊戲打折購入。過了第一個月後,只有116,000套賣出,但在1月時銷售量達到了328,000套。過了一年的銷售後,大約有600,000套的遊戲在PC平台售出,平均售價約$12鎂,原始售價是$19.99鎂。這個數量讓我們可以衝到Steam第一面的暢銷排行榜。

到了今天,大約有3百萬套的主程式及DLC在所有平台被下載。我們非常幸運,這結果比所期望的目標還要好很多。這對我們來說不是一趟簡單的旅程,經過尋找資金、及全團隊共同努力製作遊戲,我們現在可以說任務完成了。我們創造了全新的IP及全新的團隊並且是完全獨立的。有了這次的營收,我們現在有能力去製作及獨立發行目前正在進行中的Outlast 2。

我們的奮戰因Outlast的銷售而成功了,我們不會停留在這次的成功。當然也必須留下Outlast的真正DNA,及玩家真的愛上這個遊戲的地方。但是我們想要促使團隊儘力去做出最佳的恐怖遊。這一次,我們的團隊在第一天就已經準備好了,而且開發流程也確定了,接下來可以專注於遊戲內容的開發。

Outlast的製作看來已經邁入終點,現在我們必須去思考這個遊戲系列以及公司的中長期目標。

在品牌系列上,首先我們必須先決定Outlast 2是否要接著Outlast的故事繼續發展。因為我們是做恐怖遊戲,我們不希望失去開始製作Outlast那種令人驚訝的感覺。Whistleblower有點喪失了那種在未知的世界探索的感覺,因為玩家對瘋人院已經熟悉了。我們決定製作Outlast 2是相同的世界觀,但是有著不同的設定及不同的角色。

我們也在評估是否要將Outlast放到不同的媒體上,這可以幫助我們拓展客群。

在公司經營上,我們希望能保留獨立開發並且不用去擔心資金不足的問題。Outlast 2會有較大的開發成本,但是我們會持續下決策來確保公司能負擔團隊的成長。

同時我們也決定要去創造一個適合團隊開發的工作環境。這也是個非常困難的挑戰,因為這需要的技能和做遊戲非常不一樣。在Outlast,我們只有專注在把遊戲推出,並沒有人去規劃、管理及其它做遊戲不同的事務。所以我們雇用了一個製作人,Anne Gibeault,幫助我們的團隊及公司成長。

這是一段我們創造自己團隊及遊戲的旅程。我們希望你對這段旅程的經過有興趣。

過了16年後,我感到很幸運能繼續製作遊戲及娛樂玩家。我希望未來還能有很長一段時間能持續做遊戲。

January 29, 2015 | By Philippe Morin