創作內容

4 GP

[RMMV] 個人筆記用

作者:未来ずら│2016-02-23 21:54:50│巴幣:1,006│人氣:1488
拿來備忘的,和過去版本作比較,不一定正確,會不定期更新修正





腳本執行順序
用筆記本打開專案底下的 index.html 可以看到預設腳本的載入順序,
再由 main.js 內部載入插件腳本


預設腳本負責項
依載入順序--
pixi:圖像管理核心
fpsmeter:顯示FPS的外掛
lz-string:解せぬ
rpg_core:舊版本所謂的內部類,另外還有些js標準庫的追加方法,這次可直接改 (゚∀゚)
rpg_managers:VX後開始出現的 Manager 類,其實不太懂,主要是負責最底層工作
rpg_objects:舊版本所謂的 Game 類,內含VX後改叫 Game_Interpreter 的事件解釋器
rpg_scenes:場景類
rpg_sprites:舊版本所謂的RPG::Sprite 類、Spriteset 類
rpg_windows:視窗類
plugins:管理插件腳本載入順序
main:載入插件和開始遊戲運行,這版本是由 Scene_Boot 開始





常用內建指令
alert()
相當於舊版本的 p 及 msgbox 方法
alert(123); // 顯示123

console.log()
在控制台顯示變量內容,相當於VA的 p 方法
console.log(a); // 在控制台顯示 a 變量內容
不過,會以很清楚的清單列出物件所有屬性,不像過去的版本擠成一團

require('nw.gui').Window.get().showDevTools()
呼叫控制台(本機模式限定,網頁版因為沒有require函式不能用,不過瀏覽器都有內建就是)

Utils.isOptionValid('test') $gameTemp.isPlaytest()
檢查專案是否在測試模式

Utils.isOptionValid('btest') DataManager.isBattleTest()
檢查專案是否在戰鬥測試模式

Utils.isOptionValid('etest') DataManager.isEventTest()
檢查專案是否在事件測試模式



建構式/構造函式(constructor)
由於 Javascript 沒有 class 概念,所以 class 在 js 也得用函式(function)定義

Ruby 長這樣的東西:
class People
def initialize(name, age)
@name = name
@age = age
end
end

js 會變這樣 (MV實際上會在裡面調用 initialize 方法,設計者好貼心):
function People(name, age) {
this.name = name;
this.age = age;
}

做為建構式使用時就會像這樣:
var people = new People('你老媽', 17);
好,17歲的你老媽就做出來了


另外,建構式也能這麼寫(用這種方式定義屬性時要用 逗號 不是 分號 ):
function People(name, age){
name : name,
age : age
}
有沒有感覺好像在RGSS的哪看過類似的東西...?是的,就是Hash
你可以把一個物件當成hash資料使用


前面那兩個 name 和 age 就是物件的屬性名,生成後可以像:
people.name ;

people['name'] ;
取得該屬性的值(當然也可修改)
對,JS 的物件基本可以當成 Ruby 的 Hash 使用
很重要所以說兩次


物件屬性

簡單說就是 js 函式裡面除了算式外就是屬性,我是直接看成 Ruby 的實變量+attr_accessor
對應值可以是數字、數組、物件、或是函式...等等
JS 在這方面很直接很自由,可以先做個空的物件
var a = new Object();

var a = {};

然後再來加上他的屬性
a.name = '你老媽';
a.age = 17;

a['name'] = '你老媽';
a['age'] = 17;
這樣都不會出錯
而後者的好處是還可應付這種情況:
a.123 = 'gg'; // 出錯
a['123'] = 'gg'; // 沒事,但注意如果 a 物件是數組會視作 a[123],數組的屬性就是每個index



默認函式
function People(name, age){
    this.name = name || '你老媽';
    this.age = age || 17;
}
如果調用時沒丟參數,就會用默認的你老媽和17
只是這時不能用「屬性 : 值」來定義


圖片優先度
這次 Sprite、Window 等和圖像相關的類(以下統稱Sprite) 不像 RGSS 有 Z 軸,
而是改成用「chidren」屬性儲存
也就是說 Sprite 對象建立好後還得對該 Scene 使用 addChild() 追加進去
不然依舊什麼都看不到

還有 Sprite 一次只能待在一個 children 裡,也就是會被搶走:
// 假設已有A、B、C三個Sprite對象
A.addChild(C);
A.children; // => [Sprite(C對象)]
B.addChild(C);
A.children; // => []


優先度算法:
1.children > parent
2.同個children內,後者(含它的所有child) > 前者(含它的所有child)

children 中越後面的 Sprite 越優先,還有就是 children 的優先度較大
所以:
A = new Sprite;
B = new Sprite;
C = new Sprite;
D = new Sprite;
E = new Sprite;
A.addChild(B); // B > A
A.addChild(D); // D > B內所有child
B.addChild(C); // C > B
B.addChild(E); // E > C


優先度為:D > E > C > B > A

※Sprite 本身沒有 children 屬性,但做成物件後會繼承 PIXI.Sprite 的內容

至於最上層用來存放這些 Sprite 的似乎是一個 Stage對象
額,一樣是 PIXI 模塊的東西

另外,最基本的顯示圖片方法為:
document.write('<img src='根目錄下的圖片路徑'>');
預設腳本中有幾個地方是用 new Image() 做出<img>的tag......算了,扯太遠


過去的Graphics.update
這次沒這玩意了,類似的東西也不在 Graphics 模塊中
MV使用的更新方式為 requestAnimationFrame
但這函式的更新速度因為和瀏覽器綁定,無法調整

不過天無絕人之路,還有個算是 requestAnimationFrame 前身的 setTimeout
靠這函式就可以自行決定更新速度

不過,效率上落差有點大.....
另外如果真要改用 setTimeout 更新,像計時器之類的計算也要改

遊戲運作流程
配合上面順便解析一下 MV 的整體運作流程:

先載入預設腳本和插件
Main.js 最後一行有個:
window.onload = function() {
SceneManager.run(Scene_Boot);
};
SceneManager.run() 當中有一行:
this.requestUpdate();
SceneManager.requestUpdate() 當中的一行:
requestAnimationFrame(this.update.bind(this));
這表示用了 requestAnimationFrame() 去執行 SceneManager.update()
SceneManager.update() 中找到:
this.updateMain();
SceneManager.updateMain() 當中就是 Scene 的更新和描繪,
但重點是這裡再度出現:
this.requestUpdate();

於是循環成立

但光是循環成立不夠,
如果沒有固定反覆調用的頻率遊戲運行速度就是暴衝狀態  (メイド・イン・ヘヴン!!)

而在剛有提到 requestAnimationFrame()
額,總之它執行後不管早或晚都要等瀏覽器再度刷新畫面(1/60 秒)後才能再跑一次
如此遊戲就會以 fps60 的速度更新內容了

可以修改 SceneManager.requestUpdate() 來確認這件事:
var a = 0;
SceneManager.requestUpdate = function() {
    // 取得和上次執行的時間差
    console.log(Date.now() - a);
    if (!this._stopped) {
        requestAnimationFrame(this.update.bind(this));
    }
    // 記錄目前時間
    a = Date.now();
};
此時進入遊戲打開控制台,
可以看到一連串的數字在跑,大約在 16 左右 (也就是1000/60 豪秒)

※時間偵測和計算寫反的話會變成0,
   因為顛倒只能得到跑完這方法的時間,而不是跑這方法的間隔




for...in...
for (var 屬性名 in 物件){
(內容)
}

和Ruby不同的是拆出來是屬性名而不是值
所以要對值操作就是
for (var 屬性名 in 物件){
物件[屬性名] = xxx;
...
}
Array的屬性名理所當然的是每個 index,上面有提到



Object.defineProperty 與 命名空間汙染
Object.defineProperty 是一個定義對象屬性的方法,調用方法後會返回該屬性值
Object.defineProperty(物件, 屬性名, 特性描述)

簡單說和一般直接設定的不同點在:
可以決定該屬性可不可修改、可不可被列舉 (顯示在for...in...的結果中) 之類的,也就是下面這兩個隱藏屬性:
enumerable:允許列舉 (false / true)
configurable:允許修改特性 (false / true)
(因為預設腳本都是用 get/set,所以 value 和 written 就跳過)

直接設定的話,上面兩個預設是 true;
用 Object.defineProperty 就是 false,
預設腳本目前看來是為了不讓某些屬性顯示在列舉結果才用到這個

以 ConfigManager 的 bgmVolume 屬性為例:
Object.defineProperty(ConfigManager, 'bgmVolume', {
    get: function() {
        return AudioManager._bgmVolume;
    },
    set: function(value) {
        AudioManager.bgmVolume = value;
    },
    configurable: true // 允許修改特性
    //但是,此屬性不會出現在列舉結果
});


從這圖可以發現 bgmVolume 沒出現在列舉清單,但實際存在,值為80


Object.defineProperty(ConfigManager, 'bgmVolume', {
    get: function() {
        return AudioManager._bgmVolume;
    },
    set: function(value) {
        AudioManager.bgmVolume = value;
    },
    configurable: true,  // 允許修改特性
    enumerable: true  // 可被列舉
});
這樣在遊戲中執行 for (var i in ConfigManager) {console.log(i)}
bgmVolume 就會顯示出來了


這麼做還有另一個用意就是防止命名空間汙染(namespace pollution)
額...汙染就是...對Object、Array這些原生類追加屬性後產生一些預料外的結果
詳見以下 for...in 的應用:
// 來自 http://uupaa.hatenablog.com/entry/2012/11/07/143947 的範例
Object.prototype.myMethod = function() { console.log("myMethod"); }; // 汚染

function eachObject(obj) {
    for (key in obj) { console.log(key); }
}

function eachArray(ary) {
    for (key in ary) { console.log(key); }
}

eachObject({ a: 1, b: 2 });      // "myMethod", "a", "b"
eachArray([ 1, 2, 3 ]);         // "myMethod", "0", "1", "2"
範例中 Object 用prototype追加了 myMethod 屬性
於是在列舉屬性名的時候就算不想也會出現 myMethod 屬性

「只是出現在列舉清單中,有什麼問題嗎?」
所以接下來拿 MV 的情況舉例

MV 本身已經對 Array 進行擴充了幾個方法
現在馬上用控制台簡單做成一個數組:
arr = [1,2,3] // 返回 [1,2,3]

接下來用 for...in 把 arr 的內容加總:
for (var i in arr){
   if (!count) {var count = 0;}
   count += arr[i];
} // => ...?


結果會是預想的 1+2+3 = 6 嗎?太可惜了!是一大串字串:

可以看到6後面多了一大堆字,這些就是 MV 對 Array 擴充的方法內容

這種因為對原始類擴充產生的意外問題,就是汙染
(※MV這個問題在v1.3還v1.5已經修正了)


但是,內建腳本數組的 for...in 還是用得很開心啊?
就表示還是有迴避方法
比方說改用 length (MV 都是採用這種方式):
for (var i = 0; i < arr.length; i++){
   if (!count) {var count = 0;}
   count += arr[i];
} // => 6

或是使用 hasOwnProperty (不算進prototype和繼承的屬性):
for (var i in arr){
   if (!count) {var count = 0;}
   if (arr.hasOwnProperty(i)) {count += arr[i];}
} // => 6

(換句話說,能在自己的控制範圍內就不算汙染,一個我說了算的概念)


往後若想對原生類擴張,盡量使用 Object.defineProperty 定義較安全


正則
javascript 中沒有之前版本的 「~=」 運算符,而且匹配結果也不會自動存到$1~$n
所以要這麼寫:
var 匹配結果 = 正則.exec(被匹配的字串)

例:
var result = /a(b+)cc\d/.exec('aabbcc123')[0]; // => abbcc1




調查相關

獲取此對象的建構式
對象.constructor // 但如果是匿名函式只會看到內容不會看到名稱

RGSS 的 「is_a?(class)」
對象 instanceof 建構式
對象.is()

獲取此對象是否定義該屬性 (類似 RGSS 的 respond_to? )
屬性名 in 對象
or
對象.hasOwnProperty(屬性名) // <= 繼承的、或使用prototype定義的屬性也返回false

取得對象所有屬性名
Object.keys()

Ruby 的 caller (檢查此函式從哪個函式調用)
函式.caller

得寫在函式內才能發揮效果
※嚴格模式(strict mode)下使用caller會出錯


條件分歧判斷
js的條件分歧會判定為 false 的值為:
undefined、null、false(廢話)、NaN、0、空字串(''或"")
挺多的,最麻煩的是 0空字串 也不能用
空數組反而可以,泥馬...

要注意的是值為 false 的Boolean對象會被判定為true
也就是用 new Boolean(false) 做成的對象
...也不知道什麼時候才會刻意去做成Boolean對象,基本上不用管



別名&繼承

別名
以 Game_Interpreter.prototype.pluginCommand 為例:
    // 原式代入一個變量
    var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
    // 再定義
    Game_Interpreter.prototype.pluginCommand = function(command, args) {
        (追加內容)
         _Game_Interpreter_pluginCommand.call(this, command, args);  // 呼叫原式
        (追加內容)
};


繼承
以 Window_Selectable < Window_Base 為例 (藍字處視需求修改) :
// 建構式定義
function Window_Selectable() {
    this.initialize.apply(this, arguments);
}
// 繼承
Window_Selectable.prototype = Object.create(Window_Base.prototype);
Window_Selectable.prototype.constructor = Window_Selectable;

// 擴充initialize
Window_Selectable.prototype.initialize = function(x, y, width, height) {
    Window_Base.prototype.initialize.call(this, x, y, width, height);
    (Window_Selectable要用的其他內容)
};





同步(synchronous)、非同步(asynchronous/non-blocking)

有時會看到函式分為這兩種,然而--

假設程式這麼寫:
A
B
同步:依序執行,也就是 A 跑完才跑 B
不同步:並行處理,所以可能 B 跑完了 A 還在跑

第一次接觸還挺容易搞錯
中國那邊不同步叫做異步,比較好懂些


事件監聽器
簡單說還有一個在背地負責偵測操作的東西,
用這方法可以在特定操作(事件)成立時調用指定方法
下面是從 rpg_core.js 截出來、監控「按下按鍵」這動作的監聽器
// 調用本對象的 _onKeyDown 方法
// bind 查了一下是確保調用對象(這寫在 Input 的方法內,所以是確保 this 為 Input)
document.addEventListener('keydown', this._onKeyDown.bind(this));


引用網址:https://home.gamer.com.tw/TrackBack.php?sn=3110272
All rights reserved. 版權所有,保留一切權利

相關創作

同標籤作品搜尋:RM|心得

留言共 3 篇留言

sanmoelen
大感謝

02-24 08:33

劍魔魂
汙染 好像跟Ruby的汙染不一樣?! 還是說只是單純在Ruby只是相同的名詞但意義不同呢?
Ruby的汙染算是一種標記,Ruby會依照安全等級對受到污染的對象做一些限制手段
(比較常見的為Ruby的gets方法(標準輸入)獲得的字串就是被汙染的,也可手動汙染對象)

03-30 00:30

未来ずら
額,似乎是不同東西
這裡的汙染是命名空間汙染,指編寫時對一些原生類再定義可能會引發誤操作
和安全模型有關的是另一個東西了,但不清楚命名由來是不是有關連03-30 05:24
劍魔魂
看來因該只是相同名詞但意義不同,順便說一下Ruby的汙染名詞由來(雖然只是我的猜測)
Ruby被標記為汙染的對象,是具有所謂的 傳染 性質的,汙染生出來的東西也是汙染的東西
a = ""
b = gets # 標準輸入
# tainted? 方法:判斷對象是否汙染
p a.tainted?, b.tainted? #=> false, true
a += b
p a.tainted?, b.tainted? #=> true, true
p a.clone.tainted? #=> true

03-30 11:18

未来ずら
https://zh.wikipedia.org/zh-tw/%E6%B1%A1%E7%82%B9%E6%A3%80%E9%AA%8C
我是看了這個,Ruby這邊提到的是防止使用者以不當操作攻擊伺服器,是和網路有關的東西

一個在編寫階段、一個在執行階段,犯人一個是編寫者、另一個是使用者

另外兩者的英文,
命名空間汙染用的是pollution,安全模型用的是tainted,也是不一樣03-30 17:30
我要留言提醒:您尚未登入,請先登入再留言

4喜歡★qootm2 可決定是否刪除您的留言,請勿發表違反站規文字。

前一篇:[RMVA] 題材拉霸機... 後一篇:[RMMV] 1.1.0...

追蹤私訊切換新版閱覽

作品資料夾

GeminiRose大家
小屋更新漫畫啦!喜歡漫畫的朋友請不要錯過!可以的話,請多多支持!看更多我要大聲說昨天19:56


face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】