2015年10月26日 星期一

GameMaker:Studio 學習筆記7

1、Sound:

1.1、音樂檔案適合用壓縮格式,音效檔案不適合用壓縮格式,因為音效通常需要在很短的時間內播放,且重複播放頻率很高,採壓縮格式會增加CPU的loading,且可能會有延遲,而音樂檔案通常是長時間播放,只需在初始化時解壓1次,後續就不會再對CPU造成太大負荷

Target Options則應儘量和符合檔案原本的設定,若非必要不要去修改,用預設(default)值即可

1.2、audio_sound_gain(index,volume,time):設定聲音檔的音量volume,值可介於0(靜音)~1(原音量),time為音量變化時程長度,單位毫秒(千分之1秒),可以營造音量漸增或漸減效果,若要直接改變音量,time設置為0即可,注意若要直接改變音量,最好在播放前先改再播放,播放中改可能會有雜音出現,index若為聲音資源名稱則影響所有播放此聲音資源的instance都會受影響,若index使用的是特定instance聲音id,例如從audio_play_sound function傳回的id,就只影響其本身

1.3、audio_play_sound_at(index,x,y,z,falloff_ref,falloff_max,falloff_factor,loop,prioriity):以3D模式播放聲音,可很精確的指定聲音的位置和變化,參數的詳細用途以及相關functions可參閱語法說明


2、Application Surface:

為讓設計者能獲得對draw功能的全面控制能力而建立的surface,不能從遊戲中被刪除或移除

GMS並不是直接描繪畫面在顯示器上,而是在Application Surface上,再將Application Surface描繪在視窗或是顯示器畫面上,這樣可以讓遊戲在不同設備的不同螢幕大小上顯示變得單純,先將所有東西在AS上完成,再來只需要處理AS和設備螢幕大小的比例調整而省去不同顯示器大小對遊戲內容如座標等的影響,而一般情況下GMS會協助處理AS和螢幕大小比例問題。

當想自行控制AS的繪製時,要考量對象設備的螢幕大小據以計算相關位置,例如:

draw_surface_ext(AS,x,y,xscale,yscale,colour,alpha)

除AS之外其他處理是在ROOM的架構下完成,與螢幕大小無關,而AS的處理是相對於螢幕大小

以上述function為例,要手動繪制AS,其座標x,y是基於設備螢幕座標,而xscale,yscale卻是基於room大小,x,y的處理可透過appllication_get_position來取得AS在設備上的座標(需注意若有做surface_resize的動作,數值在下一個step才會變更)再依需求計算,而xscale,yscale的處理則要先取得AS在設備上的縮放比率後再據以調整大小,例如ROOM大小是1024*768(無View作用時AS的大小和Room一樣),而手機的顯示器是854*480,等比例縮放後(在global game setting 有勾選"keep aspect ratio"選項)AS在手機上的比例是640*480,xscale=640/1024=0.625,yscale=480/768=0.625

注意除了AS之外,其他自定義Surface相對關係一樣是在ROOM架構下,例如自定義surface_temp,draw_surface(surf_temp,0,0)此處的0,0會是指room的0,0而非設備螢幕座標

瞭解上述諸規則才能正確輸出想要的結果

視窗大小等同於設備螢幕大小,非全螢幕情況底下的視窗長寬不計其上方title bar和邊框

繪製順序


surface_resize(index,w,h):重新設定指定surface的大小,注意這將會crop或stretch此surface的內容,更精確的說這是destroy現在的surface,並以新的尺寸以及同樣的index重新建立它,這意味它必須先被清除以及重新繪製,除非此surface是application_surface,則GMS會自動清除及重畫

如果改變application_surface的尺寸,這些改變在下一輪的draw event才會開始生效,意思是在同一個event或step中使用application_get_position(),surface_get_width()或surface_get_height()傳回的值會是改變前的尺寸

application_surface:global variable,代表application_surface

當沒有"view"正在作用,application_surface的size會設定為和第1個room的大小相同,當room切換,而新的room和第1個room大小不同,則必須改變application_surface的大小以符合新room的大小

view:

view in room:要在screen上秀出來的room區域

port on screen:要顯示view內容的螢幕區域

例如把room裡面的一塊200*150的區域在螢幕的一塊指定區域(0,0,200,150形成的方形區域)顯示,和在以另一個指定區域(0,0,400,300)比較,後者就像是把遊戲畫面放大2倍

application_surface_draw_enable(boolean):若要手動繪製,為避免double drawing,應設定為false

draw_surface_ext(index,x,y,xscale,yscale,rot,colour,alpha):和draw_surface一樣會繪製指定的surface,但可以給定比例、旋轉角度、混色、透明度,需注意surface(除了application_surface之外)可能在任何時間被清除,故在參照之前應先以surface_exists function確認其存在,避免參照錯誤發生

注意:當手動移動application surface之後,mouse events將不會正確登錄,mouse clicks和position依然會被偵測(keyboard不受影響),但需自行計算正確的值

繪製在Application_Surface的東西會自動隨顯示器的畫面大小縮放,而畫在GUI Layer上的則不會,或是需經過其他設定

3、Coding:

關於維護的心得:

以變數的方式取代直接給定值的方式可避免在參數改變時修改太多程式碼,變數搭配適當的名稱可提高效率,以下例子說明

if gamestate=1 //1代表玩家勝利
{
...
}

if gamestate=Player_Win //很明顯的是代表玩家勝利
{
...
}

"Don't Repeat Yourself" principle(DRY):重複的程式碼以寫成function呼叫,不要重複編寫

"Single Responsibility" principle(SR):將需要處理的項目切割,1段程式區塊處理1個項目,清楚區分功能,易管理

關於效率的心得:

二維區間搜尋法:

var low=0;up=mapheight-1;midd=0;

while up>=low//迴圈直到收斂至剩單點為止
midd=round((up+low)/2);//取上下限中點 

if (mouse_y > (ypos[midd,1] + blank_halfheight)) low=midd+1;//未命中且大於中點範圍上限,下限點改為中點+1

else if (mouse_y < (ypos[midd,1] - blank_halfheight)) up=midd-1;//目標小於中點範圍下限,上限點改為中點-1

else break;//命中則結束
}

tb_row=midd;

讓sprite跟隨指定目標轉向的程式碼寫法:

var dd;

dd = ((((point_direction(x, y, global.Player_x, global.Player_y) - image_angle) mod 360) + 540) mod 360) - 180;

image_angle += min(abs(dd), 5) * sign(dd);

效率原則:

a.容易命中的條件先判斷,順序擺在前

b.比對速度快的條件先執行

c.正向舉證與逆向舉證:有黑白兩色,當白色較少時,以白色為搜尋目標會比用黑色好,因為把白色找出來剩下的就是黑色,而白色比較少,需要進行的搜尋動作也相對較少

d.任何動作都是需要時間的,包含變數宣告,故以效率為重時,在create時先宣告變數,會比在使用前才宣告有效率,使用前才宣告的好處是能以local變數即用即釋放,降低記憶體的佔用,應視需求調整宣告方式

e.很多條件是很少會成立的,建立巢狀關係,讓發生機率高的優先判斷,進入後就不用進行後面的判斷,省去不必要的判斷,例如:

整體考量a.b.兩點,則"條件命中率之反比" * "條件比對動作數" 值愈小者順序應愈前面

例如:

if a=5和if a=9兩個條件,已知a=5的機率高於=9的機率,則a=5的條件應列在前先做判斷,可減少條件確認次數

if a>10 and a<20和if a>20兩個條件式,a>20條件式需比對的動作較少,則a>20條件應先進行判斷

正向舉證與逆向舉證:

可以從一堆目標裡比對不要的以篩選要的,也可以直接比對要的,端看何者效率高,例如SHUTU裡的關卡結束判斷條件,可以很麻煩的一格一格確認其格線是否已連,亦可直接比對敵人受攻擊次數達到目的,但效率天壤之別,同樣的可以一格一格判斷是否是不可逆連結,是的不加總以求出可動總數,亦可以一開始就紀錄總數,然後在攻擊敵人後將攻擊數扣除,程式碼編寫簡單且執行效率更好

關於語法的心得:

要將文字與變數組合需使用"+"符號,且要注意要組合的元素必須是同形態,文字只能跟文字組合

例:

m=5;

combine="abc"+string(m);

用real()將get_string()的結果轉為數字可避免大部份的輸入錯誤,例如按到空白鍵

real(get_string())

用string()將數字轉文字可讓輸出結果不帶多餘的小數位數,例如寫入ini檔或是get_string預設值

get_string("test",100)畫面會顯示預設值100.00

get_string("test",string(100))畫面會顯示預設值100

重要:

將規則以圖形表現出來能大大幫助編程思考

遞廻語法範例(參照SHUTU):

///scr_6IsFree(ar0,ar1)

if cto[ar0,ar1]=0

{
if isactive[ar0,ar1]=1 global.isover=0;
}
else
{
scr_6IsFree(desty[ar0,ar1],destx[ar0,ar1]);//開始遞廻
}

4、 User-Defined Surfaces:

通常情況下,所有drawing都是作用在application surface,但使用者可指定將特定的drawing作用在自定義的surface上,這代表這些東西不會被看見,直到使用draw_surface function把自定義surface繪製出來,這同時代表繪製在其上的東西也將被看見

surface初始左上角座標為0,0,繪製東西於其上時,座標位置與一般情況無異

自定義surface是保存在VRAM(顯示記憶體),這表示它隨時會被"徵收"而消失(當系統需要用到它的VRAM時),故使用上需特別留心,在參照特定surface前要先確認其是否依然存在,若不存在則需先重新建立,同時需儘量最小化surface佔用的VRAM量,因為用盡VRAM會發生其他的問題,surface繪製時是預設在room的0,0位置,所以如果是在一個大room使用views或僅是想要一個小區域做特效,記得調整surface繪製區域,避免浪費VRAM和執行效能

以surface_create function建立surface之後,接著以surface_set_target function設定該surface為drawing作用目標,在開始繪製東西於此新建surface之前,需先用draw_clear_alpha function把畫面清乾淨,然後再開始畫東西,以避免殘留影像造成預期之外的結果,這算是新建surface時的一個歸零動作,對於已使用過的surface則不需要此動作,除非是想清空該surface上的舊圖像

以畫布圖層比喻說明surface運作規則:

surface就像是一張畫布圖層,使用者畫需要的東西在上面,然後暫存在VRAM裡,在需要的時候在screen(application_surface)上描繪(draw_surface)出來,或是要改變上面的圖像時呼叫出來(surface_set_target)修改,妥善運用surface可節省資源,例如繪製陰影,每個step在各個instance執行繪製陰影的動作是非常沒有效率的,可先將陰影全部畫在指定的surface上,然後每個step描繪該surface1次即可有相同的效果,相對於原本的方法,這是非常有效率的方式

運用surface的基本流程如下:

Step1.確認surface是否存在:存在且並不想改變上面的東西則可以draw_surface把上面的東西描繪在screen上顯示出來,若不存在則需先建立surface(surface_create),接著開始在上面繪製想要的東西

Step2.在surface上面繪製東西:指定接下來的繪製目標(預設為application_surface)為該畫布(surface_set_target),若為初次繪製,在繪製前需先清除畫面(draw_clear_alpha),避免殘留影像污染畫布,清除完後開始繪製(各式draw指令),當此畫布想畫的東西都已繪製後,需將繪製目標還原(surface_reset_target)成screen(application_surface),畫面才會正常顯示

Step3.視需求在screen上"描繪"(draw_surface)該畫布,使其可見,或是修改上面的東西,同樣的,參照surface ID前需先確認其是否存在

Step4.當不會再使用特定surface時,記得刪除它以釋放VRAM,確保遊戲運行穩定

一個節省VRAM的技巧是建立較小的surface,然後以較大的倍數繪製surface,例如screen大小為800*600,則可建立1個400*300的surface,然後用draw_surface_ext以2倍的xscale,yscale設定繪製surface,同樣可有full screen的大小,但要調整繪製於surface上的圖像的位置和大小,使放大後的結果和需求一樣,例如若建立400*300的surface,想讓此surface2倍放大繪製後的結果和800*600的surface一樣,則繪製於其上的圖像座標需除以2(這樣相對位置才會相同,因為座標都是從0,0開始),大小也需除以2,放大後才會是同樣的尺寸

5、make_colour_hsv(hue,sat,val):以hue(色彩)、saturated(飽和度)、value(明暗度)三個參數定義顏色

hue:0~255,255種基礎色彩

saturated:0(灰)~255(純色)

value:0(暗)~255(亮)

沒有留言:

張貼留言