切換
舊版
前往
大廳
主題

RM MV學習筆記(1) 創建場景跟視窗

路漫行 | 2018-01-17 11:31:03 | 巴幣 1040 | 人氣 4939


大概一個月前,下定決心把去年特價在Steam上買好的RPG MAKER MV拿出來玩,稍微找了一下資料,決定轉換跑道從VX跳到MV,由於兩者間差異頗大,所以VX上的羅小寶不能直接套進MV裡面,那就想既然換新引擎,不如就來開個新專案來玩吧。

所以羅小寶可能會陷入無期限停工狀態,至於新專案還在研究中,就先不公開了。

然後這系列主要是我為了學習MV而寫的筆記本,

首先我入門是看這系列:siakoMobi | RMMV

siakoMobi人真的很好,非常推薦去看

看完之後還有不懂的部分可以參考:JavaScript MDN


另外也有一篇巴哈小屋很適合新手去看

qootm2的小屋:[RMMV] 個人筆記用

後篇也讓我起了一些想法,其實我也可以把我自己的研究筆記整理一下,幫助自己記憶,也分享給其他人一起學習,因為是筆記所以內容可能有點隨性,而且我這人毫無程式基礎,只會一堆小聰明去鑽漏洞直接COPY別人寫好的來用,所以應該很適合給沒基礎的人看(花哈哈哈哈哈哈)。

WELL JUST HAVE FUN

-------------------------------------------------------------------------------------

在腳本的第一課,我們要先了解什麼叫做場景(Scene),還有什麼叫做視窗(Window)。

我們來看例圖,最外圈紫框就是場景,中間的紅框就是視窗。


換句話說,場景是整個遊戲畫面,視窗則是可以跟遊戲互動的操作介面。每一個場景跟視窗都有自己的名字。選用標題畫面當作範例是因為他最單純!

比如說這個標題畫面叫做: Scene_Title ,所有的預設好的場景都會放在 專案名稱\js\rpg_scenes.js 這個檔案裡面,所以如果我想修改標題畫面該怎麼作呢?

用筆記本,或是其他文字編輯器去打開 rpg_scene.js 然後用Ctrl+F去搜尋Scene_Title

會找到像這樣的東西:


哇喔,好多喔,先不管他把這一整段COPY下來,貼到一個新的檔案裡面,隨便幫檔案取個名字吧,就叫做 Scene_Title_new.js 吧,並放到 專案名稱\js\plugins\ 資料夾的下面:


接著就把我們新的new掛進遊戲裡面,在編輯器環境下按F10,開啟插件管理器:




並且設定為ON


好啦,這樣插件就設定好了,可以進遊戲看看了!

什麼,你說有什麼不一樣?當然是一樣的,因為還沒開始改嘛,先來改個背景圖試試看:

找到Scene_Title_new.js 裡面這一段:


Scene_Title.prototype.createBackground = function() {
    this._backSprite1 = new Sprite(ImageManager.loadTitle1($dataSystem.title1Name));
    this._backSprite2 = new Sprite(ImageManager.loadTitle2($dataSystem.title2Name));
    this.addChild(this._backSprite1);
    this.addChild(this._backSprite2);
};


阿,字太多了,再少一點,改成下面這樣:


Scene_Title.prototype.createBackground = function() {
};

他是一個function,function就是函式,那函式又是甚麼呢...就是...可重複執行的程式段落(By Google)。總之紅字的部分Scene_Title.prototype 表示這個function屬於Scene_Title

藍字的部分createBackground則是自定義的 function名稱,通常用來表達function的功用,顧名思義createBackground就是用來顯示背景圖片用的。至於裡面寫什麼呢?

this._backSprite1 = new Sprite(ImageManager.loadTitle1($dataSystem.title1Name));

// this._backSprite1:this._  這個開頭表示對自己,backSprite1是自定義的變數名稱
// new Sprite():預設好的類別,用來呼叫圖形處理。
//  ImageManager.loadTitle1($dataSystem.title1Name):讀入的圖片路徑,這一行會指向資料庫裡面設定的位置。



不過我現在不想要用資料庫設定,所以我來改寫一下,把$dataSystem.title1Name換成'Dragon' 結果如下:

然後試試看結果:


成功把背景圖換掉了!

如果不喜歡這個更動,再去插件管理器裡面把Scene_Title_new 設定改為Off ,就變回原本資料庫設定的樣子了,這樣做的目的是可以盡情的改寫插件內容,同時避免把遊戲本體改壞!好處非常的多,然後JS的設定上,同名的函式會跑後面的那個,所以兩個Scene_Tilte的時候,會顯示我們改過的而非內定的。

這樣做的好處就可以把東西刪光光也不會壞掉

現在我們要來說明一下這個函式內的細節,再看一次程式,這次先把重複的內容註解掉:

Scene_Title.prototype.createBackground = function() {
    this._backSprite1 = new Sprite(ImageManager.loadTitle1('Dragon'));
    // this._backSprite2 = new Sprite(ImageManager.loadTitle2($dataSystem.title2Name));
    this.addChild(this._backSprite1);
    // this.addChild(this._backSprite2);
};

Sprite() 是一個類別,專門用來管理圖形處理,第一行意思是將 this._backSprite1 指定成一個Sprite。ImageManager用來指定圖片的路徑。

ImageManager.loadTitle1('檔案名字')   ==>  專案資料夾\img\title1
ImageManager.loadTitle2('檔案名字')   ==>  專案資料夾\img\title2

最後的addchild則是用來將Sprite加進來,add的順序會影響到圖片上下層,越後面讀入的圖片會在越上層。比如現在我們也把this._backSprite2給一張圖片

this._backSprite2 = new Sprite(ImageManager.loadTitle2('Floral'));

然後測試一下看結果:


this._backSprite2果然顯示到this._backSprite1的上面了,如果改成:

this.addChild(this._backSprite2);
this.addChild(this._backSprite1);

那就會看不到this._backSprite2了,因為this._backSprite1比較大張,會把this._backSprite2整個蓋掉。

現在你想放幾張圖就可以放幾張圖,只要會複製貼上改檔名就行了!

想對Sprite了解更多的話,可以來看這裡:幫助文件



再來我們要介紹一下Scene_Title裡面其他東西,從上面開始看好了

function Scene_Title() {
    this.initialize.apply(this, arguments);
}

Scene_Title.prototype = Object.create(Scene_Base.prototype);
Scene_Title.prototype.constructor = Scene_Title;

Scene_Title.prototype.initialize = function() {
    Scene_Base.prototype.initialize.call(this);
};

這裡是宣告,表示創建了一個叫做Scene_Title的場景,並且繼承了Scene_Base裡面所有的函式跟變數。如果想要創建自己的場景,只要Copy這段然後把Scene_Title的名字改掉即可。

下面兩段是環境初始化,用途在於預先讀入會用到的所有函式、圖片、物件:

Scene_Title.prototype.create = function() {
    Scene_Base.prototype.create.call(this);//繼承Scene_Base.prototype.create
    this.createBackground();// 這裡叫了讀背景的函式
    this.createForeground();
    this.createWindowLayer();
    this.createCommandWindow();// 這裡叫了命令視窗
};

Scene_Title.prototype.start = function() {
    Scene_Base.prototype.start.call(this);//繼承Scene_Base.prototype.start
    SceneManager.clearStack();
    this.centerSprite(this._backSprite1);// 這裡叫了背景用的圖1
    this.centerSprite(this._backSprite2);// 這裡叫了背景用的圖2
    this.playTitleMusic();// 這裡叫了背景音樂
    this.startFadeIn(this.fadeSpeed(), false);// 這裡設定淡入顯示
};

那為什麼要區分create跟start放在一起不好嗎?他們有什麼不一樣?除了名字不一樣以外,繼承的東西也不一樣,因為他有Call(this),所以我們可以往上一層Scene_Base裡面去找他的來源,太棒了連說明都有:

/**
* Create the components and add them to the rendering process.
*
* @method create
* @instance
* @memberof Scene_Base
*/
Scene_Base.prototype.create = function() {
};

/**
* Start the scene processing.
*
* @method start
* @instance
* @memberof Scene_Base
*/
Scene_Base.prototype.start = function() {
    this._active = true;
};

不過我看不懂英文,所以就不解釋了。總之就是放在start裡面的會自動設定"啟動",而create的不會。

其他函式的快速介紹:
Scene_Title.prototype.createBackground  = function()  //顯示背景,前面介紹過了

Scene_Title.prototype.createForeground = function()  //顯示前景,包括標題文字等

Scene_Title.prototype.playTitleMusic = function()  //播放背景音樂

Scene_Title.prototype.drawGameTitle = function() //顯示標題文字

大至上會需要改的就這些,裡面不難懂,隨便改改看結果大概就知道是什麼了,頂多可能會需要用到Google翻譯,再不行就問我吧。

最後介紹一下update:

Scene_Title.prototype.update = function() {
    if (!this.isBusy()) {
        this._commandWindow.open();
    }
    Scene_Base.prototype.update.call(this);
};

看一下Base裡對update的說明:

/**
* Update the scene processing each new frame.
*
* @method update
* @instance
* @memberof Scene_Base
*/
Scene_Base.prototype.update = function() {
    this.updateFade();
    this.updateChildren();
};

就是每frame幫你更新畫面用的,也就是說想讓圖片飛來飛去,音樂會隨時間更換之類的效果就要寫在update裡面。進階做法就找Siako吧!幾乎各種想到的效果他都有教學了。


最後,真的是最後了,很快就要結束了,只剩下Window還沒說明了!在Scene_Title裡面跟Window有關的一共有四段,先看第一段:


Scene_Title.prototype.createCommandWindow = function() {
    this._commandWindow = new Window_TitleCommand();
    this._commandWindow.setHandler('newGame',  this.commandNewGame.bind(this));
    this._commandWindow.setHandler('continue', this.commandContinue.bind(this));
    this._commandWindow.setHandler('options',  this.commandOptions.bind(this));
    this.addWindow(this._commandWindow);
};

這段的內容是創建一個視窗,名字是this._commandWindow

他是一個標題命令視窗(Window_TitleCommand),Window_TitleCommand又是什麼?Ctrl+F搜尋一下,找到這個:

Window_TitleCommand.prototype.initialize = function() {
    Window_Command.prototype.initialize.call(this, 0, 0);
    this.updatePlacement();
    this.openness = 0;
    this.selectLast();
};

原來Window_TitleCommand又是屬於Window_Command也就是說層級是這樣的:

Window_Command
    ↓
Window_TitleCommand
    ↓
this._commandWindow

什麼看不懂我在說什麼,沒關係我也搞不太懂,只要知道他們三個都可以可以設定「命令」以及「執行命令的內容」。

這個視窗有三個選項,分別是

這三個選項有三個對應的命令名稱,分別是:

'newGame'
'continue'
'options'

然後裡面這句:

this._commandWindow.setHandler( 'newGame' ,  this.commandNewGame.bind(this) );

//this._commandWindow.setHandler( '命令名稱' , 命令後執行的對象函式) 

Scene_Title.prototype.commandNewGame = function() {
    DataManager.setupNewGame();
    this._commandWindow.close();
    this.fadeOutAll();
    SceneManager.goto(Scene_Map);
};

以上兩段翻譯成白話就是,當我點了'newGame' 後,會執行Scene_Title.prototype.commandNewGame = function()這個函式的內容。內容其實也很簡單,就關閉標題視窗後移動到Scene_Map去。

下兩段分別是對應 'continue'  'options' 都會直接開另一個新場景,就不再說明

Scene_Title.prototype.commandContinue = function() {
    this._commandWindow.close();
    SceneManager.push(Scene_Load);
};

Scene_Title.prototype.commandOptions = function() {
    this._commandWindow.close();
    SceneManager.push(Scene_Options);
};



是不是好簡單阿,咦,那還忘了什麼?

對了,那要怎麼設定或更改視窗裡面的細節?可以在Create裡面做,比如說我新增了藍字的部分:

Scene_Title.prototype.createCommandWindow = function() {
    this._commandWindow = new Window_TitleCommand();
    this._commandWindow.x = 100;
    this._commandWindow.y = 120;
    this._commandWindow.setHandler('newGame',  this.commandNewGame.bind(this));
    this._commandWindow.setHandler('continue', this.commandContinue.bind(this));
    this._commandWindow.setHandler('options',  this.commandOptions.bind(this));
    this.addWindow(this._commandWindow);
};


視窗就移動了!

再來改多一點,像這樣:

Scene_Title.prototype.createCommandWindow = function() {
    this._commandWindow = new Window_TitleCommand();

    this._commandWindow.x = 100;
    this._commandWindow.y = 120;
    this._commandWindow.width = 300;
    this._commandWindow.height = 400;
    this._commandWindow.windowskin = ImageManager.loadSystem('Window_03');

    this._commandWindow.setHandler('newGame',  this.commandNewGame.bind(this));
    this._commandWindow.setHandler('continue', this.commandContinue.bind(this));
    this._commandWindow.setHandler('options',  this.commandOptions.bind(this));
    this.addWindow(this._commandWindow);
};

結果呢:


視窗變大,還換了skin了,如果想讓視窗移動的話,也可以到update裡面去變更this._commandWindow.x以及this._commandWindow.y

這些.x .y的東西叫做「視窗的屬性」,那還有哪些屬性可以改呢?詳見這裡


現在我們會修改視窗大小位置SKIN了,還需要改什麼?

還有個很重要的,就是視窗的選項!

那視窗的選項要怎麼改呢?

要去他的上一層Window_TitleCommand裡面修改

在rpg_windows.js裡面找到下段:

Window_TitleCommand.prototype.makeCommandList = function() {
    this.addCommand(TextManager.newGame,   'newGame');
    this.addCommand(TextManager.continue_, 'continue', this.isContinueEnabled());
    this.addCommand(TextManager.options,   'options');
};

//this.addCommand( '選項顯示名稱',   '選項指令名稱'   );
選項指令名稱大小寫不可寫錯,會對應到執行的函式
如果想要新增選項的話,這裡面多寫幾行就可以了,比如像這樣:
Window_TitleCommand.prototype.makeCommandList = function() {
    this.addCommand(TextManager.newGame,   'newGame');
    this.addCommand(TextManager.continue_, 'continue', this.isContinueEnabled());
    this.addCommand(TextManager.options,   'options');
    this.addCommand( '選項3',   'command3');
    this.addCommand( '選項4',   'command4');
    this.addCommand( '選項5',   'command5');
    this.addCommand( '選項6',   'command6');
};

測試結果如下:


可是一般不建議更動原生程式裡面的東西,簡單的做法是把「所有」Window_TitleCommand的內容都COPY下來貼到自建Scene_Title_new.js裡面,Window_TitleCommand要放在Scene_Title的上面,因為JavaScript是直譯式的語言。這樣才會先創建好視窗內容再創建場景。

另外還有一種版面比較漂亮的做法:程式別名
不用把整段Window_TitleCommand貼進來,只擴充自己想要的部分。


不過因為我只有自己一個人在寫,沒有多工問題,而且也想知道那些沒用到的程式碼的用途,所以通常還是全段貼過來慢慢玩弄他們。


好啦,真的要結束了,什麼,還沒開始創建視窗耶!?

下...下回吧...


目錄:
送禮物贊助創作者 !
0
留言

創作回應

感謝您,真的對我有很大幫助!!(汪~
2019-10-22 00:52:56
安寧郡主
真的太好了,好詳細~~連笨蛋如我都看得懂了!!
2019-11-28 02:53:08
路漫行
因為我也是笨蛋啊
2019-11-28 08:06:16

更多創作